天天看點

為多态基類聲明virtual析構函數——條款07

        c++面向對象特性其中有多态這個特性,平時使用時用基類指針指向子類對象。當我們進行記憶體回收的時候調用 delete (基類指針),此實如果基類有一個non-virtual析構函數。這種情況下會導緻派生類的對象沒有被銷毀,隻有基類部分會被銷毀掉,于是造成一個詭異的“局部銷毀”對象。這樣的後果會形成資源洩露、資料結構被破壞、而且很難找出原因。

#ifndef _BASECLASS_H
#define _BASECLASS_H

class BaseClass
{
private:
    /* data */
public:
    BaseClass(/* args */);
    ~BaseClass();
};

#endif // _BASECLASS_H
           
#include "BaseClass.h"

#include <iostream>

BaseClass::BaseClass(/* args */)
{
    std::cout << "BaseClass construct" << std::endl;
}

BaseClass::~BaseClass()
{
    std::cout << "BaseClass destructor" << std::endl;
}
           
#ifndef _DERIVEDCLASS_H
#define _DERIVEDCLASS_H

#include "BaseClass.h"
class DerivedClass : public BaseClass
{
private:
    /* data */
public:
    DerivedClass(/* args */);
    ~DerivedClass();
};
#endif // _DERIVEDCLASS_H

           
#include "DerivedClass.h"

#include <iostream>

DerivedClass::DerivedClass(/* args */)
{
    std::cout << "DerivedClass construct" << std::endl;
}

DerivedClass::~DerivedClass()
{
    std::cout << "DerivedClass destructor" << std::endl;
}
           
BaseClass *base = new DerivedClass();
delete base;
           

輸出:

BaseClass construct
DerivedClass construct
BaseClass destructor
           

        消除這個問題的做法很簡單:給base class一個virtual析構函數。此後删除derived class對象就會如你想要的那般,将整個對象都銷毀,包括derived class成分。

#ifndef _BASECLASS_H
#define _BASECLASS_H

class BaseClass
{
private:
    /* data */
public:
    BaseClass(/* args */);
    virtual ~BaseClass();   // virtual修飾
};

#endif // _BASECLASS_H
           

運作程式,輸出:

BaseClass construct
DerivedClass construct
DerivedClass destructor
BaseClass destructor
           

        如果class不含virtual函數,通常表示它并不意圖被用做一個base class。當class不企圖被當作base class,令其析構函數為virtual往往是個馊主意。因為使用virtual函數會導緻對象中有一個虛表指針(vptr),該表存在的目的就是為了實作多态的,但是會占用記憶體空間。這樣做的結果會導緻該對象不再和其他語言(如C)内的相同聲明有着一樣的結構(因為其它語言對應物并沒有vptr),是以也不再具有移植性。

請記住

  • polymorphic(帶多态性質的)base class應該聲明一個virtual析構函數。如果class帶有任何virtual函數,它就應該擁有一個virtual析構函數。
  • Classes的設計目的如果不是作為base classes使用,或不是為了具備多态性(polymorphically),就不該聲明virtual析構函數。

繼續閱讀