您的位置:首页 > 编程语言 > C语言/C++

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

2016-03-01 21:27 411 查看
        先上结论:

        1、带有多态性质的基类,应该声明一个 virtual 析构函数。如果类带有任何 virtual 函数,它就应该有一个 virtual 析构函数。

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

举一个工厂模式的例子:

class TimeKeeper {
public:
TimeKeeper();
~TimeKeeper();
};
 
class AtomicClock : public TimeKeeper
{
//原子钟
};
 
class WristWatch : public TimeKeeper
{
//腕表
};


先定义一个基类 TimeKeeper 和它的派生类 AtomicClock 和 WristWatch,设计一个工厂函数(工厂模式),返回指针指向一个计时对象。

TimeKeeper* getTimeKeeper()
{
TimeKeeper* t_keeper = NULL;
switch(type)
{
case 1:
t_keeper = new AtomicClock();
break;
case 2:
t_keeper = new WristWatch();
break;
default:
break;
}
return t_keeper;
}


        在该函数中,我们 new 了一个指针,为了防止内存泄露,在使用完毕的时候一定要delete:

        “依赖客户端执行 delete 动作,基本上便带有某种错误的倾向”!

        如何理解这句话呢?getTimeKeeper 函数返回的指针类型是基类的,但指针却是指向一个派生类的对象,并最终要经由一个基类指针被删除,基类的析构函数是 non-virtual 的,这就导致在实际的执行中,对象中基类的部分被析构,但是派送类独有的部分却没有!这种情况被称为“局部销毁”对象,很容易造成内存泄露。

        所以我们应该将基类的析构函数声明为 virtual 函数: virtual ~TimeKeeper(); 确保在调用析构函数的时候,子类的析构函数可以“覆盖”父类的析构函数,从而完整的析构整个对象。

        但是,如果一个类,在设计的时候并不是为了作为 base class 来使用,或着说不是为了具备多态性,那么就不该将析构函数声明为 virtual 函数(这种情况下其他的成员函数也都不应该被声明为 vi
9ec5
rtual 函数)。

        这是从代码的可移植性角度总结的做法。

class Point {
public: 
Point();
~Point();
private:
int32_t a, b;
};


        这样一个类,它的一个对象可以放入一个64位的寄存器,或者作为一个“64位长度的量”传递。
        但如果这个类有一个 virtual 函数,那么它的对象必须携带更多的信息,用来在运行时期决定哪一个 virtual 函数该被调用。为了保存这些信息,我们需要一张虚表(vtbl)来保存函数的指针,相应的,类 Point 里要增加一个虚表指针 vptr
来指向这张虚表。这样的话,类需要的空间就是 96bits,那么在传递类的对象的时候,就不能把它当做“64bits的量”,要新增很多细节的实现。因此不再具有移植性。

        所以,如果一个类不需要具备多态性,那么就不要为它声明 virtual 函数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: