題目
爲什麼基類中的析構函數要聲明爲虛析構函數?
解答
用對象指針來調用一個函數,有以下兩種情況:
- 如果是虛函數,會調用派生類中的版本。
- 如果是非虛函數,會調用指針所指類型的實現版本。
析構函數也會遵循以上兩種情況,因爲析構函數也是函數嘛,不要把它看得太特殊。 當對象出了作用域或是我們刪除對象指針,析構函數就會被調用。
當派生類對象出了作用域,派生類的析構函數會先調用,然後再調用它父類的析構函數, 這樣能保證分配給對象的內存得到正確釋放。
但是,如果我們刪除一個指向派生類對象的基類指針,而基類析構函數又是非虛的話, 那麼就會先調用基類的析構函數(上面第2種情況),派生類的析構函數得不到調用。
請看例子:
| class Base{ public: Base() { cout<<"Base Constructor"<<endl; } ~Base() { cout<<"Base Destructor"<<endl; } }; class Derived: public Base{ public: Derived() { cout<<"Derived Constructor"<<endl; } ~Derived() { cout<<"Derived Destructor"<<endl; } }; int main(){ Base *p = new Derived(); delete p; return 0; } |
輸出是:
|
BaseConstructor
DerivedConstructor
BaseDestructor
|
如果我們把基類的析構函數聲明爲虛析構函數,這會使得所有派生類的析構函數也爲虛。 從而使析構函數得到正確調用。
將基類的析構函數聲明爲虛的之後,得到的輸出是:
|
BaseConstructor
DerivedConstructor
DerivedDestructor
BaseDestructor
|
因此,如果我們可能會刪除一個指向派生類的基類指針時,應該把析構函數聲明爲虛函數。 事實上,《Effective C++》中的觀點是,只要一個類有可能會被其它類所繼承, 就應該聲明虛析構函數。
原文地址:http://www.cricode.com/760.html
|
BaseConstructor
DerivedConstructor
DerivedDestructor
BaseDestructor
|
C++中的虛函數是如何工作的?
虛函數依賴虛函數表進行工作。如果一個類中,有函數被關鍵詞virtual進行修飾, 那麼一個虛函數表就會被構建起來保存這個類中虛函數的地址。同時, 編譯器會爲這個類添加一個隱藏指針指向虛函數表。如果在派生類中沒有重寫虛函數, 那麼,派生類中虛表存儲的是父類虛函數的地址。每當虛函數被調用時, 虛表會決定具體去調用哪個函數。因此,C++中的動態綁定是通過虛函數表機制進行的。
當我們用基類指針指向派生類時,虛表指針vptr指向派生類的虛函數表。 這個機制可以保證派生類中的虛函數被調用到。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | class Shape{ public: int edge_length; virtual int circumference () { cout<<"Circumference of Base Classn"; return 0; } }; class Triangle: public Shape{ public: int circumference () { cout<<"Circumference of Triangle Classn"; return 3 * edge_length; } }; int main(){ Shape *x = new Shape(); x->circumference(); // prints “Circumference of Base Class” Shape *y = new Triangle(); y->circumference(); // prints “Circumference of Triangle Class” return 0; } |
原文地址:http://www.cricode.com/751.html
|
BaseConstructor
DerivedConstructor
DerivedDestructor
BaseDestructor
|