1、概念:
繼承(inheritance)機制是面向對象程序設計使代碼可以復用的最重要的手段,它允許程序員在保持原有類特性的基礎上進行擴展,增加功能。這樣產生新的類,稱派生類。繼承呈現了面向對象程序設計的層次結構,體現了由簡單到復雜的認知過程。
一個新類從已有的類中獲得其已有的特性稱為繼承,被繼承的稱為父類(Base class)或基類,新產生的類稱為派生類或子類。
2、繼承的方式:
繼承權限規則表
繼承方式
基類的public成員
基類的protected成員
基類的private成員
繼承引起的訪問控制變化
Public
Public
Protected
不可見
基類成員在派生類中訪問權限不變
Protected
Protected
Protected
不可見
基類的非私有成員成為子類的保護成員
Private
Private
Private
不可見
基類的所有成員成為子類的私有成員
class Base
{
public:
Base()
{
_pri = 1;
_pro = 2;
_pub = 3;
}
private:
int _pri;
protected:
int _pro;
public:
int _pub;
};
class Derived1 :public Base
{
public:
Derived1()
{
_d1_pri = 4;
_d1_pro = 5;
_d1_pub = 6;
}
void showd()
{
_pub = 0; // 仍為public成員
_pro = 2; // 仍為protected成員
//_pri = 3; // 父類私有成員對子類 不可見
}
private:
int _d1_pri;
protected:
int _d1_pro;
public:
int _d1_pub;
};
void TestPublic()
{
Base b;
b._pub = 1;
//b._pro = 2; // 父類對象不可訪問父類保護成員
//b._pri = 3; // 父類對象不可訪問父類私有成員
Derived1 d1;
d1._pub = 1; // 子類對象可以訪問父類公有成員
//d1._pro = 2; //子類對象不可訪問父類保護成員
//d1._pri = 3; // 子類對象不可訪問父類私有成員
d1._d1_pub = 4;
}
// protected 保護繼承
class Derived2 :protected Base
{
public:
Derived2()
{
_d2_pri = 4;
_d2_pro = 5;
_d2_pub = 6;
}
void showd()
{
_pub = 1; // 變為子類的protected成員
_pro = 2; // 仍為protected成員
//_pri = 3; // 父類私有成員對子類不可見
}
private:
int _d2_pri;
protected:
int _d2_pro;
public:
int _d2_pub;
};
void TestProtected()
{
Base b;
b._pub = 1;
//b._pro = 2; // 父類對象不可訪問父類保護成員
//b._pri = 3; // 父類對象不可訪問父類私有成員
Derived2 d2;
//d2._pub = 1; // 父類的public成員權限被修改為protected,子類對象不可訪問
//d2._pro = 2; //子類對象不可訪問父類保護成員
//d2._pri = 3; // 子類對象不可訪問父類私有成員
d2._d2_pub = 4;
}
// private私有繼承
class Derived3 :private Base
{
public:
Derived3()
{
_d3_pri = 4;
_d3_pro = 5;
_d3_pub = 6;
}
void showd()
{
_pub = 1; // 變為子類的privite成員
_pro = 2; // 變為子類的privite成員
//_pri = 3; // 父類私有成員對子類不可見
}
private:
int _d3_pri;
protected:
int _d3_pro;
public:
int _d3_pub;
};
void TestPrivate()
{
Base b;
b._pub = 1;
//b._pro = 2; // 父類對象不可訪問父類保護成員
//b._pri = 3; // 父類對象不可訪問父類私有成員
Derived3 d3;
//d3._pub = 1; // 父類的public成員權限被修改為private,子類對象不可訪問
//d3._pro = 2; // 父類的protected成員權限被修改為private,子類對象不可訪問
//d3._pri = 3; // 子類對象不可訪問父類私有成員
d3._d3_pub = 4;
}
3、繼承又可以分為單繼承,多繼承,鑽石繼承(又叫菱形繼承),現在來談論下三種情況下派生類的布局:
(1)單繼承:
#include
using namespace std ;
class Base
{
public:
Base(int a= 0, int b= 1):_a(a),_b(b)
{
cout << "我是基類的構造函數" <
為啥子類對象賦值給父類對象可以,父類對象賦值給子類對象不可以?
原因就是子類對象賦值給父類對象時,子類對象發生對象切片,將子類對象的父類部分賦值給父類對象,但是父類對象賦值給子類對象時,沒有辦法對子類自己的成員賦值,所以父類對象賦值給子類對象不可以。
(2)多繼承
#include
using namespace std ;
class Base1
{
public:
Base1(int a= 0, int b= 1):_a(a),_b(b)
{
cout << "我是基類的構造函數" <從結果我們能分析,先調用base2的構造函數,在調用base1的構造函數,最後調用自己的構造函數,析構函數相反,這就說明先繼承誰先調用誰的構造函數,子類的成員布局如下:
(3) 菱形繼承:#include
using namespace std ;
class Base1
{
public:
Base1(int a= 0, int b= 1):_a(a),_b(b)
{
cout << "我是基類的構造函數" <
派生類Derived對象d成員布局如下:
從派生類對象成員布局可以看出,派生類對象繼承了兩份Base類成員,所以d._a訪問不明確,因此虛繼承的語法出現了。#include
using namespace std ;
class Base1
{
public:
Base1(int a= 0, int b= 1):_a(a),_b(b)
{
cout << "我是基類的構造函數" <
從結果分析我們能看出只是繼承了一份基類,但是對象的大小變成了28,說明派生類中有8個字節是系統給我們自動加上的。
從圖中我們可以看出確實只是繼承了一份繼承,但是基類的布局和原來的布局不一樣,現在基類在最低下,Derived1中插入一個指針,指向一個表,表中存放了Deriverd1相對於基類的偏移量,Derived2也存放一個指針,同樣指向一個表,這個表中同樣存放了Derived2相對於基類的偏移量。從圖中我們看出虛繼承內存不一樣的機制解決了變量的二義性問題。4.繼承綜合案例:#include
using namespace std ;
namespace
{
class Base
{
public:
Base(int a):_a(a)
{
cout << "我是基類的構造函數" <
5.繼承注意點:(1)父類的構造、析構、拷貝構造、重載賦值運算符,友元函數不能繼承。(2)基類沒有缺省構造函數,派生類必須要在初始化列表中顯式給出基類名和參數列表。
(3)基類沒有定義構造函數,則派生類也可以不用定義,全部使用缺省構造函數。
(4)、基類定義了帶有形參表構造函數,派生類就一定定義構造函數 (無參或者帶參)
(5)繼承時,先調用父類的構造函數,再調用組合對象的構造函數,最後調用自己的構造函數,析構時相反。
(6) public繼承是is-a關系,private繼承和組合對象是has-a關系。