// 条款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应该提供一个普通函数
// 而不是在析构函数里面去执行该操作。
条款08:别让异常逃离析构函数
最新推荐文章于 2023-02-14 17:11:10 发布