Effective C++ 条款8、9、10

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

析构函数吐出异常就是危险,总会带来“过早结束程序”或“发生不明确行为”的风险

析构函数绝对不要吐出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们(不传播)或结束程序。

假设使用一个class负责数据库连接:

class DBConnection {
public:
	static DBConnection create();//此函数返回DBConnection对象,暂略参数
	void close();//关闭联机;失败则抛出异常
};

class DBConn {//这个class用来管理DBConnection对象
public:
	~DBConn() {
		db.close();//确保数据库连接总是会被关闭
	}
private:
	DBConnection db;
};
{										//g开启一个block
	DBConn dbc(DBConnection::create());//建立DBConnection对象并交给DBConn对象以便管理
									   //通过DBConn的接口,合用DBConnection对象。
										//在区块结束点,DBConn对象被销毁,因而自动
										//为DBConnection对象高用close
}

上述block,如果调用导致异常,DBConn析构函数会传播该异常,也就是允许它离开这个析构函数,造成的问题难以驾驭。

i、如果close抛出异常就结束程序。通过调用abort完成:

DBConn::~DBConn() {
	try { db.close(); }
	catch (...) {
		/*制作运转记录,记下对close的调用失败*/
		abort();//调用abort可以抢先制“不明确行为”于死地
	}
}

于析构期间发生的错误后无法继续执行,强迫结束程序,可以阻止异常从析构函数传播出去,即调用abort可以抢先制不明确行为于死地。

ii、吞下因调用close而发生的异常:

DBConn::~DBConn() {
	try { db.close(); }
	catch (...) {
		/*制作运转记录,记下对close的调用失败*/
	}
}

吞掉异常是个坏主意,因为它压制了某些动作失败的重要信息!然而有时也好,因为即使在遭遇并忽略一个错误以后,程序必须能继续可靠的执行。

iii、一个较佳策略是重新设计DBConn接口,使其客户有机会对可能出现的问题作出反应。例如DBConn自己可以提供一个close函数,赋予客户一个机会行以处理“因该操作而发生的异常”。代码如下:

class DBConn {//这个class用来管理DBConnection对象
public:
	void close() {//供客户使用的新函数
		db.close();
		closed = true;
	}
	~DBConn() {
		if (!closed) {
			try { db.close(); }   //关闭连接(如果客户不那么做的话)
			catch (...) {                                   //如果关闭动作失败
				/*制作运转记录,记下对close的调用失败*/     //记录下来并结束程序
			}												//或吞下异常
		}
	}
private:
	DBConnection db;
	bool closed;
};

条款9 绝不在构造和析构过程中调用virtual函数

base class构造期间virtual函数绝不会下降到derived classes阶层。取而代之的是,对象的作为就像隶属base类型一样。通俗讲,在base class构造期间,virtual函数不是virtual

class Transaction {
public:
	Transaction();
	virtual void logTransaction()const = 0;
};
Transaction::Transaction() {
	logTransaction();
}
class BuyTransaction :public Transaction {
public:
	virtual void logTransaction()const;
};
class SellTransaction :public Transaction {
	virtual void logTransaction()const;
};

BuyTransaction b;//问题在于,在bass class进行构造时,会对纯虚函数进行调用,这是严重错误的事

基函数在构造期间,如果调用纯虚函数,大多执行系统会中止程序。

避免上述问题的做法:确定构造函数和析构函数都没有(在对象被创建和被销毁期间)调用virtual函数,而它们调用的所有函数也都服从同一约束。

解决上述问题的方法:既然无法使用virtual函数从base classes向下调用,在构造期间,可以由"令derived classes将必要的构造信息向上传递到base class构造函数"替换之而加以弥补。

class Transaction {
public:
	explicit Transaction(const string logInfo);
	void logTransaction()const;//现在是个non-virtual函数
};
Transaction::Transaction(const string& logInfo) {
	logTransaction(logInfo);//现在是个non-virtual调用
}
class BuyTransaction :public Transaction {
public:
	BuyTransaction(parameters):Transaction(createLogString(parameters)){}//将信息传给base class构造函数
private:
	static string createLogString(parameters);//静态函数运用,故不可能意外指向“初期未成熟之BuyTransaction对象内尚未初始化的成员变量”
                                                  //正是因为“那些成员变量处于未定义状态”,所以“在base class构造和析构期间调用的virtual函数
	                                          //不可下降至derived classes”。
};

 条款10 令operator=返回一个reference to *this(不仅是=,如+=、-=、*=、/=等等)

此条款举一实例说明,代码如下

class Widget {
public:
	Widget& operator=(const Widget& rhs) {//返回类型是引用,因为返回对象作为左值,需要是引用返回类型才能被修改
		...
		return *this;                      //返回左侧对象
	}
};

以上内容均来自Scott Meyers大师所著Effective C++ version3,如有错误地方,欢迎指正!相互学习,促进!!

猜你喜欢

转载自blog.csdn.net/tt_love9527/article/details/80394383