您的位置:首页 > 编程语言 > C语言/C++

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

2017-08-06 18:56 232 查看
// 条款08: 别让异常逃离析构函数
// 这个条款有歧义,并不是一定要在析构函数里面放异常,
// 而是析构里面发生的异常不要抛出到析构外面去处理。

#include <iostream>
#include <string>

using namespace std;

// 1.析构函数吐出异常,程序可能过早结束或出现不明确行为。

// 数据库连接类
class DBConnection {
public:
static DBConnection create() {
return DBConnection();
}
void close() {  // 关闭联机,失败则抛出异常。
}
};

// 为了避免客户忘记调用close,创建一个管理DBConnection的类,并在析构函数中调用close。
class DBConn {
public:
DBConn(DBConnection dbc) {
db = dbc;
}
~DBConn() {  // 确保数据库总是被关闭
db.close();
}

private:
DBConnection db;
};

// 客户可以这样调用它们
void TestDBConnection() {
DBConn dbc(DBConnection::create());
}
// 当对象dbc销毁的时候,自动调用close函数。但是如果关闭不成功,DBConn析构函数就会
// 抛出该异常,也就是允许该异常离开析构函数。这就会造成难以驾驭的麻烦。

// 2.为了避免上述问题,有三种办法来解决。一个就是即时终止程序,另一种就是析构函数内部吞掉。
// 方法一: 终止程序
// ~DBConn() {
//  	try {
//		db.close();
//		}
//		catch (...) {
//		// 打log,记下close的调用失败。
//			abort();  // 终止程序,避免将异常从析构函数中抛出,那会导致不明确行为。
//  	}
//  }
// 方法二: 内部吞掉
// ~DBConn() {
//  	try {
//		db.close();
//		}
//		catch (...) {
//		// 打log,记下close的调用失败。
//  	}
//  }
// 一般而言,将异常吞掉会压制“某些失败动作”的重要信息。但是比草率的结束程序好。

// 方法三:重新设计DBConn接口,时刻会有机会去处理该异常。
class DBConn2 {
public:
~DBConn2() {
if (!closed) {
try {  // 如果客户不去关闭的话,自己关闭连接
db.close();
}
catch (...) {  // 如果关闭失败,只能使用使用方法一和二。
//吞下程序或者结束程序
}
}
}
void close() {  // 供客户使用的新函数
db.close();
closed = true;
}

private:
bool closed;
DBConnection db;
};
// 为什么要使用DBConn2这个类。这个类是用来管理DBConnection的。为了避免客户忘记关闭
// 而在析构函数里面来关闭,这种情况是客户忘了关闭或者客户坚信关闭不会发生异常。所以用
// 这个类来管理很方便。但是如果客户很在乎关闭异常怎么办,那么可以调用DBConn2里面的close
// 来自己处理异常。

// 2.如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么class应该提供一个普通函数
// 而不是在析构函数里面去执行该操作。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Effective C++ c++