首先,我們得知道,面向對象的三大語言:封裝 繼承 多态
今天我們主要談談繼承
1.什麼是繼承
——子類(派生類)可以通路和使用父類(基類)的成員
比如:有兩個類,A和B,我們在定義時,使得B 可以通路 A 的成員,我們叫做 B繼承了A。
B為子類,(派生類);A為父類(基類)
2.為什麼要用繼承呢 ?
代碼分析:
#include<iostream>
using namespace std;
class A
{
public:
void fun1(){}
void fun2(){}
};
class B :public A
{
public:
void fun3(){}
void fun4(){}
};
int main()
{
B b;
b.fun1();
b.fun2();
b.fun3();
b.fun4();
return ;
}
繼承是為了提高程式的複用性。
如上述代碼,不需要額外定義fun1( )和fun2(),直接從父類繼承即可。
3.三種繼承關系
- 成員通路限定符&繼承關系:

- 三種繼承關系下基類成員在派生類的通路關系變化:
- 問題1:為什麼已經有了私有成員還要有保護成員?
答:基類的私有成員在派生類中是無法被通路的,但如果一些基類成員,不想被基類對象直接通路,需要在派生類中通路,就定義為保護成員。保護成員限定符是為了繼承而生的。
- 問題2:public / protected / private繼承之間的差別是什麼?
public繼承是一個接口繼承,保持 is-a 原則,(我是一個你)
每個父類可用的對象對子類也可用,每個子類對象也是一個父類對象。
protected / private 繼承是一個實作繼承,是 has -a 的關系原則,(我有一個你)
基類的部分成員,并未完全成為子類接口的一部分。
-
問題3:什麼是成員不可見?
答:不管是哪種繼承方式,在派生類内部都可以通路基類的共有成員和保護成員,基類的私有成員存在,但是在派生類中無法通路。(不可見)
- 使用關鍵字class時預設的繼承方式是private,使用struct時預設的繼承方式是public,不過最好顯示的寫出繼承方式。
4.繼承規則
- A B兩個類不相關,不能繼承,能發生繼承關系的兩個類必須具有相關性。
- 從邏輯上看,B是A 的一種,可以繼承(比如:男人是人的一種,男人可以繼承人的特性)
- 從邏輯上看,B是A 的一種,并且 A 的功能和屬性對B 有意義,才可以繼承。(比如:鴕鳥是鳥的一種,但是鴕鳥不會飛,鴕鳥将這個功能繼承過來毫無意義)
5.繼承與轉換——指派相容規則——public繼承
- 子類對象可以指派給父類對象(切割/切片)
- 父類對象不可以指派給子類對象
- 父類的指針/ 引用可以指向子類對象
- 子類的指針/ 引用不能指向父類對象(可以通過強制類型轉換實作)
代碼分析:
class Person
{
public:
void Show()
{
cout<<_name << endl;
}
protected:
string _name;
private:
int _age;
};
//class Student :private Person
//class Student :protected Person
class Student :public Person
{
public:int _num;
};
int main()
{
Person p;
Student s;
p = s;//1.子類對象可以直接指派給父類對象(切割/切片)
//s = p;//2.父類對象不能直接指派給子類對象(空間不足)
//3.父類的指針/引用,可以指向子類對象
Person *p1 = &s;
Person&r = s;//不加const也能引用,——天然支援
//4.子類的指針/引用不能指向父類的對象(但可以通過強制轉換實作)
Student*p2 = (Student*)&p;
Student&r2 = (Student&)p;
//const Student &r2 = p;//編不過:天然不支援
system("pause");
return ;
}
6.繼承體系中的作用域:
- 在繼承體系中,基類和派生類都有獨立的作用域
-
子類和父類中有同名成員,子類成員将屏蔽父類對成員的直接通路。
(在子類成員函數中,可使用基類::基類成員 )通路 ——隐藏(重定義)
- 注意:在實際中最好不要定義同名成員。
7.單繼承&&多繼承&&菱形繼承
- 單繼承:一個子類隻有一個直接父類時稱這個繼承關系為單繼承。
- 多繼承:一個子類有兩個或兩個以上直接父類稱這個繼承關系為多繼承。
C++——繼承(單繼承、多繼承、菱形繼承)&&虛繼承&&虛基類 - 菱形繼承
C++——繼承(單繼承、多繼承、菱形繼承)&&虛繼承&&虛基類
菱形繼承存在問題:二義性和 資料備援(虛繼承解決)
**注:
1.虛基表中存的是一個偏移量(相對位址,而不是實際位址)**
2.同類型對象共享一個虛基表(偏移量相同)
3.虛基表的使用:節省空間
8.虛繼承
代碼分析:
//不是虛繼承時
#include<iostream>
using namespace std;
class A
{
public:
int _a;
};
class B:public A
{
public:
int _b;
};
class C :public A
{
public:
int _c;
};
class D:public B,public C
{
public :
int _d;
};
int main()
{
D dd;
cout << sizeof(dd) << endl;
dd.B::_a = ;
dd._b= ;
dd.B::_a = ;
dd._c = ;
dd._d = ;
B bb;
C cc;
system("pause");
return ;
}
運作結果:
結果分析:
D中一共隻有四個變量,但此時sizeof 的結果為20,也就是說有5個值,那這五個值是從哪來的呢?
——實際上D繼承了兩個_a, B繼承的_a 和 A繼承的_a是不一樣的,無法确定哪一個值應該是_a,是以系統預設有兩個_a。(有歧義)
在監視視窗上看到,程式的執行步驟為:
- B中的_a 變成 1
- D中的_b 變成 3
- C中的_a 變成 2
- D中的_c 變成 4
- D中的_d 變成 5
當繼承方式為虛繼承時:
class B:virtual public A
{
public:
int _b;
};
class C :virtual public A
{
public:
int _c;
};
當繼承方式為虛繼承時,程式的執行過程為:
- B中的_a 和 C中的 _a 同時變為1
- D中的_b 變成 3
- B中的_a 和 C中的 _a 同時變為2
- D中的_c 變成 4
- D中的_d 變成 5
注:此時A 叫做虛基類,當子類對父類的繼承方式為虛繼承時,父類就叫做虛基類。
虛基類的作用:當一個基類被聲明為虛基類後,即便它被多次繼承,最後的派生類中也隻有它的一個備份
參照上述代碼,A被聲明為虛基類,D 既被B繼承,又被 C繼承 。但最後的派生類D中隻有一個_a
——如此便解決了資料備援和二義性的問題