C++ 并不禁止從析構(gòu)函數(shù)中引發(fā)異常,但是這確實妨礙了實踐。至于有什么好的理由,考慮:
class Widget {
public:
...
~Widget() { ... } // assume this might emit an exception
};
void doSomething()
{
std::vector v;
...
} // v is automatically destroyed here
當 vector v 被析構(gòu)時,它有責任銷毀它包含的所有 Widgets。假設(shè) v 中有十個 Widgets,在銷毀第一個的時候,拋出一個異常。其他 9個 Widgets 仍然必須被銷毀(否則他們持有的任何資源將被泄漏),所以 v 應該調(diào)用它們的析構(gòu)函數(shù)。但是假設(shè)在這個調(diào)用期間,第二個 Widgets 的析構(gòu)函數(shù)又拋出一個異常?,F(xiàn)在有兩個異常同時在活動中,對于 C++ 來說這太多了。在非常巧合的條件下發(fā)生這樣兩個同時活動的異常,程序的執(zhí)行會終止或者引發(fā)未定義行為。在本例中,將引發(fā)未定義行為。與此相同,使用任何標準庫容器(比如,list,set),任何 TR1中的容器,甚至是一個數(shù)組,都可能會引發(fā)未定義問題。并非必須是容器或數(shù)組才會陷入麻煩。程序夭折或未定義行為是析構(gòu)函數(shù)引發(fā)異常的結(jié)果,即使沒有使用容器或數(shù)組也會如此。C++ 不喜歡引發(fā)異常的析構(gòu)函數(shù)。 這比較容易理解,但是如果你的析構(gòu)函數(shù)需要執(zhí)行一個可能失敗而拋出異常的操作,該怎么辦呢?例如,假設(shè)你與一個數(shù)據(jù)庫連接類一起工作:
class DBConnection {
public:
...
static DBConnection create(); // function to return
// DBConnection objects; params
// omitted for simplicity
void close(); // close connection; throw an
}; // exception if closing fails
為了確??蛻舨粫浾{(diào)用 DBconnection 對象的 close,一個合理的主意是為 DBConnection 建立一個資源管理類,在它的析構(gòu)函數(shù)中調(diào)用 close。這樣的資源管理類將在以后的文章中探討,但在這里,只要認為這樣一個類的析構(gòu)函數(shù)看起來像這樣就足夠了:
class DBConn { // class to manage DBConnection
public: // objects
...
~DBConn() // make sure database connections
{ // are always closed
db.close();
}
private:
DBConnection db;
};
它允許客戶像這樣編程:
{
// open a block
DBConn dbc(DBConnection::create()); // create DBConnection object
// and turn it over to a DBConn
// object to manage
... // use the DBConnection object
// via the DBConn interface
} // at end of block, the DBConn
// object is destroyed, thus
// automatically calling close on
// the DBConnection object
既然能成功地調(diào)用 close 那就好了,但是如果這個調(diào)用導致了異常,DBConn 的析構(gòu)函數(shù)將散播那個異常,也就是說,它將離開析構(gòu)函數(shù)。這就產(chǎn)生了問題,因為析構(gòu)函數(shù)拋出了一個燙手的山芋。
有兩個主要的方法避免這個麻煩。DBConn 的析構(gòu)函數(shù)能:
終止程序 如果 close 拋出異常,調(diào)用 abort。
DBConn::~DBConn()
{
try { db.close(); }
catch (...) {
make log entry that the call to close failed;
std::abort();
}
}
class Widget {
public:
...
~Widget() { ... } // assume this might emit an exception
};
void doSomething()
{
std::vector
...
} // v is automatically destroyed here
當 vector v 被析構(gòu)時,它有責任銷毀它包含的所有 Widgets。假設(shè) v 中有十個 Widgets,在銷毀第一個的時候,拋出一個異常。其他 9個 Widgets 仍然必須被銷毀(否則他們持有的任何資源將被泄漏),所以 v 應該調(diào)用它們的析構(gòu)函數(shù)。但是假設(shè)在這個調(diào)用期間,第二個 Widgets 的析構(gòu)函數(shù)又拋出一個異常?,F(xiàn)在有兩個異常同時在活動中,對于 C++ 來說這太多了。在非常巧合的條件下發(fā)生這樣兩個同時活動的異常,程序的執(zhí)行會終止或者引發(fā)未定義行為。在本例中,將引發(fā)未定義行為。與此相同,使用任何標準庫容器(比如,list,set),任何 TR1中的容器,甚至是一個數(shù)組,都可能會引發(fā)未定義問題。并非必須是容器或數(shù)組才會陷入麻煩。程序夭折或未定義行為是析構(gòu)函數(shù)引發(fā)異常的結(jié)果,即使沒有使用容器或數(shù)組也會如此。C++ 不喜歡引發(fā)異常的析構(gòu)函數(shù)。 這比較容易理解,但是如果你的析構(gòu)函數(shù)需要執(zhí)行一個可能失敗而拋出異常的操作,該怎么辦呢?例如,假設(shè)你與一個數(shù)據(jù)庫連接類一起工作:
class DBConnection {
public:
...
static DBConnection create(); // function to return
// DBConnection objects; params
// omitted for simplicity
void close(); // close connection; throw an
}; // exception if closing fails
為了確??蛻舨粫浾{(diào)用 DBconnection 對象的 close,一個合理的主意是為 DBConnection 建立一個資源管理類,在它的析構(gòu)函數(shù)中調(diào)用 close。這樣的資源管理類將在以后的文章中探討,但在這里,只要認為這樣一個類的析構(gòu)函數(shù)看起來像這樣就足夠了:
class DBConn { // class to manage DBConnection
public: // objects
...
~DBConn() // make sure database connections
{ // are always closed
db.close();
}
private:
DBConnection db;
};
它允許客戶像這樣編程:
{
// open a block
DBConn dbc(DBConnection::create()); // create DBConnection object
// and turn it over to a DBConn
// object to manage
... // use the DBConnection object
// via the DBConn interface
} // at end of block, the DBConn
// object is destroyed, thus
// automatically calling close on
// the DBConnection object
既然能成功地調(diào)用 close 那就好了,但是如果這個調(diào)用導致了異常,DBConn 的析構(gòu)函數(shù)將散播那個異常,也就是說,它將離開析構(gòu)函數(shù)。這就產(chǎn)生了問題,因為析構(gòu)函數(shù)拋出了一個燙手的山芋。
有兩個主要的方法避免這個麻煩。DBConn 的析構(gòu)函數(shù)能:
終止程序 如果 close 拋出異常,調(diào)用 abort。
DBConn::~DBConn()
{
try { db.close(); }
catch (...) {
make log entry that the call to close failed;
std::abort();
}
}