標籤:com ase style oid 存在 程式 span cout private
參考:http://www.weixueyuan.net/view/6373.html
總結:
建構函式是不能聲明為虛函數的,解構函式可以被聲明為虛函數。
將基類的解構函式聲明為虛函數之後,衍生類別的解構函式也自動成為虛解構函式。
未將基類的解構函式定義為虛函數,如下面的例子的情況可能會出現記憶體流失。原因是不構成多態,函數屬於編譯期綁定,無論基類指標p指向的是衍生類別對象或者是基類對象,執行的都將會是基類的函數。
通常來說,如果基類中存在一個指向動態分配記憶體的成員變數,並且基類的解構函式中定義了釋放該動態分配記憶體的代碼,則應該將基類的解構函式聲明為虛函數。
只有非靜態成員函數才可以成為虛函數,而靜態成員函數不能聲明為虛函數。
在類中,建構函式用於初始化對象及相關操作,建構函式是不能聲明為虛函數的,因為在執行建構函式前對象尚未完成建立,虛函數表尚不存在,此時就無法去查詢虛函數表,因此也就無法得知該調用哪一個建構函式了。
解構函式則用於銷毀對象時完成相應的資源釋放工作,解構函式可以被聲明為虛函數。我們先通過一個例子來說明解構函式聲明為虛函數的必要性。
例1:
#include<iostream>using namespace std;class base{public: base(); ~base();private: int * a;};class derived: public base{public: derived(); ~derived();private: int * b;};base::base(){ cout<<"base constructor!"<<endl; a = new int[10];}base::~base(){ cout<<"base destructor!"<<endl; delete[] a;}derived::derived(){ cout<<"derived constructor!"<<endl; b = new int[1000];}derived::~derived(){ cout<<"derived destructor!"<<endl; delete[] b;}int main(){ base* p; p = new derived; delete p; return 0;}
在本類中定義了兩個類,一個基類,一個衍生類別,衍生類別和基類中都分別定義了自己的建構函式和解構函式。基類和衍生類別中各有一個int型指標成員變數,在基類的建構函式中,給指標變數a分配了10個int型空間,在基類的解構函式則用於將是將a所指向的空間釋放掉,在衍生類別的建構函式中,指標成員變數被分配了1000個整型空間,衍生類別的解構函式則是為了釋放掉b指標所指向的儲存空間。在主函數中,我們建立一個基類類型的指標,指標指向一個衍生類別對象,之後釋放p指標所指向的對象的儲存空間。最後程式運行結果如下:
base constructor!
derived constructor!
base destructor!
觀察程式運行結果,程式列印出了“base constructor!”這串字元,則說明基類的建構函式被調用了,之後又列印出了“derived constructor!”這串字元,同樣地衍生類別的建構函式也被調用了。當我們用new操作符建立一個衍生類別對象時會先調用基類建構函式,然後再調用衍生類別建構函式,程式輸出結果與我們料想的是一致的。至此基類的成員變數a通過建構函式被分配了10個整型儲存空間,衍生類別的成員變數b通過建構函式被分配了1000個整型儲存空間。之後程式列印出了“base destructor!”字串,這說明基類的解構函式被調用了,a指標所指向的10個整型記憶體空間被釋放了。但是之後卻並未調用衍生類別的解構函式,不調用衍生類別的解構函式則會導致b指標所指向的1000個整型儲存空間不會被釋放,如此一來造成了記憶體泄露了。記憶體泄露問題肯定是我們程式設計人員需要極力避免的。本例中出現的問題就是因為衍生類別的解構函式未被調用,為瞭解決這個問題,我們將基類的解構函式聲明為虛函數,修改後基類的定義如下:
class base{public: base(); virtual ~base();private: int * a;};
修改基類的定義後,程式運行結果如下:
base constructor!
derived constructor!
derived destructor!
base destructor!
將基類的解構函式聲明為虛函數之後,衍生類別的解構函式也自動成為虛解構函式,在主函數中基類指標p指向的是衍生類別對象,當delete釋放p指標所指向的儲存空間時,會執行衍生類別的解構函式,衍生類別的解構函式執行完之後會緊接著執行基類的解構函式,以釋放從基類繼承過來的成員變數所消耗的資源。如此一來就不會存在記憶體流失問題了。
從此例中我們很明顯可以看出解構函式聲明為虛函數的必要性,但是如果不管三七二十一的將所有的基類的解構函式都聲明為虛函數,這也是不合適的。通常來說,如果基類中存在一個指向動態分配記憶體的成員變數,並且基類的解構函式中定義了釋放該動態分配記憶體的代碼,則應該將基類的解構函式聲明為虛函數。
只有非靜態成員函數才可以成為虛函數,而靜態成員函數不能聲明為虛函數。
例1:
class test{public : virtual test(){a = new int[5];} //error static void g(); //ok virtual void f(); //ok virtual static void h(); //compile error virtual ~test(){delete[] a;} //okprivate: int * a;};
在本例中定義了一個test類,這個類中有一個指標成員變數a,test類中有五個成員函數,在本例中將解構函式和普通成員函數f聲明為虛函數是沒有問題的,將建構函式和靜態成員函式宣告為虛函數則會出現編譯錯誤,這兩種做法都是有違C++文法規定的。
4.4 C++虛解構函式