第二章 構造 析構 賦值運算

2021-09-23 17:06:35 字數 3867 閱讀 3137

對於乙個class,如果你自己沒有宣告,則編譯器就會為這個類宣告乙個copy建構函式、乙個copy assignment操作符和乙個析構函式。如果你沒有宣告任何建構函式,編譯器還會為你宣告乙個default建構函式。所有這些函式都是publicinline的。不過唯有這些函式被呼叫了,它們才會被編譯器創造出來。

編譯器產出的析構函式是non-virtual的,除非這個class的base class自身宣告有virtual析構函式。

對於編譯器生成的copy建構函式和copy assignment操作符,它們只是單純的將**物件中的每乙個non-static成員拷貝到目標物件中。

如果你宣告了自己的建構函式,編譯器則不會再生成default建構函式,除非你明確要求編譯器生成。

如果class中含有c++中預設無法拷貝的型別(例如被const修飾的型別或reference),則編譯器會拒絕為該class生成copy assignment操作符

如果base class中含有private屬性的copy assignment操作符,那麼在derived class中,編譯器也會拒絕為該class生成copy assignment操作符

將對應得成員函式宣告為private並且不予實現,可以阻止編譯器生成預設版本,同時也可以阻止客戶呼叫。

當base class中包含virtual析構函式時,銷毀其derived class物件時會銷毀所有的base class成分和derived class成分。

如果乙個class不意圖成為乙個基類,則它不應該包含任何virtual函式。

對於virtual函式,需要在執行期才能決定其呼叫。

virtual函式的資訊由vptr(virtual table pointer)指出。vptr指向乙個由函式指標構成的陣列,稱為vtbl(virtual table);每乙個帶有virtual函式的class都有乙個相應的vtbl。當物件呼叫某一virtual函式,實際被呼叫的函式取決於該物件的vptr所指的那個vtbl。

vptr會儲存在物件的記憶體中,virtual函式的存在會增加物件體積。

當class中存在virtual時,就不再可能把它傳遞至(或接受自)其他語言所寫的函式,除非你明確補償vptr,所以就不再具有移植性。

建議當乙個class中至少包含乙個virtual函式時,才為其宣告virtual析構函式。

pure virtual函式的存在會導致abstract class- 也就是不能被實體化的class。你不能為這種型別的class建立物件。

對於那些沒有純虛函式,又希望這個class是抽象類的時候,可以為其宣告乙個pure virtual析構函式。但是你仍要為這個pure virtual虛構函式提供乙份定義。

class awov

awov::~awov(){};

析構函式的運作方式是,最深層派生的那個class的析構函式最先被呼叫,然後是每乙個base class的析構函式被呼叫。編譯器會在awov的derived class的析構函式中建立乙個》對~ awov的呼叫動作,所以需要提供乙份~awov的定義。

如果程式在析構函式中丟擲異常,容易導致程式過早結束或出現未定義的行為。如果乙個被析構函式呼叫的函式可能丟擲異常,析構函式應該捕獲任何異常,然後吞下它們或者結束程式。

對於吞下異常(catch)而言:將異常吞掉並不好,因為它壓制了某些動作失敗的重要資訊,不過即使這樣,吞下異常仍然比由於草率的結束程式或由不明確的行為帶來的風險要好。

對於結束程式而言:通常由呼叫std::abort完成,強制程式結束可以防止異常離開析構函式。

如果客戶需要對某個操作函式執行期間丟擲的異常作出反應,那麼class應該提供乙個普通函式執行該操作。

derived classbase class構造期間,物件的型別時base class而不是derived class。不只virtual函式會被編譯器解析至base class,如使用執行期資訊,也會把物件視為base class,所以物件在derived class建構函式執行前不會成為乙個derived class物件。

對於析構函式,一旦derived class析構函式開始執行,物件內的derived class成分就會成為未定義的值,當進入base class後物件就成為乙個base class物件,所以在析構函式中也不應呼叫virtual函式。

operator=返回乙個reference to *this可以實現「連鎖賦值」操作。

該協議無強制性,但被所有內建型別和標準庫型別共同遵守。

自我賦值會發生在物件被賦值給自己時。為了防止在自我賦值時出現資源的異常釋放,傳統的做法是在operator=的最前面增加」證同測試「的操作。

widget& widget::operator=(const widget& rhs)

使用copy and swap技術,可以保證異常安全和自我賦值安全。

widget& widget::operator=(const widget& rhs)

對於任何函式,如果其操作乙個以上的物件,且其中多個物件是同一物件時,應保證其行為依然正確。

編譯器版本的copying函式,會將被拷貝物件的所有成員都做乙份拷貝。

在自定義的copying函式中,如果你忘了對某些成分做拷貝,編譯器一般不會提醒你,即使在在高警告級別中。

如果你在class增加了成員,你需要對每乙個copying函式進行修改。

derived class的copying函式中,需要複製其每乙個base class成分,如果base class中存在private的成員,則需要呼叫base classcopying函式對base class成分賦值。

不要嘗試用某個copying函式實現另乙個copying函式。應該將共同機能放入第三個函式,並由兩個copying函式共同呼叫。

二 構造 析構 賦值運算

c 編譯器會宣告編譯器版本的copy建構函式,乙個copy assignment操作符和乙個析構函式,此外若沒有宣告乙個建構函式,則還會宣告乙個預設建構函式。這些函式都是public且inline的。copy建構函式和copy assignment操作符,編譯器版本只是單純地將 物件的每乙個non ...

構造 析構 賦值運算

非內建資料型別 一般而言,只有當生出的 合法且有適當機會證明它有意義,編譯器才會為class 生出operator 建構函式 析構函式 stl 或標準庫或已經存在的,不包含虛函式的類,我們不應該繼承它們 比較好的一種辦法是,自己在析構函式中,可以選擇,記錄並退出,或者記錄並繼續執行。但同時提供乙個p...

構造 析構 賦值運算

條款05 了解c 默默編寫並呼叫哪些函式 如果我們寫了乙個空類 class empty 編譯器會為這個類新增一些default的函式,相當於 class empty default建構函式 empty const empty rhs copy建構函式 empty 析構函式 empty operato...