6.Declare destruction virtual in polymorphic base classes.
- c++中明确指出,如果derived class对象被一个base class指针持有,通过这个base class指针去删除对象,并且这个base class带有一个non-virtual析构函数,结果是未定义的,实际情况下,通常是对象的derived 部分没有被销毁。所以任何class只要带有virtual函数几乎确定应该也有一个virtual的析构函数。
- 但是如果一个被设计成不作为base class的类,不应该有一个virtual析构函数。因为为了支持virtual机制,对象必须携带很多其他信息,包括虚函数表vtbl和虚表指针vptr,不仅对象的体积会增加也丢失了可移植性。
- 析构函数绝对不要吐出异常,如果一个析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后将打碎的牙咽下去或者直接结束程序。— class的设计可以类内设计一个类似close的函数,这个操作将分担析构的任务,并且能够体面的处理该操作发生的异常。
class DBConn{
public:
...
void close()
{
db.close();
closed = true;
}
~DBConn()
{
if(!closed)
{
try
{
db.close();
}
catch(...)
{
//记下log,调用close失败
...
}
}
}
private:
DBConnection db;
bool closed;
};
如果某个操作可能在失败时抛出异常,而又存在某种需要必须处理该异常,那么这个异常必须来自析构以外的某个函数。
-
绝对不要在构造和析构中调用virtual函数
derived class对象内的base class成分会在derived class成分自身的成分构造之前先被构造妥当,所以如果构造内调用的virtual函数被derived函数重写,在base class构造期间,此virtual函数还处于base class的版本。
反过来说,因为base class构造函数时机早于derived class,所以在base class构造函数执行时,derived class的成员变量还没有初始化,如果此时virtual函数下降到derived class阶层,函数内调用的derived class 的 local 未初始化的成员变量,将会导致不明确的行为,c++是不会允许这样的行为发生的。
所以在构造中调用virtual函数,常常会让你摸不着头脑,为什么一个derived class对象会调用base class版本的成员函数?
对于析构函数,同样,一旦derived class函数析构开始执行,对象内的derived class 成员变量便呈现出未定义的状态,C++将其视为不存在,进入base class析构函数,对象便成为一个base class对象,析构中的virtual函数不会按照你设想的那样完成工作,因为他已经叛变了。 -
operator= 的设计注意事项
赋值运算符重载,用户可能会出现连锁形式: a = b = c = 15;解决连锁赋值的点在于 返回一个reference to *this;
同样,用户可能会出现自我赋值的操作,自我赋值的场景不一定很清晰明了的可以识别,往往也会是概率性事件
让operator=兼顾"异常安全性"和"自我赋值安全性"
Class Bitmap{...};
Class Widget{
...
private:
Bitmap* pb;
};
Widget& Widget::operator=(const Widget& rhs)
{
Bitmap* pOrig = pb; //记住原来的pb
pb = new Bitmap(*rhs.pb); //令pb指向一个*pb的副本
delete pOrig;
return *this;
}
面向对象中,会将对象的内部全部封装起来,只留两个函数负责对象的copying,copy构造函数和copy assignment操作符;
任何情况下如果给derived class编写copying函数,需要小心赋值其base class成分,调用base class的copying函数来实现;
copying assignment和copy构造函数不能互相调用,copy assignment 只施用于已经初始化后的对象身上,copy构造施用于构造一个新的对象。