C++之多继承
1.基礎(chǔ)知識
1.1 類之間的關(guān)系
has-A,uses-A 和 is-A
has-A
包含關(guān)系,用以描述一個(gè)類由多個(gè)“部件類”構(gòu)成。實(shí)現(xiàn)has-A關(guān)系用類成員表示,即一個(gè)類中的數(shù)據(jù)成員是另一種已經(jīng)定義的類。
常和構(gòu)造函數(shù)初始化列表一起使用
uses-A
一個(gè)類部分地使用另一個(gè)類。通過類之間成員函數(shù)的相互聯(lián)系,定義友元函數(shù)友元類或?qū)ο髤?shù)傳遞實(shí)現(xiàn)。
is-A
機(jī)制稱為繼承。關(guān)系具有傳遞性,不具有對稱性。
1.2 繼承概念
萬事萬物都有繼承
1.3 繼承示例代碼
注意:C++中的繼承方式(public、private、protected)會(huì)影響子類的對外訪問屬性。
示例代碼:
#include <iostream> using namespace std;class Parent { public:void print(){a = 0;b = 0;cout<<"a"<<a<<endl;cout<<"b"<<b<<endl;}int b; protected: private:int a; };//class Child : private Parent //class Child : protected Parent public class Child : public Parent { public: protected: private:int c; };void main11() {Child c1;//c1.c = 1;//err//c1.a = 2;//errc1.b = 3;c1.print();cout<<"hello..."<<endl;system("pause");return ; }1.4 重要說明
1、子類擁有父類的所有成員變量和成員函數(shù)
2、子類就是一種特殊的父類
3、子類對象可以當(dāng)作父類對象使用
4、子類可以擁有父類沒有的方法和屬性
2.派生類的訪問控制
派生類繼承了基類的全部成員變量和成員方法(除了構(gòu)造和析構(gòu)之外的成員方法),但是這些成員的訪問屬性,在派生過程中是可以調(diào)整的。
2.1 單個(gè)類的訪問控制
1、類成員訪問級別(public、private、protected)
2、思考:類成員的訪問級別只有public和private是否足夠?
示例代碼:
#include <iostream> using namespace std;//public 修飾的成員變量 方法 在類的內(nèi)部 類的外部都能使用 //protected: 修飾的成員變量方法,在類的內(nèi)部使用 ,在繼承的子類中可用 ;其他 類的外部不能被使用 //private: 修飾的成員變量方法 只能在類的內(nèi)部使用 不能在類的外部//派生類訪問控制的結(jié)論 //1 protected 關(guān)鍵字 修飾的成員變量 和成員函數(shù) ,是為了在家族中使用 ,是為了繼承 //2 項(xiàng)目開發(fā)中 一般情況下 是 public //3class Parent { public:int a; //老爹的名字 protected:int b; //老爹的銀行密碼 private:int c; //老的情人 };//保護(hù)繼承 class Child3 : protected Parent { public: protected: private: public: void useVar(){a = 0; // okb = 0; // ok //b這這里 寫在了子類Child3的內(nèi)部 2看protected 3密碼===>protected//c = 0; //err} };void main() {Child3 c3;//c3.a = 10; //err//c3.b = 20; //err//c3.c = 30;//err}//私有繼承 class Child2 : private Parent { public:void useVar(){a = 0; //okb = 0; //ok//c = 0; // err}protected: private: };void main22() {Child2 c2;//c1.a = 10; err//c2.b = 20; err//c3.b = 30; }class Child : public Parent { public:void useVar(){a = 0; //okb = 0; //ok//c = 0; // err}protected: private: };/* C++中的繼承方式(public、private、protected)會(huì)影響子類的對外訪問屬性 判斷某一句話,能否被訪問1)看調(diào)用語句,這句話寫在子類的內(nèi)部、外部2)看子類如何從父類繼承(public、private、protected) 3)看父類中的訪問級別(public、private、protected) */ //共有繼承 void main21() {Parent t1, t2;t1.a = 10; //ok//t1.b = 20; //err//t2.c = 30 ; //errcout<<"hello..."<<endl;system("pause");return ; }2.2 不同的繼承方式會(huì)改變繼承成員的訪問屬性
1)C++中的繼承方式會(huì)影響子類的對外訪問屬性
public繼承
父類成員在子類中保持原有訪問級別
private繼承
父類成員在子類中變?yōu)閜rivate成員
protected繼承
父類中public成員會(huì)變成protected
父類中protected成員仍然為protected
父類中private成員仍然為private
2)private成員在子類中依然存在,但是卻無法訪問到。不論種方式繼承基類,派生類都不能直接使用基類的私有成員 。
3)C++中子類對外訪問屬性表
2.3 “三看”原則
C++中的繼承方式(public、private、protected)會(huì)影響子類的對外訪問屬性
判斷某一句話,能否被訪問
1)看調(diào)用語句,這句話寫在子類的內(nèi)部、外部
2)看子類如何從父類繼承(public、private、protected)
3)看父類中的訪問級別(public、private、protected)
2.4 派生類類成員訪問級別設(shè)置的原則
思考:如何恰當(dāng)?shù)氖褂胮ublic,protected和private為成員聲明訪問級別?
1、需要被外界訪問的成員直接設(shè)置為public
2、只能在當(dāng)前類中訪問的成員設(shè)置為private
3、只能在當(dāng)前類和子類中訪問的成員設(shè)置為protected,protected成員的訪問權(quán)限介于public和private之間。
示例代碼:
//類的繼承方式對子類對外訪問屬性影響#include <cstdlib> #include <iostream>using namespace std;class A { private:int a; protected:int b; public:int c;A(){a = 0; b = 0; c = 0;}void set(int a, int b, int c){this->a = a; this->b = b; this->c = c;} }; class B : public A { public:void print(){//cout<<"a = "<<a; //errcout<<"b = "<<b; //okcout<<"c = "<<endl; //ok} };class C : protected A { public:void print(){//cout<<"a = "<<a; //errcout<<"b = "<<b; // okcout<<"c = "<<endl; //ok} };class D : private A { public:void print(){//cout<<"a = "<<a; //errcout<<"b = "<<b<<endl; //okcout<<"c = "<<c<<endl; // ok} };int main() {A aa;B bb;C cc;D dd;aa.c = 100; //okbb.c = 100; // ok//cc.c = 100; // err 保護(hù)的//dd.c = 100; // erraa.set(1, 2, 3); //okbb.set(10, 20, 30); //ok//cc.set(40, 50, 60); // err//dd.set(70, 80, 90); //errbb.print(); //okcc.print(); //okdd.print(); ///**/system("pause");return 0; }3.繼承中的構(gòu)造和析構(gòu)
3.1 類型兼容性原則
類型兼容規(guī)則是指在需要基類對象的任何地方,都可以使用公有派生類的對象來替代。通過公有繼承,派生類得到了基類中除構(gòu)造函數(shù)、析構(gòu)函數(shù)之外的所有成員。這樣,公有派生類實(shí)際就具備了基類的所有功能,凡是基類能解決的問題,公有派生類都可以解決。類型兼容規(guī)則中所指的替代包括以下情況:
- 子類對象可以當(dāng)作父類對象使用
- 子類對象可以直接賦值給父類對象
- 子類對象可以直接初始化父類對象
- 父類指針可以直接指向子類對象
- 父類引用可以直接引用子類對象
- 在替代之后,派生類對象就可以作為基類的對象使用,但是只能使用
基類繼承的成員。 - 類型兼容規(guī)則是多態(tài)性的重要基礎(chǔ)之一。
總結(jié):子類就是特殊的父類 (base *p = &child;)
#include <iostream> using namespace std;class Parent { public:void printP(){cout<<"我是爹..."<<endl;}Parent(){cout<<"parent構(gòu)造函數(shù)"<<endl;}Parent(const Parent &obj){cout<<"copy構(gòu)造函數(shù)"<<endl;}private:int a; };class child : public Parent { public:void printC(){cout<<"我是兒子"<<endl;} protected: private:int c; };/* 兼容規(guī)則中所指的替代包括以下情況:子類對象可以當(dāng)作父類對象使用子類對象可以直接賦值給父類對象子類對象可以直接初始化父類對象父類指針可以直接指向子類對象父類引用可以直接引用子類對象*///C++編譯器 是不會(huì)報(bào)錯(cuò)的 ..... void howToPrint(Parent *base) {base->printP(); //父類的 成員函數(shù) }void howToPrint2(Parent &base) {base.printP(); //父類的 成員函數(shù) } void main() {//Parent p1;p1.printP();child c1;c1.printC();c1.printP();//賦值兼容性原則 //1-1 基類指針 (引用) 指向 子類對象Parent *p = NULL;p = &c1;p->printP(); //1-2 指針做函數(shù)參數(shù)howToPrint(&p1);howToPrint(&c1); //1-3引用做函數(shù)參數(shù)howToPrint2(p1);howToPrint2(c1); //第二層含義//可以讓子類對象 初始化 父類對象//子類就是一種特殊的父類Parent p3 = c1;cout<<"hello..."<<endl;system("pause");return ; }3.2 繼承中的對象模型
- 類在C++編譯器的內(nèi)部可以理解為結(jié)構(gòu)體
- 子類是由父類成員疊加子類新成員得到的
問題:如何初始化父類成員?父類與子類的構(gòu)造函數(shù)有什么關(guān)系
- 在子類對象構(gòu)造時(shí),需要調(diào)用父類構(gòu)造函數(shù)對其繼承得來的成員進(jìn)行初始化
- 在子類對象析構(gòu)時(shí),需要調(diào)用父類析構(gòu)函數(shù)對其繼承得來的成員進(jìn)行清理
3.3 繼承中的構(gòu)造析構(gòu)調(diào)用原則
1、子類對象在創(chuàng)建時(shí)會(huì)首先調(diào)用父類的構(gòu)造函數(shù)
2、父類構(gòu)造函數(shù)執(zhí)行結(jié)束后,執(zhí)行子類的構(gòu)造函數(shù)
3、當(dāng)父類的構(gòu)造函數(shù)有參數(shù)時(shí),需要在子類的初始化列表中顯示調(diào)用
4、析構(gòu)函數(shù)調(diào)用的先后順序與構(gòu)造函數(shù)相反
3.4 繼承與組合混搭情況下,構(gòu)造和析構(gòu)調(diào)用原則
- 先構(gòu)造父類,再構(gòu)造成員變量、最后構(gòu)造自己
- 先析構(gòu)自己,在析構(gòu)成員變量、最后析構(gòu)父類
3.5 繼承中的同名成員變量處理方法
1、當(dāng)子類成員變量與父類成員變量同名時(shí)
2、子類依然從父類繼承同名成員
3、在子類中通過作用域分辨符::進(jìn)行同名成員區(qū)分(在派生類中使用基類的同名成員,顯式地使用類名限定符)
4、同名成員存儲在內(nèi)存中的不同位置
總結(jié):同名成員變量和成員函數(shù)通過作用域分辨符進(jìn)行區(qū)分
#include <iostream> using namespace std;class A { public:int a;int b; public:void get(){cout<<"b "<<b<<endl;}void print(){cout<<"AAAAA "<<endl;} protected: private: };class B : public A { public:int b;int c; public:void get_child(){cout<<"b "<<b<<endl;}void print(){cout<<"BBBB "<<endl;} protected: private: };void main() {B b1;b1.print(); b1.A::print();b1.B::print(); //默認(rèn)情況system("pause"); }//同名成員變量 void main71() {B b1;b1.b = 1; //b1.get_child();b1.A::b = 100; //修改父類的bb1.B::b = 200; //修改子類的b 默認(rèn)情況是Bb1.get();cout<<"hello..."<<endl;system("pause");return ; }3.6 派生類中的static關(guān)鍵字
繼承和static關(guān)鍵字在一起會(huì)產(chǎn)生什么現(xiàn)象哪?
理論知識
- 基類定義的靜態(tài)成員,將被所有派生類共享
- 根據(jù)靜態(tài)成員自身的訪問特性和派生類的繼承方式,在類層次體系中具有不同的訪問性質(zhì) (遵守派生類的訪問控制)
- 派生類中訪問靜態(tài)成員,用以下形式顯式說明:
類名 :: 成員
或通過對象訪問 對象名 . 成員
總結(jié):
1> static函數(shù)也遵守3個(gè)訪問原則
2> static易犯錯(cuò)誤(不但要初始化,更重要的顯示的告訴編譯器分配內(nèi)存)
3> 構(gòu)造函數(shù)默認(rèn)為private
4.多繼承
4.1 多繼承概念
一個(gè)類有多個(gè)直接基類的繼承關(guān)系稱為多繼承
- 多繼承聲明語法
class 派生類名 : 訪問控制 基類名1 , 訪問控制 基類名2 , … , 訪問控制 基類名n
{
數(shù)據(jù)成員和成員函數(shù)聲明
}; - 類 C 可以根據(jù)訪問控制同時(shí)繼承類 A 和類 B 的成員,并添加自己的成員
4.2 多繼承的派生類構(gòu)造和訪問
4.3 簡單應(yīng)用
4.4 虛繼承
如果一個(gè)派生類從多個(gè)基類派生,而這些基類又有一個(gè)共同的基類,則在對該基類中聲明的名字進(jìn)行訪問時(shí),可能產(chǎn)生二義性
分析如下:
小結(jié):
1)如果一個(gè)派生類從多個(gè)基類派生,而這些基類又有一個(gè)共同
的基類,則在對該基類中聲明的名字進(jìn)行訪問時(shí),可能產(chǎn)生二義性
2)如果在多條繼承路徑上有一個(gè)公共的基類,那么在繼承路徑的某處匯合點(diǎn),這個(gè)公共基類就會(huì)在派生類的對象中產(chǎn)生多個(gè)基類子對象
3)要使這個(gè)公共基類在派生類中只產(chǎn)生一個(gè)子對象,必須對這個(gè)基類聲明為虛繼承,使這個(gè)基類成為虛基類。
4)虛繼承聲明使用關(guān)鍵字 virtual
4.5 總結(jié)
- 繼承是面向?qū)ο蟪绦蛟O(shè)計(jì)實(shí)現(xiàn)軟件重用的重要方法。程序員可以在已有基類的基礎(chǔ)上定義新的派生類。
- 單繼承的派生類只有一個(gè)基類。多繼承的派生類有多個(gè)基類。
- 派生類對基類成員的訪問由繼承方式和成員性質(zhì)決定。
- 創(chuàng)建派生類對象時(shí),先調(diào)用基類構(gòu)造函數(shù)初始化派生類中的基類成員。 - 調(diào)用析構(gòu)函數(shù)的次序和調(diào)用構(gòu)造函數(shù)的次序相反。
- C++提供虛繼承機(jī)制,防止類繼承關(guān)系中成員訪問的二義性。
- 多繼承提供了軟件重用的強(qiáng)大功能,也增加了程序的復(fù)雜性。
總結(jié)
- 上一篇: 十大笔记本电脑排行_十大笔记本电脑品牌排
- 下一篇: Visual C++ 编译器选项 /MD