c++明确指出,当derived class对象由一个base class指针删除,而该base class带着一个non-virtual析构函数,其结果未有定义—
实际执行时,通常发生的是对象的derived成分没被销毁。会造成“局部销毁”对象。 而避免这个问题很简单:给base class一个virtual析构函数。
任何class只要带有virtual函数,都几乎确定应该有一个virtual析构函数。
如果class不含virtual函数,通常表示它并不意图被用作一个base class。当class不企图被当做一个base class,令其析构函数为
virtual往往是个馊主意。欲实现virtual函数,对象必须携带某些信息,主要用来在运行期决定哪个virtual函数该被调用。这份信息通常是由一个vptr虚函数表指针指出。vptr指向一个由函数指针构成的数组,成为virtual table;每个带有virtual函数的class都有一个相应的virtual table。当对象调用一个virtual函数,实际被调用的函数取决于该对象的vptr所指的那个vtbl--编译器在其中寻找适当的函数指针。所以含有virtual函数的class的对象的体积会增加一个vptr指针的大小
class SpecialString : public std:string{//馊主意!!std:string 有一个non-virtual析构函数
}
当无意中将一个pointer-to-SpecialString转换成pointer-to-string,然后将转换所得到的那个string指针delete掉,你立刻就被流放到“行为不明确”的恶地上。相同的分析适用于任何不带virtual析构函数的class,包括stl容器如vector,list,set等
有时候class带一个pure virtual析构函数颇为便利。 pure virtual函数导致abstract classes。有时候你希望拥有抽象class,但手上
没有pure virtual函数,怎么办? 由于抽象class总是企图被当做一个base class来用,而又由于base class应该有个virtual析构函数,并且由于pure virtual函数会导致抽象class,因此解法很简单:为你希望它成为抽象的那个class声明一个pure virtual析构函数。
class Awov{
public:
virtual ~Awov()=0;
}
Awov::~Awov(){} //必须为这个pure virtual 析构函数提供一份儿定义
析构函数的运作方式是,最深层派生的那个class其析构函数最先被调用,然后是每个base class的析构函数被调用。编译器会在Awov的derived class的析构函数中创建一个队~Awov的调用动作,所以必须为这个函数提供一个定义。否则,连接器会报错。
“给base class一个virtual析构函数”,这个规则只适用于polymorphic(带多态性质的)base class身上。这种base class的
设计目的是为了用来“通过base class接口处理derived class对象”。
并非所有base class的设计目的都是为了多态用途。例如标准string和stl容器,都不被设计成base classes使用,更别提多态了。
某些classes的设计是作为base class 使用但是不是为了多态用途。就如条款6中的Uncopyable和标准库的input_iterator_tag,它们并非被设计用来“经由base class接口处置derived class对象”,因此它们不需要virtual析构函数。