条款 08:别让异常逃离析构函数

Prevent exceptions from leaving destructors.

如果析构函数吐出异常,可能导致不明确行为,如下面 vector 销毁时会调用所有元素的析构函数,这会导致不明确行为

  1. class A {
  2. ...
  3. ~A() {...} // 假设析构函数会吐出异常
  4. };
  5. void f() {
  6. std::vector<A> v;
  7. ...
  8. }

关闭资源时经常会遇到类似问题,如处理数据库连接时为了方便做封装,就可能导致析构吐出异常

  1. class DBConnection {
  2. public:
  3. static DBConnection create();
  4. void close();
  5. }
  6. class DBManager {
  7. public:
  8. ...
  9. ~DBManager() {
  10. db.close(); // 可能有异常逃离析构
  11. }
  12. private:
  13. DBConnection db;
  14. }
  15. {
  16. DBManager db(DBConnection::create());
  17. }

方法一:明确终止或吞掉异常

  1. ~DBManager() {
  2. try { db.close(); }
  3. catch (...) {
  4. log();
  5. std::abort(); // 明确终止
  6. }
  7. }
  8. ~DBManager() {
  9. try { db.close(); }
  10. catch (...) {
  11. log(); // 直接吞掉异常
  12. }
  13. }

方法二:将析构函数中可能吐出异常的操作提取到普通函数,给用户自行处理异常的机会

  1. class DBManager {
  2. public:
  3. ...
  4. ~DBManager() {
  5. if (!closed) {
  6. try {
  7. db.close();
  8. }
  9. catch (...) {
  10. log();
  11. }
  12. }
  13. }
  14. void close() // 由客户显式调用,自行处理异常
  15. {
  16. db.close();
  17. closed = true;
  18. }
  19. private:
  20. DBConnection db;
  21. bool closed;
  22. }