天天看點

C++繼承中的虛析構函數

看看下面程式有什麼錯誤:

C++繼承中的虛析構函數
#include <iostream>
using namespace std;

class Father
{
public:
    Father(){};
    ~Father(){};
};

class Son:public Father
{
public:
    Son(){};
    ~Son(){};
};

int main()
{
    Father *pfather=new Son;
    delete pfather;
    pfather=NULL;

    return 0;
}      
C++繼承中的虛析構函數

該程式在VC++6.0運作後并未發生錯誤,何解?

修改上面程式如下:

C++繼承中的虛析構函數
#include <iostream>
using namespace std;

class Father
{
public:
    Father(){cout<<"contructor Father!"<<endl;};
    ~Father(){cout<<"destructor Father!"<<endl;};
};

class Son:public Father
{
public:
    Son(){cout<<"contructor Son!"<<endl;};
    ~Son(){cout<<"destructor Son!"<<endl;};
};

int main()
{
    Father *pfather=new Son;
    delete pfather;
    pfather=NULL;

    return 0;
}      
C++繼承中的虛析構函數

運作結果:

contructor Father!

contructor Son!

destructor Father!

       顯然派生類Son并沒有析構,馬上引出了C++繼承中的虛析構函數問題。

(1)基類的的析構函數不是虛函數的話,删除指針時,隻有其類的記憶體被釋放,派生類的沒有。這樣就記憶體洩漏了。

(2)析構函數不是虛函數的話,直接按指針類型調用該類型的析構函數代碼,因為指針類型是基類,是以直接調用基類析構函數代碼。

(3)問:啥已經delete p了還能給p指派啊。。。不解,求高人指點??

       答:delete是删除指針p指向的執行個體,p指針本身依然存在,delete後将p置為空值是常用做法,空值一般寫成NULL宏,其實就是0。因為記憶體0位置是不允許通路的,delete 0操作編譯器可以判斷是錯誤操作不會執行,是以将p置為空值0是很安全的做法。

(4)養成習慣:基類的析構一定virtual。

(5)當基類指針指向派生類的時候,如果析構函數不聲明為虛函數,在析構的時候,不會調用派生類的析構函數,進而導緻記憶體洩露。

(6)子類對象建立時先調用父類構造函數然後在調用子類構造函數,在清除對象時順序相反,是以delete p隻清除了父類,而子類沒有清除。。。

(7)當基類對象的指針或引用調用派生類對象時,如果基類的析構函數不是虛析構函數,則通過基類指針或引用對派生類的析構是不徹底的。

後語:

(1) 對于這個程式,實際上是沒有關系的,delete pfather雖然隻調用了Father類的析構函數,但是程式運作完成,退出main函數後,Son類的部分資料也會被自動清除。      
(2)那麼什麼時候才要用虛析構函數呢?通常情況下,程式員的經驗是,當類中存在虛函數時要把析構函數寫成virtual,因為類中存在虛函數,就說明它有想要讓基類指針或引用指向派生類對象的情況,此時如果派生類的構造函數中有用new動态産生的記憶體,那麼在其析構函數中務必要delete這個資料,但是一般的像以上這種程式,這種操作隻調用了基類的析構函數,而标記成虛析構函數的話,系統會先調用派生類的析構函數,再調用基類本身的析構函數。 
(3)一般情況下,在類中有指針成員的時候要寫copy構造函數,指派操作符重載和析構函數。       

修改後程式:

C++繼承中的虛析構函數
#include <iostream>
using namespace std;

class Father
{
public:
    Father(){cout<<"contructor Father!"<<endl;};
    virtual ~Father(){cout<<"destructor Father!"<<endl;};
};

class Son:public Father
{
public:
    Son(){cout<<"contructor Son!"<<endl;};
    ~Son(){cout<<"destructor Son!"<<endl;};
};

int main()
{
    Father *pfather=new Son;
    delete pfather;
    pfather=NULL;

    return 0;
}      
C++繼承中的虛析構函數

運作結果:

contructor Father!

contructor Son!

destructor Son!

destructor Father!