網(wǎng)站制作 北京網(wǎng)站建設(shè)公司seo搜索鋪文章
全文目錄
- 繼承的概念
- 定義格式
- 繼承關(guān)系和訪問限定符
- final
- 基類和派生類對象賦值轉(zhuǎn)換
- 繼承中的作用域
- 派生類的六個默認(rèn)成員函數(shù)
- 構(gòu)造函數(shù)
- 拷貝構(gòu)造函數(shù)
- operator=
- 析構(gòu)函數(shù)
- 友元和靜態(tài)成員
- 友元
- 靜態(tài)成員
- 各種繼承形式
- 菱形繼承
- 虛繼承
- 菱形虛擬繼承對象模型
- 繼承和組合
繼承的概念
通過繼承機制,可以利用已有的數(shù)據(jù)類型來定義新的數(shù)據(jù)類型。所定義的新的數(shù)據(jù)類型不僅擁有新定義的成員,而且還同時擁有舊的成員。我們稱已存在的用來派生新類的類為基類,又稱為父類。由已存在的類派生出的新類稱為派生類,又稱為子類。
簡單來說就是在一個類的基礎(chǔ)上進(jìn)行擴展。
定義格式
繼承關(guān)系和訪問限定符
繼承方式和訪問限定符可以合出九種組合:
總結(jié):
- 基類 p r i v a t e private private 成員在派生類中無論以什么方式繼承都是不可見的。這里的不可見是指基類的私有成員還是被繼承到了派生類對象中,但是語法上限制派生類對象不管在類里面還是類外面都不能去訪問它。
- 基類的其他成員 在子類的訪問方式 = = M i n ( 成員在基類的訪問限定符,繼承方式 ) 在子類的訪問方式 == Min(成員在基類的訪問限定符,繼承方式) 在子類的訪問方式==Min(成員在基類的訪問限定符,繼承方式) , p u b l i c > p r o t e c t e d > p r i v a t e public > protected > private public>protected>private
- 基類 p r i v a t e private private 成員在派生類中是不能被訪問,如果基類成員不想在類外直接被訪問,但需要在派生類中能訪問,就定義為 p r o t e c t e d protected protected ??梢钥闯霰Wo(hù)成員限定符是因繼承才出現(xiàn)的。
- 使用關(guān)鍵字 c l a s s class class 時默認(rèn)的繼承方式是 p r i v a t e private private ,使用 s t r u c t struct struct 時默認(rèn)的繼承方式是 p u b l i c public public ,不過最好顯示的寫出繼承方式。
- 在實際運用中一般使用都是 p u b l i c public public 繼承,幾乎很少使用 p r o t e t c e d / p r i v a t e protetced/private protetced/private 繼承,也不提倡使用 p r o t e t c e d / p r i v a t e protetced/private protetced/private 繼承,因為 p r o t e t c e d / p r i v a t e protetced/private protetced/private 繼承下來的成員都只能在派生類的類里面使用,實際中擴展維護(hù)性不強
final
如果一個類不想被繼承可以使用final
修飾:
class Person final // 表示該類不可被繼承
{...
}
基類和派生類對象賦值轉(zhuǎn)換
- 派生類對象 可以賦值給 基類的對象 / 基類的指針 / 基類的引用。這里有個形象的說法叫切片或者切割。寓意把派生類中父類那部分切來賦值過去。
Student sobj;
// 1.子類對象可以賦值給父類對象/指針/引用
Person pobj = sobj;
Person* pp = &sobj;
Person& rp = sobj;
- 基類對象不能賦值給派生類對象。
Student sobj;
Person pobj = sobj;
//2.基類對象不能賦值給派生類對象
sobj = pobj; // err
- 基類的指針或者引用可以通過強制類型轉(zhuǎn)換賦值給派生類的指針或者引用。但是必須是基類的指針是指向派生類對象時才是安全的。
Student sobj;
Person pobj = sobj;
Person* pp = &sobj;// 3.基類的指針可以通過強制類型轉(zhuǎn)換賦值給派生類的指針
Student* ps1 = (Student*)pp; // 這種情況轉(zhuǎn)換時可以的。
ps1->_No = 10;
pp = &pobj;
Student* ps2 = (Student*)pp; // 這種情況轉(zhuǎn)換時雖然可以,但是會存在越界訪問的問題
ps2->_No = 10 // 越界
繼承中的作用域
子類和父類都有獨立的作用域。
隱藏: 如果子類和父類有同名成員,子類將會隱藏父類成員,優(yōu)先訪問子類中的成員,但是弄夠通過指定作用域來訪問父類中的成員 (父類::父類成員)。
注意: 如果是成員函數(shù)的隱藏,只要是函數(shù)名相同就構(gòu)成隱藏。如果父類與子類中的函數(shù)參數(shù)類型與個數(shù)不相同,也會發(fā)生隱藏現(xiàn)象,也就是說不會發(fā)生重載 (因為在兩個不同的作用域中)。
派生類的六個默認(rèn)成員函數(shù)
派生類和基類的構(gòu)造析構(gòu)順序:
構(gòu)造函數(shù)
派生類的構(gòu)造函數(shù)必須調(diào)用基類的構(gòu)造函數(shù)初始化基類的那一部分成員。如果基類沒有默認(rèn)的構(gòu)造函數(shù),則必須在派生類構(gòu)造函數(shù)的初始化列表階段顯示調(diào)用。
Son() // 子類構(gòu)造: _Parent(...) // 父類構(gòu)造
{}
拷貝構(gòu)造函數(shù)
派生類的拷貝構(gòu)造函數(shù)必須調(diào)用基類的拷貝構(gòu)造完成基類的拷貝初始化。
Son(const Son& son) // 子類拷貝構(gòu)造: _Parent(son) // 父類拷貝構(gòu)造,通過切片直接調(diào)用
{}
operator=
Son& operator=(const Son& son)
{if (this != &son) // 防止自己給自己賦值{Parent::operator=(son); // 利用切片調(diào)用父類的賦值運算符....}
}
析構(gòu)函數(shù)
派生類的析構(gòu)函數(shù)會在被調(diào)用完成后自動調(diào)用基類的析構(gòu)函數(shù)清理基類成員。因為這樣才能保證派生類對象先清理派生類成員再清理基類成員的順序。
~Son()
{...
} // 函數(shù)結(jié)束自動調(diào)用父類析構(gòu)
編譯器會對析構(gòu)函數(shù)進(jìn)行特殊處理,統(tǒng)一處理成destructor(),所以所有的析構(gòu)函數(shù)都是同名函數(shù),子類析構(gòu)和父類析構(gòu)構(gòu)成隱藏。
友元和靜態(tài)成員
友元
友元關(guān)系不能繼承,也就是說基類友元不能訪問子類私有和保護(hù)成員
靜態(tài)成員
基類定義了 s t a t i c static static 靜態(tài)成員,則整個繼承體系里面只有一個這樣的成員。無論派生出多少個子類,都只有一個 s t a t i c static static 成員實例
改變?nèi)魏我粋€基類變量的靜態(tài)成員,整個繼承體系中的靜態(tài)成員都會跟著改變。
各種繼承形式
單繼承: 一個子類只有一個直接父類時稱這個繼承關(guān)系為單繼承
多繼承: 一個子類有兩個或以上直接父類時稱這個繼承關(guān)系為多繼承
菱形繼承
菱形繼承是多繼承的一種特殊情況。
菱形繼承有數(shù)據(jù)冗余和二義性的問題:
// 這樣會有二義性無法明確知道訪問的是哪一個
Assistant a ;
a._name = "peter";
// 需要顯示指定訪問哪個父類的成員可以解決二義性問題,但是數(shù)據(jù)冗余問題無法解決
a.Student::_name = "xxx";
a.Teacher::_name = "yyy";
虛繼承
虛擬繼承virtual
可以解決菱形繼承的二義性和數(shù)據(jù)冗余的問題。如上面的繼承關(guān)系,在Student和Teacher的繼承Person時使用虛擬繼承,即可解決問題
class Teacher : virtual public Person
...class Student : virtual public Person
...
菱形虛擬繼承對象模型
先將繼承關(guān)系簡化,方便觀察:
class A
{
public:int _a;
};// class B : public A
class B : virtual public A
{
public:int _b;
};// class C : public A
class C : virtual public A
{
public:int _c;
};class D : public B, public C
{
public:int _d;
};int main()
{D d;d.B::_a = 1;d.C::_a = 2;d._b = 3;d._c = 4;d._d = 5;return 0;
}
通過簡化的繼承關(guān)系借助內(nèi)存窗口觀察對象成員的模型:
虛擬繼承的原理:
虛擬繼承就是將祖父類(最基礎(chǔ)的類)存放在最后面,其每個虛繼承的父類的第一個位置存放到祖父類的偏移量。
在實際設(shè)計中應(yīng)該盡量避免使用菱形繼承
繼承和組合
組合:在類中定義另一個類
class Student
{
private:Person pson;...
}
- public繼承是一種is-a的關(guān)系。也就是說每個派生類對象都是一個基類對象。
- 組合是一種has-a的關(guān)系。假設(shè)B組合了A,每個B對象中都有一個A對象。
- 繼承允許你根據(jù)基類的實現(xiàn)來定義派生類的實現(xiàn)。這種通過生成派生類的復(fù)用通常被稱為白箱復(fù)用(white-box reuse)。術(shù)語“白箱”是相對可視性而言:在繼承方式中,基類的內(nèi)部細(xì)節(jié)對子類可見 。繼承一定程度破壞了基類的封裝,基類的改變,對派生類有很大的影響。派生類和基類間的依賴關(guān)系很強,耦合度高。
- 對象組合是類繼承之外的另一種復(fù)用選擇。新的更復(fù)雜的功能可以通過組裝或組合對象來獲得。對象組合要求被組合的對象具有良好定義的接口。這種復(fù)用風(fēng)格被稱為黑箱復(fù)用(black-box reuse),因為對象的內(nèi)部細(xì)節(jié)是不可見的。對象只以“黑箱”的形式出現(xiàn)。組合類之間沒有很強的依賴關(guān)系,耦合度低。優(yōu)先使用對象組合有助于你保持每個類被封裝。
所以在日常編程中盡量使用組合,避免使用繼承
但是有時候繼承又是必不可少的,比如后面要講的多態(tài)。