多態中的虛析構函數


為什么析構函數要聲明成virtual呢?

因為,如果delete一個基類的指針時, 如果它指向的是一個子類的對象,那么析構函數不為虛就會導致無法調用子類析構函數,從而導致資源泄露。

如果一個類要被使用成多態的,那么這個virtual是必須的。比如:

#include <iostream>
using namespace std;

class Animal
{
	char* ap;
public:
    Animal()
    {
        ap = new char;
        std::cout << "Animal ctor" << std::endl;
    }
    virtual void foo()
    {
        std::cout << "Animal::foo" << std::endl;
    }
    virtual ~Animal()
    {
        std::cout << "Animal dtor" << std::endl;
        delete ap;
    }
};
class Dog : public Animal
{
    char* dp;
public:
    Dog()
    {
        dp = new char;
        std::cout << "Dog ctor" << std::endl;
    }
    virtual void foo()
    {
        std::cout << "Dog::foo" << std::endl;
    }
    virtual ~Dog()
    {
        delete dp;
        std::cout << "Dog dtor" << std::endl;
    }
};

int main()
{
    Animal* pa = new Dog();
    pa->foo();
    delete pa;
    return 0;
}

delete pa 實際上相當於:

pa->~Animal();

釋放pa所指向的內存

在這里,因為~Animal()是virtual的,盡管是通過Animal類型的指針調用的,根據虛表v-table的信息,~Dog()被正確調用到。如果把virtual屬性去掉,那么被調用的是~Animal(),Dog類的構造函數被調用而析構函數未被調用,構造函數中分配的資源沒有釋放,從而產生了內存泄漏。析構函數缺省聲明為virtual,就可以避免這一問題。

如果基類析構函數不加virtual,運行效果如下:

Animal ctor
Dog ctor
Dog::foo
Animal dtor

可另一個問題是,有時virtual是不需要的。

如果一個類不會被繼承,比如一個utility類,該類完全是靜態方法;

或者一些類盡管可能會被繼承,但不會被使用成多態的,即除了析構函數外,沒有其他的方法是virtual的,這時就可以把virtual屬性去掉。

去掉析構函數的virtual屬性后,因為該類中沒有其他的virtual函數,所以編譯時不會生成v-table,這樣就節省了編譯時間,並減少了最終生成的程序的大小。更重要的是,遵從這一規則,給該類的維護者一個信息,即該類不應被當作多態類使用。

同樣,當作一個抽象時,如果你模仿Java的interface,聲明了如下的虛基類:

class AbstractBase
{
    virtual method1() = 0;
    virtual method2() = 0;
};

那么應該給它增加一個空的virtual析構函數:

virtual ~AbstractBase(){}

如果你對COM比較熟悉,可能會注意到,COM interface中並沒有這個virutal構造函數。這是因為,COM通過使用引用計數的機制來維護對象。當你使用完一個COM對象,調用Release()時,COM的內部實現檢查引用技術是否為零。如果是,則調用:

delete this;

因為Release()是virtual的,所以該COM對象對應的正確的派生類被調用,delete this會調用正確的析構函數,達到了使用virtual析構函數的效果。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM