天天看點

c++ oop構造函數與拷貝控制

class Quote {

public:

Quote(int x) :x(x) {}

//如果我們删除的是一個指向派生類對象的基類指針,則需要虛析構函數

virtual ~Quote() = default;//動态綁定析構函數

int x;

virtual void show()

{

cout << x << endl;

}

};

class Bulk_quote :public Quote

{

public:

Bulk_quote(int x,int y) :Quote(x), y(y) {}

int y;

void show()

{

cout << y << endl;

}

};

int main()

{

構造函數與拷貝控制

//繼承體系中的類和其他的類一樣,也會有建立、拷貝、移動、指派和銷毀,沒有定義時編譯器也會合成一個版本,合成的版本也定義為删除的。

虛析構函數

//繼承關系對基類拷貝控制最直接的影響是基類通常應該定義一個虛析構函數,這樣就能動态配置設定繼承體系中的對象了。

//虛析構函數也會被繼承,delete基類指針時将運作真确的析構函數版本。

//如果基類的析構函數不是虛函數,則delete一個指向派生類對象的基類指針将産生未定義的行為。

//虛析構函數将阻止合成移動操作。

Quote* itemP = new Quote(9);//靜态類型與動态類型一緻

delete itemP;//調用Quote的析構函數

itemP = new Bulk_quote(9,8);//靜态類型與動态類型不一緻

delete itemP;//調用Bulk_quote的析構函數

合成拷貝控制與繼承

//除了對類本身的成員依次進行初始化、指派或銷毀的操作外,合成的成員還負責使用直接基類中對應的操作對一個對象直接基類部分進行相應的操作。

//基類中定義的拷貝控制函數是被删除的活不可通路的,則派生類中對應的成員将是被删除的。

//如果在基類中有一個不可通路或被删除的析構函數,則派生類中合成的預設和拷貝構造函數将是被删除的,因為編譯器無法銷毀派生類對象的基類部分。

//編譯器不會合成一個删除的移動操作。

class B

{

public:

B();

B(const B&) = delete;

};

class D :public B{};

D d;//正确 D會合成預設構造函數使用B的預設構造函數

D d2(d);//錯誤 D的合成拷貝構造函數是被删除的

D d3(std::move(d));//錯誤 隐式地使用D的被删除的拷貝構造函數

移動操作與繼承

//預設情況下,基類通常不含有合成的移動操作,而且在它的派生類中也沒有合成的移動操作。

//因為基類缺少移動操作會阻止派生類擁有自己的合成移動操作,是以當我們需要執行移動操作時,應該首先在基類中進行定義。

//除非派生類含有排斥移動的成員,否則将自動獲得合成的移動操作。

class Quote

{

public:

Quote() = default;//對成員依次進行預設初始化

Quote(const Quote&) = default;//隊成員依次拷貝

Quote(Quote&&) = default;//隊成員依次拷貝

Quote& operator=(const Quote&) = default;//拷貝指派

Quote& operator=(Quote&&) = default;//移動指派

virtual ~Quote() = default;

};

派生類的拷貝控制成員

//當派生類定義了拷貝或移動操作時,該操作負責拷貝或移動包括基類部分成員在内的整個對象。

class Base{};

class D :public Base

{

public:

//預設情況下,基類的預設構造函數初始化對象的基類部分

//要想使用拷貝或移動構造函數,必須在構造函數初始值清單中顯示地調用該構造函數

D(const D& d) :Base(d)

{}

D(D&& d):Base(std::move(d)){}

//派生類指派運算符

//派生類的指派運算符也要顯示的為基類部分指派

D& operator=(const D& rhs)

{

Base::operator=(rhs);//為基類部分指派

return *this;

}

//析構函數隻負責銷毀派生類自己配置設定的資源,基類部分會隐式的自動銷毀。

//也就是析構函數隻負責自己成員的銷毀。

~D();

};

//如果構造函數或析構函數調用了某個虛函數,則我們應該執行與構造函數或析構函數所屬類型相對應的虛函數版本。

繼承的構造函數

//一個類隻能繼承其直接基類的構造函數。類不能繼承預設、拷貝和移動構造函數。

//派生類繼承基類構造函數的方式是提供一條注明基類名的using聲明語句。

class D :public Base

{

public:

//當作用于構造函數時,using聲明語句将令編譯器産生代碼,不會改變該構造函數的通路級别。

//using聲明語句不能指定explicit或constexpr。

using Base::Base;//繼承Base的構造函數

};

容器與繼承

//容器和存在繼承關系的類型無法相容

//在容器中放置指針或智能指針而非對象

vector<shared_ptr<Quote>> bask;

bask.push_back(make_shared<Quote>(10));

bask.push_back(make_shared<Bulk_quote>(6,7));

bask.back()->show();

return 0;

}

繼續閱讀