今天這則守則非常單純好理解,就來輕鬆一下吧!
這個守則是:
Prevent exceptions from leaving destructors
簡單來說就是不要讓destructor throw exception。為什麼呢?因為destructor他會去清理這個物件該被清掉的東西,如果清到一半throw了一個exception,但destructor又需要繼續清理,此時如果又來一個exception,C++對於這種情形未定義,要馬直接terminate,要馬會產生未定義的行為。
好吧!那道理我都懂,現在該怎麼辦呢?例如書中舉了一個例子:
class DBConnection{
public:
static DBConnection create();
void close();
};
為了要讓所有call到DBConnection的東西都記得要close,我們用了一個DBConn
class來管理DBConnection
這個object,這個後續的守則會再提到(資源管理的部分)。這個DBConn
我們先不管他怎麼做的,但總之就是讓他desctructor的時候會執行db.close
,好確保這個DBConnection
都會被close:
class DBConn{
public:
~DBConn()
{
db.close();
}
}
private:
DBConnection db;
別人就會這樣使用他:
{
DBConn dbc(DBConnection::create());
}
此時如果dbc
在desctruct的時候,裡面的db.close()
有exception產生,而這個destructor如果再把這個exception往外丟,就如同前面描述的,就有麻煩了。
書中說明現在有兩種解法:
DBConn::~DBConn()
{
try { db.close(); }
catch (...)
{
// make log entry that call to close failed;
std::abort();
}
}
這很直接地避免了exception被從destructor丟出來的問題,因為直接在丟出來之前就終止程式了。換言之,就是搶先在未定義行為發生前終止程式。簡單來說,就是遇到exception就不繼續清啦!不知道繼續清你會怎樣,乾脆大家都停一停,不要做了。
DBConn::~DBConn()
{
try { db.close(); }
catch (...)
{
// make log entry that call to close failed;
}
}
跟前面比起來就是不abort
,這有什麼好處?一樣就不繼續清啦!也是因為繼續清可能會有問題,那我們就清到這邊ok。不過這樣就要有把握程式接下來還能繼續跑,而且這樣等於有些東西沒被清掉。
那有沒有更好的做法呢?預防勝於治療,就是讓這個exception最好不要到destructor本身才被丟出來,讓它有機會提前去爆發應對。例如這樣:
class DBConn{
public:
void close()
{
db.close();
closed = true;
}
}
~DBConn()
{
if(!closed)
{
try
{
db.close();
}
catch (...)
{
// make log entry that call to close failed;
}
}
}
private:
DBConnection db;
bool closed;
這做了什麼?說白了就是讓close不再由destructor去管理,db要自己記得去關。雖然desctructor一樣要確保沒close的話也要close掉,但這只是以防萬一。所以若有exception的話應該早在前面就要爆發並處理掉。當然destructor那邊也要做好如果沒close掉的話要去中止或吞掉。
這有道理嗎?道理就是會引發exception的行為不該在destructor發生。如果有可能那就要先處理掉,挪出destructor。
貼心重點提醒:
- Destructors should never emit exceptions. If functions called in a destructor may throw, the destructor should catch any exceptions, then swallow them or terminate the program.
- If class clients need to be able to react to exceptions thrown during an operation, the class should provide a regular(i.e., non-destructor) function that performs the operation.
簡單來說desctructor預期noexcept(since C++11),不預期有exception在desctructor發生。