二 構造 析構 賦值運算

2022-07-12 00:39:09 字數 3957 閱讀 5697

c++編譯器會宣告編譯器版本的copy建構函式,乙個copy assignment操作符和乙個析構函式,此外若沒有宣告乙個建構函式,則還會宣告乙個預設建構函式。這些函式都是public且inline的。

copy建構函式和copy assignment操作符,編譯器版本只是單純地將**物件的每乙個non-static成員變數拷貝到目標物件。

如果打算對乙個內含reference和const的類支援賦值操作,那需要自己定義copy assignment操作符。此外,在多型場景下,base class將copy assignment操作符宣告為private,那麼編譯器會拒絕為derived class提供乙個copy assignment,因為derived class中的copy assignment函式是預設需要對base class部分的成員進行處理的。

可以將copy建構函式和copy assignment操作符宣告為private,避免產生無意義的複製行為:

class homeforsale
當有了上述定義後,若在member函式或者friend函式中使用copy或copy assignment,通常也不能編譯通過,需要定義乙個專門為了阻止copying動作而設計的base class內:

class uncopyable

~uncopyable() {}

private:

uncopyable(const uncopyable &);

uncopyable& operator=(const uncopyable&);

};//此時繼承uncopyable即可,無需再次宣告

class homeforsale: private uncopyable

為駁回編譯器自動提供的函式,可以將相應的函式宣告為private並且不予實現,或者使用類似於uncopyable這樣的base class。

c++指出,當derived class物件經由乙個base class指標被刪除,而該base class帶著乙個non-virtual析構函式,會造成未定義結果。實際上通常發生的是物件的derived成分沒被銷毀,然而其base class則會詭異的被銷毀,會造成資源洩漏,難以除錯。

解決辦法如下:

class timekeeper;

timekeeper* ptk = gettimekeeper();

...delete ptk;

通常base class除開virtual析構函式,還會有多個virtual函式,因為virtual函式的目的是允許derived class的實現得以客製化,即在不同的需求下有不同的實現。

若不是base class,但卻攜帶有virtual函式,會增加物件的體積。64位計算機中可能會占用64-128bits,指標佔64bits.因此增加有virtual函式的物件,其大小會增加50-100%。這會導致無法將該物件傳遞到其他函式所使用的函式中,失去移植性。。

析構函式的運作方式是,最深層派生的class其析構函式首先被呼叫,之後回溯呼叫每乙個base class的析構函式。

多型性質的base class應該宣告乙個virtual析構函式,若乙個函式帶有任何virtual函式,那麼他應該擁有乙個virtual析構函式。

若乙個class的設計目的不是為了實現多型或者作為base class,那麼不應該宣告virtual析構函式。

對於異常,往往較佳的策略是重新設計函式介面,使其客戶有機會對可能出現的問題作出反應。見dbconn class:

class dbconn

~dbconn()

catch (...) }}

private:

dbconnection db;

bool closed;

};

本例中提供了乙個供使用者對異常做出反應的普通函式,若使用者放棄對異常的處理,則再依賴析構函式來吞下異常或結束程式。

總而言之,析構函式最好不要吐出異常。

base class構造期間virtual函式絕不會下降到derived classes階層。不正式的說法為:在base class構造期間,virtual函式不是virtual函式。這是由於base class的執行早於derived class建構函式,當base class建構函式執行時derived class函式的成員變數尚未初始化,若此時base class的virtual函式下至derived class階層,則會導致未定義的行為。這是derived class必然使用成員local成員變數導致的。

上述更根本的原因是:在derived class物件的base class構造期間,物件的型別是base class而不是derived class,不只virtual函式會被編譯器解析至base class,若使用執行期間的型別資訊(dynamic_cast和typeid),也會把物件視為base class型別。因為當base class正在允許建構函式初始化時,derived class的成員變數還未初始化,所以最安全的辦法就是視這些變數不存在,因此便不會下放到derived class。

確保每一次建構函式和析構函式都沒有呼叫vitual函式的用法,參見transaction類:

class transaction;

transaction::transaction(const std::string& info)

class buytransaction:public transaction

private:

static std::string createlogstring(type parameters); //與成員初值列相比,使用帶static關鍵字建立的輔助函式傳遞資訊給建構函式可讀性更高,也更方便。

}

可以更好的連續呼叫「=」.

第乙份operator=實現**:

class bitmap ;

class widget;

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

第二份operator=實現**:

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

第三份operator=實現**

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

本例中處理了new構造過程中可能導致的異常,也可以應付自我賦值的問題。但還可以通過copy and swap繼續優化:

class widget;

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

本段**運用了以下事實:

i) class的copy assignment操作符可能被宣告為「以by value方式接受實參」;

ii) 以by value方式傳遞東西會造成乙份附件;

請記住:

1.確保物件自我賦值時有良好行為,其中技術包括「比較**」和「目標物件」的位址,以及精心設計的語句順序和copy-and-swap技術;

2.確保任何函式操作乙個以上的物件,而其中多個物件是同乙個物件時,其行為仍然正確。

如果你發現你的copy建構函式和copy assignment操作符有相應的**,消除重複的**正確做法應是:建立乙個新的成員函式給兩者呼叫,這樣的函式往往是private而且常被命名為init.

請記住:

1.copying函式應該確保複製「物件內的所有成員變數」及「所有base class成分」。

2.不要嘗試以某個copying函式實現另乙個copying函式。應該將共同機能放進第三個函式中由兩個函式共同呼叫。

構造 析構 賦值運算

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

構造 析構 賦值運算

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

C 構造 析構 賦值運算

有時候,某個物件是獨一無二的,不能沒複製也不能被賦值!所以我們要強制編譯器不允許使用 和copy 建構函式,但如果你不寫他們,編譯器又會自動幫你加上,問題由此引發。class home uncopyable private uncopyable const uncopyable uncopyable...