为多态基类声明virtual析构函数吧

转自:http://blog.sina.com.cn/s/blog_74233e710100zbkf.html

在《C++ primer》,以及在Meyers的《Effective C++》《More Effective C++》中都提到过virtual析构函数,在这里做一下总结。

class Base{

public:

    Base();

    ~Base();

    ...

};

 

class Derivepublic Base{...};

 

Base *pb=new Derive(...);

...

delete pb;           //这会发生什么事情呢?

C++中明确指出,当派生类对象经由一个基类指针被删除,而此基类仅提供了non-virtual析构函数,其结果是未定义的。如果删除基类指针,则需要运行基类构造函数并清除基类的成员,如果对象实际是指向派生类类型的对象,则没有定义这种行为。实际执行是通常发生的是对象的derived成分没有被销毁,而派生类中的base class成分通常会被销毁,这就造成了一个“局部销毁”的对象,从而造成资源泄露。消除这个问题很简答:给base class一个virtual析构函数,这会保证运行适当的析构函数。

class Base{

public:

    Base();

    virtual ~Base();

    ...

};

 

class Derivepublic Base{...}; 

Base *pb=new Derive(...);

...

delete pb;               //现在行为正常了

 

Now,现在就有这样一个问题,是不是要把所有的class的析构函数都要带上virtual呢,这同样是错误的。要实现virtual函数,对象必须携带某些信息,主要用来在运行时决定哪一个virtual函数应该被调用。这份信息通常是由一个所谓vptrvirtual table pointer)指针指出。vptr指向一个由函数指针构成的数组,成为vtblvirtual table);每一个带有virtual函数的class都有一个相应的vtbl。当对象调用一个virtual函数,实际被调用的函数取决于该对象的vptr所指的那个vtbl,编译器在其中寻找适当的函数指针。当然在Scott Meyers的另一本书《More Effective C++》中有详细的介绍,同时尝试实现虚函数机制。virtual函数的实现细节并不重要,重要的是如果class中含有virtual函数,其对象的体积会增加,这会带来许多麻烦问题。

即使class完全不带virtual函数,就要注意了,不要试图去继承它。如标准的string和标准库中的容器vectorlistset等都不带有任何的virtual函数,如果你试图写这样的代码:

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

文章刚开始提到过的问题,你是不是能够避免呢?有时候你会不自觉的义正言辞的去做的。拒绝诱惑吧,毕竟C++没有提供类似于Javafinal class禁止派生机制。

有时候令class带有一个pure virtual析构函数,可能会带来便利,如Meyers在《More Effective C++》中“条款33:将非尾端类设计为抽象类”曾经提到过的那样。

class Base{

public:

    Base();

    virtual ~Base()=0;

    ...

};

这样Base成为一个abstract class,当有时候你希望拥有abstract class,但是没有任何pure virtual函数怎么办?很简答:为你希望成为abstract class的那个class声明一个pure virtual析构函数吧。虽然abstract class不能被实例化,但是别忘了需要为pure virtual析构函数提供一个定义,这也只能在类外进行了:

Base::~Base(){}      //pure virtual析构函数的定义

毕竟,派生类析构函数还是回调用此函数来析构其相应部分的。

最后请记住:

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

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

你可能感兴趣的:(C++)