析構函式為你提供了一種機制,可以讓你在系統釋放物件前做一些善後工作,如清理記憶體,釋放空間等。但是在以下兩個方面使用虛構函式時要尤為注意。
1. 多型性
具有多型性質的base classes的析構函式應該為virture,或者說任何帶有virture函式的class都應該有virture析構函式。考慮下面的**:
class a;
class b: public a;
int main()
如果以上**出現在你的專案中,那麼就會造成災難性的後果。在上述**中,b類的物件經由乙個base class(a類)指標被刪除,但是base class(a類)有個non-virture析構函式,這就導致其內的b類成分沒有被銷毀(宣告於b類的成員變數),然而其base class成分(也就是a類這一部分)通常會被銷毀,於是導致乙個詭異的「區域性銷毀」物件。資源洩露、敗壞資料結構等問題都會因此而產生。
消除這個問題很簡單,只要給base class加上乙個virture析構函式即可。之後刪除derived class物件就會如你想象的那樣銷毀整個物件,包括所有的derived class部分。但是如果class中沒有virture函式,不打算被當做base class,那麼將析構函式宣告為virture並不是一件好事。原因是如果class中有virture函式,那麼class就會包含乙個vptr來指向vtbl,這會導致class的體積增加。因此該類也不會和其他語言內的相同宣告有著一樣的結構,除非你明確補償vptr,否則其不再具有移植性。對此我們只要記住:只有當class內含有至少乙個virture函式,才為它宣告virture析構函式。此外,如果你企圖繼承乙個標準容器或是任何其他帶有「non-virture析構函式」的class,拒絕**吧!
2. 異常
不要讓異常逃出析構函式。如果析構函式吐出異常,那麼程式有可能出現不明行為或過早的結束。因此我們要謹記如果析構函式呼叫的函式可能丟擲異常,析構函式應該捕捉任何異常然後吞下它們或結束程式。如果客戶需要對某個函式執行期間丟擲的異常進行反應,那麼class應該提供乙個普通函式(而非析構函式)來執行該操作。
3. 虛函式
不要在建構函式和析構函式內呼叫virture函式,否則會給你帶來意料不到的結果。下面的例子可以說明問題:
class transaction;
transaction::transaction()
class buytransaction: public transaction;
buytransaction b;
毫無疑問,buytransaction的建構函式會被呼叫。但是在這之前,transaction的建構函式會被更早被呼叫。derived class內的base class成分會在derived class自身成分被構造之前先構造妥當。transaction的建構函式在最後一行呼叫virture函式logtransaction,這也是引起問題的地方。因為它呼叫的是transaction內的版本而不是buytransaction版本,即使當前建立的物件型別是buytransaction。在base class構造期間,virture函式不會下降到derived class階層。
以上問題的根本原因是:在derived class物件的base class構造期間,物件的型別是base class而不是derived class。不止virture函式會被編譯器解析至base class,若使用執行期型別資訊,也會把物件視為base class型別。
相同的道理也適用於析構函式。一旦derived class的析構函式開始執行,物件內的derived class的成員變數便呈現出未定義值。所以c++視它們彷彿不存在。進入base class析構函式後,物件就成為乙個base class物件,而c++得任何部分,包括virture函式、dynamic_casts等等也就那麼看它。還要注意的是,我們不僅要確定建構函式和析構函式沒有呼叫virture函式,也要保證它們所呼叫的函式也要服從這一約定。
而我們要怎樣解決上面的問題呢?一種方法是將transaction類的logtransaction函式改為non-virture,然後要求buytransaction傳遞必要的資訊給transaction建構函式,然後那個函式便可以安全地呼叫non-virture的logtransaction函式。
class transaction;
class buytransaction: public transaction
private:
static std::string createlogstring(parameters);
};
換句話說,你無法使virture函式從base class向下呼叫,在構造期間,你可以藉由「令derived class將必要的構造資訊向上傳遞至base class的建構函式」替換之而加以彌補。比起使用成員初始化列表賦予base class所需資料,使用乙個輔助函式來建立乙個值傳遞給base class往往比較方便。而令此函式為static,也不可能指向「初期未成熟的buytransaction物件內尚未初始化的成員變數」。 構造和析構的幾點注意事項
變數要初始化,初始化盡量使用初始化列表。如果不初始化,變數的值是隨機不確定的。class point 改進之後後 class point private int x int y 私有析構函式,類物件不能在棧上分配空間.只能在堆上分配,但不能delete直接釋放。include using names...
析構函式和建構函式的注意事項
1建構函式 在c 中,使用建構函式來實現物件的初始化。建構函式的特點 1 建構函式無需呼叫,建立物件時自動執行。2 建構函式的名稱必須與類名相同,不能隨意起名 不同以初始化函式,雖然和其功能相同 3 建構函式沒有返回值。4 如果程式者沒有自定義建構函式,系統會自動呼叫預設的建構函式。5 如果程式者自...
python析構函式用法及注意事項
1 主動刪除物件呼叫del 物件 程式執行結束後,python也會自動進行刪除其他的物件。class animal def del 程式設計客棧self print 銷毀物件 format self cat animal cat2 animal del cat2 print 程式結束 2 如果重寫子...