Effective C++学记之07 为多态基类声明virtual析构函数

来源:互联网 发布:高中历史辅助教程 淘宝 编辑:程序博客网 时间:2024/04/29 23:35
1 polymorphic(带多态性质的)base classes应该声明一个virtual析构函数,如果class带有任何virtual函数,应该拥有一个virtual析构函数。

class TimeKeeper{
public:
    TimeKeeper();
    ~TimeKeeper();
    ...
};
class AtomicClock:public TimeKeeper{...}; //原子钟
class WaterClock:public TimeKeeper{...}; //水表

TimeKeeper* ptk = getTimeKeeper();//返回一个指针,指向一个TimeKeeper派生类的动态分配对象。
...
delete ptk  //释放,避免泄露内存和其他资源

有上面的代码可知:客户无须关心各种钟表的计时计算细节,使用工厂函数返回一个base calss指针,指向新生成的派生类对象。
但是为了避免内存和其他资源的泄露,需要在最后将返回的对象适当的delete掉。
但是,条款13说:依赖客户执行delete动作基本上带有某种错误倾向。
getTimeKeeper返回的指针指向一个派生类对象,而该对象经base class指针被删除。而目前的base class有个non-vitual析构函数。
实际执行时对象的继承成分(比如声明在子类中的成员变量)不能被销毁。而且子类的析构函数也未能执行起来,形成资源泄露,败坏数据结构,在调试器上浪费许多时间。

消除以上问题的做法:给base class一个virtual析构函数。此后删除class对象会销毁整个对象包括继承成分。
class TimeKeeper{
public:
    TimeKeeper();
    virtual ~TimeKeeper();
    ...
};
TimeKeeper* ptk = getTimeKeeper();
...
delete ptk  //OK

2 Classes的设计目的如果不是作为base classes使用,或不是为了具备多态性,就不应该声明virtual析构函数。


virtual函数的目的是允许继承class的实现得以客制化(条款34?),当class不企图被当做base calss ,令其析构函数为virtual往往是个馊主意。

class Point{
public:
    Point(int xCoord,int yCoord); //二维空间点
    ~Point();
private:
    int x,y;    
};

如上例所示,如果int占32bits,那么Point对象可塞入一个64bit缓存器中。
更甚这个对象可当做64bit量传递给其他语言的函数。

但是如果有virtual析构函数,形式发生变化。

每个带有virtual函数的class都有一个相应的vptr(virtual table pointer)指针指向一个成为vtbl(virtual table)的数组。
当对象调用某一virtual函数,实际被调用的函数取决于额该对象的vptr所指的那个vtbl。编译器在其中寻找适当的函数指针。

如果Point class内涵virtual函数,其对象的体积会增加:
在32bit计算机体系结构中将占用64bits(存放2个ints)至96bits(两个ints加vptr);
在64bit计算机体系结构中将占用64~128bits,因为指针在这样的结构中占64bits。
因此为Point添加一个vptr会增加其对象大小达50%~100%,Point对象不能够塞入一个64-bit缓存器;
而c++的Point也不能和其他语言内的相同声明有一样的结构(其他语言例如C的对象没有vptr)
因此也不再可能把它传递至其他语言缩写的函数,除非明确补偿cptr。不具有移植性。


并非多有的基类的设计目的都是为了多态。如标准string和STL容器都不被设计作为基类使用,更别提多态了。
标准string和所有STL容器如vector,list set trl::unordered_map(条款54)等都带有non-virtual析构函数,把它们当basee class是错误的抉择。

class SpecialString:public std::string{
...
};

SpecialString* pss = new SpecialString("Doom");
std::string* ps;
ps = pss;
delete ps;   //未定义!显示中*ps的SpecialString资源会泄露,因为SpecialString析构函数没被调用
原创粉丝点击