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

《Effect C++》学习------条款07:为多态基类声明virtual析构函数

2016-08-16 12:40 239 查看

条款07:为多态基类声明virtual析构函数

有许多种做法可以记录时间,因此,设计一个
TimeKeeper base class
和一些
derived classes
作为不同的计时方法,相当合情合理:

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


许多客户只想在程序中使用时间,不想操心如何实现细节,这是可以用工厂模式,设计一个factory函数,返回指向一个计时对象。Factory函数“返回一个base类指针”,指向新生成的derived class对象:

TimeKeeper* getTimeKeeper();
TimeKeeper* p = getTimeKeeper();
...
delete p;


如上所示,分配的指针进行完相关操作后delete很重要。

由于p是一个基类的指针,当进行析构时,会调用基类的析构函数,也就是说会把p所指对象的基类部分进行,但是p实际指向了一个派生类的对象,对于派生类的数据怎么办呢?此时会产生不明确行为,这是造成内存泄露的一个绝佳方式。

如果我们把基类的析构函数做成虚函数,则这个问题就可以解决了:

class TimeKeeper{
public :
TimeKeeper();
virtual ~TimeKeeper();
....
};

class AtomicClock : public TimeKeeper{...};

class WaterClock : public TimeKeeper{...};

class WristClock : public TimeKeeper{...};


再次进行析构时,由于析构函数是一个虚函数,所以在析构时,会在运行期来决定调用哪个类的析构函数,此时,经判断是派生类的析构函数,所以会调用派生类的析构函数,此时,可以把ptk所指向的对象全部析构。

结论:任何class只要带有virtual函数都几乎确定应该也有一个virtual析构函数。

如果一个class不含virtual函数,通常表示它并不意图被用做一个base class,即使被当作基类,也不希望用基类的指针来指向一个派生类的对象。当class不企图被当作base class,令其析构函数为virtual,往往不是一个好主意。如下类:

class Point{

public :

Point(int xCoord, int yCoord) : x(xCoord),y(yCoord){}

~Point();

private:

int x, y;

}


这样一个类的对象,会占用64个字节的内存,如果其析构函数是一个虚函数,则此类的对象,将会是96字节,这样,浪费了50%的空间。

所以许多人的心得是:只有当class内含至少一个virtual函数,才为它声明一个virtual析构函数。

对于继承任何析构函数不是virtual的类,如果通过指针使用多态机制,最后通过delete该指向derived类型的base类型指针销毁原derived对象,就会出现泄漏的问题。

所以,包括所有STL容器和任何有非虚析构函数的类,请不要继承它们!!(C++11里面有一个final关键字,可以禁止派生,这样就好许多了!以后定义有非虚析构函数的类,最好都加上final关键字!而且标记为final的类,编译器则根本不会生成虚表。这样的代码显然更有效率。所以新标准真好!!!)

小技巧:但你需要一个抽象类,但是你没有一个虚函数,这时根据“当你打算使用的多态的时候,base class一般应该有个virtual析构函数”,你可以使用纯虚析构函数

注意:这时必须为这个纯虚析构函数提供一个定义(普通纯虚函数不用提供定义)。因为析构函数的运作机理是最深处的基类先被调用,然后是其后的每一个基类的析构函数被调用

并非所有的基类都是为了多态,对于不打算使用多态的情况,基类不需要virtual析构函数。

请记住:

- [x] Polymorphic base class 应该声明一个virtual析构函数。如果class带有任何virtual函数,它就应该拥有一个virtual析构函数。

- [x] Class的设计目的如果不是作为base class 使用,或不是为了具备多态性,就不应该声明virtual析构函数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: