天天看點

析構函數為虛函數

多态是面向對象的一個基本屬性,包括靜态多态(編譯階段)和動态多态(運作階段),靜态多态主要是指函數參數不同産生的多态性,是在編譯階段可以識别的一種多态機制,而運作時多态則主要用于基類指針指向派生類對象時,可以通過基類指針直接調用派生類的對象函數,當然這種多态是通過虛函數實作的。

虛函數的目的就是通知系統在函數調用時能夠自動識别對應的類對象類型,進而能夠根據指針所指類型調用對應的類對象,實作函數調用時的多态性。對于析構函數而言,同樣适用于上述規則。如果析構函數不是虛函數,那麼在調用該函數時(對象被删除時)則隻會調用目前對象對應的類的析構函數,這對于直接定義的對象是沒有什麼影響的,但是對于使用基類指向派生類的指針而言,因為基類指針實際上是基類類型,是以析構時自然隻會調用基類的析構函數,這就可能産生記憶體洩漏(因為派生類的析構函數不被調用)。是以如果确定程式中有基類指針指向派生類的問題,則必須将基類的析構函數指定為虛函數,如此才能確定NEW出來的對象被正确的DELETE。

以下是幾個示例程式,用于友善了解:

class ClxBase{
    public:
    ClxBase() {};
    ~ClxBase()
     {
     cout << "Output from the destructor of class ClxBase!" << endl;
     };
    
    void DoSomething() { cout << "Do something in class ClxBase!" << endl; };
    };
    
    class ClxDerived : public ClxBase{
    public:
    ClxDerived() {};
    ~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; };
    
    void DoSomething() { cout << "Do something in class ClxDerived!" << endl; };
    };
           

示例程式一:

int main(){ 
    ClxBase base;
    
    ClxDerived derived;
    base.DoSomething();
    
    derived.DoSomething();
    return 0;
    }
           

運作結果:

Do something in class ClxBase!

Do something in class ClxDerived!

Output from the destructor of class ClxDerived!

Output from the destructor of class ClxBase!

Output from the destructor of class ClxBase!
           

分析:

從以上執行個體程式可以發現,正常構造的對象,派生類的析構函數會主動調用基類的析構函數,是以不會存在記憶體洩漏問題。

示例程式二:

int main(){ 
    ClxDerived *p = new ClxDerived;
    p->DoSomething();
    delete p;
    return 0;
    }
           
Do something in class ClxDerived!

Output from the destructor of class ClxDerived!

Output from the destructor of class ClxBase!
           

從以上結果可以發現,派生類的指針在析構時,同樣是運作指針所對應類型的派生類的析構函數,而此析構函數自然會調用基類的析構函數,是以也不會産生記憶體洩漏。

執行個體程式三:

int main(){ 
        ClxBase *p = new ClxDerived;
        p->DoSomething();
        delete p;
        return 0;
        }
           
Do something in class ClxBase!
Output from the destructor of class ClxBase!
           

從以上結果可以發現,基類指針在析構時,會調用基類的析構函數,縱然其指向派生類,但其依然會調用基類的析構,是以派生類中如有需要釋放的記憶體空間,則必然得不到釋放,進而産生記憶體洩漏。

示例程式四:

class ClxBase{
    public:
    ClxBase() {};
    virtual ~ClxBase() {cout << "Output from the destructor of class ClxBase!" << endl;};
    virtual void DoSomething() { cout << "Do something in class ClxBase!" << endl; };
    };
    
    class ClxDerived : public ClxBase{
    public:
    ClxDerived() {};
    ~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; };
    void DoSomething() { cout << "Do something in class ClxDerived!" << endl; };
    };
    
    int main(){ 
    ClxBase *p = new ClxDerived;
    p->DoSomething();
    delete p;
    return 0;
    }
           
Do something in class ClxDerived!
Output from the destructor of class ClxDerived!
Output from the destructor of class ClxBase!
           

繼續閱讀