C++Primer_Chap15_面向物件程式設計_List07_建構函式與拷貝控制_筆記
虛解構函式
虛解構函式由於動態繫結可以確保delete基類指標時將執行正確的解構函式版本。一般,如果一個類需要解構函式,那麼它同樣需要拷貝和賦值操作。但基類的解構函式不遵循上述準則,是一個重要的例外。
虛解構函式將組織合成移動操作。如果一個類定義了解構函式,即使它通過=default的形式使用了合成版本,編譯器也不會為這個類合成移動操作。
(如果我們不宣告自己的拷貝建構函式或拷貝賦值運算子,編譯器總會為我們合成這些操作。拷貝操作要麼被定義成逐成員拷貝,要麼被定義為物件賦值,要麼定義為delete的函式。)
合成拷貝控制與繼承
基類或派生類可能出於某些原因將合成的預設建構函式或者任何一個拷貝控制成員定義為delete函式。此外,某些定義基類的方式也可能導致有的派生類成員稱為delete的函式:
- 如果基類中的預設建構函式、拷貝建構函式、拷貝賦值運算子或解構函式是delete的或者不可訪問的(例如private),則派生類中對於的成員將是delete的,原因是編譯器不能使用基類成員來執行派生類物件基類部分的構造、賦值或銷燬操作
- 如果在基類中有一個不可訪問或刪除的解構函式,則派生類中合成的預設和拷貝建構函式將是delete,因為編譯器無法銷燬派生類物件的基類部分
- 編譯器將不會合成一個delete的移動操作。當我們使用=default請求一個移動操作時,如果基類中的對應操作是刪除的或不可訪問的,那麼派生類中該函式將是delete,原因是派生類物件的基類部分不可移動。
派生類的拷貝控制成員
當派生類定義了拷貝或移動操作時,該操作負責拷貝或移動包括基類部分在內的整個物件
class Base {/*……*/} class D : public Base { public: //預設情況下,基類的預設建構函式初始化物件的基類部分 //想要使用拷貝或移動建構函式,我們必須在建構函式初始化列表中 //顯式地呼叫該建構函式 D(const D& d) : Base(d) /*D的成員初始值*/ {/*……*/} D(D&& d) : Base(std::move(d)) /*D的成員初始值*/ {/*……*/} };
在預設情況下,基類預設建構函式初始化派生類物件的基類部分。如果我們想拷貝(移動)基類部分,則必須在派生類的建構函式初始化列表中顯示地使用基類的拷貝(移動)建構函式
派生類賦值運算子
D &D::operator=(const D &rhs)
{
Base::operator=(rhs);
/*……*/
return *this;
}
如果建構函式或解構函式呼叫了某個虛擬函式,則我們應該指向與建構函式或解構函式所屬型別相對於的虛擬函式版本。
繼承的建構函式
一個類只初始化它的直接基類,也只繼承其直接基類的建構函式。類不能繼承預設、拷貝和移動建構函式。如果派生類沒有直接定義這些建構函式,編譯器將為派生類合成他們。派生類繼承基類建構函式的方式是提供一條註明了直接基類名的using宣告語句:
class Bulk_quote : public Disc_quote{
public:
using Disc_quote::Disc_quote;
double net_price(std::size_t) const override;
};
通常情況下,using宣告語句只是令某個名字在當前作用域內可見,而當作用於建構函式時,using宣告語句將令編譯器產生程式碼,即對於基類的每個建構函式,編譯器都在派生類中生成一個形參列表完全相同的建構函式:
derived(parms) : base(args) {}
Bulk_quote(const std::string& book, double p,
std::size_t qty, double disc) :
Disc_quote(book, p, qty, disc) {}
繼承的建構函式的特點
和普通成員的using宣告不同,一個建構函式的using宣告不會改變該建構函式的訪問級別(不管using出現在哪,基類的private建構函式在派生類中還是private)。而且,一個using宣告語句不能指定explicit和constexpr(如果基類的建構函式是explicit或者constexpr,繼承的建構函式也具有相同的屬性)。
當一個基類建構函式含有預設實參時,這些實參不會被繼承。且派生類將或得多個繼承的建構函式,其中每個構造安徽省農戶分別省略掉一個含有預設實參的形參。(即,如果基類有一個接受兩個形參的建構函式,其中第二個形參含有預設實參,則派生類將獲得兩個建構函式:1.建構函式接受兩個形參(沒有預設實參);2.建構函式只接收一個形參,對應基類中最左側的沒有預設實參的那個形參)
如果基類含有幾個建構函式,則除了兩個列外情況,大多數時候派生類會繼承所有這些建構函式:
- 派生類可以繼承一部分建構函式,而為其他建構函式定義自己的版本。如果派生類定義的建構函式與基類的建構函式具有相同的引數列表,則該建構函式不會被繼承,定義在派生類中的建構函式將替換繼承而來的建構函式
- 預設、拷貝和移動建構函式不會被繼承