組合,繼承,多型,模板,覆蓋,隱藏
一.組合
1.定義:
組合就是一個類的物件具備了某一個屬性,該屬性的值是指向另一個類的物件
2.用處:
解決類與類之間程式碼冗餘的問題
二.繼承
1.定義
用一個數據型別來定義一個新的資料型別,定義的新型別(派生類或子類)既有原來資料(基類或父類)中的成員,也能新增自己的成員。
2.分類
1.單繼承
定義的格式
class 派生類名:繼承方式 基類名
{
派生類成員
};
2.多繼承
定義的格式
class 派生類名:繼承方式 基類名,繼承方式 基類名
{
派生類成員;
};
在多繼承中靠近派生類的基類屬於先宣告的,資料在派生類中先儲存。
3.菱形繼承
當一個子程序繼承了多個父類的時候,多個父類最終繼承了同一個類,這種繼承稱之為菱形繼承
4.虛繼承
class Furniture{……};
class Bed : virtual public Furniture{……}; // 使用虛繼承
class Sofa : virtual public Furniture{……};// 使用虛繼承
class sleepSofa : public Bed, public Sofa {……};
//Furniture類只需要構造一次
3.三種繼承方式
public 公有繼承
private 私有繼承
protected 保護繼承
訪問方式 | 類裡面 | 類外面 |
---|---|---|
public | 允許訪問 | 允許訪問 |
protected | 允許訪問 | 不允許訪問 |
private | 允許訪問 | 不允許訪問 |
繼承方式 | 基類的public成員 | 基類的protected成員 | 基類的private成員 | 繼承引起的訪問控制關係變化概括 |
---|---|---|---|---|
public繼承 | 仍為public成員 | 仍為protected成員 | 不可見 | 基類的非私有長遠在子類的訪問屬性不變 |
protected繼承 | 變為protected成員 | 變為protected成員 | 不可見 | 基類的非私有成員都為子類的保護成員 |
private繼承 | 變為private成員 | 變為private成員 | 不可見 | 基類中的非私有成員都稱為子類的私有成員 |
class預設私有繼承,struct預設公有繼承
4.友元函式的繼承
1.友元函式和友元類
友元函式:可以訪問指定類的私有和受保護的自定義成員,如果不是被指定的成員,則不能被訪問。
友元類:友元關係是單向的,也不能被傳遞。
2.注意事項
1.友元函式可以訪問類的私有成員,但不是類的成員函式
2.友元函式不能用const修飾
3.友元函式可以在類定義的任何地方宣告,不受類訪問限定符的限定
4.一個函式可以是多個類的友元函式
5.友元關係不能繼承
5.靜態成員的繼承
靜態成員可以繼承,一個繼承體系中static成員只有一個,無論有多少個派生類,都僅僅有一個static成員
三.多型
1.定義
指同一個事物的多種狀態
2.用處
多型性:繼承同一個類的多個子類中有相同的方法名,這時子類產生的物件就可以不用考慮具體的型別而直接呼叫該功能
3.靜態多型性
1.函式過載和預設引數
1.過載的函式必須有相同的函式名
2.過載的函式不可以擁有相同的引數
只有在 同一類定義中的同名成員函式才存在過載關係 ,主要特點是 函式的引數型別和數目有所不同 ,但 不能出現函式引數的個數和型別均相同 ,僅僅依靠返回值型別不同來區分的函式,這和普通函式的過載是完全一致的。另外,過載和成員函式是否是虛擬函式無關
3.預設引數只有解除安裝函式宣告中即可
4.預設引數應該儘量靠近函式引數列表的最右邊,以防二義性。
比如
double sum (float nNum2 = 10,float nNum1);
這樣的函式宣告,我們呼叫時:sum(15);程式就有可能無法匹配正確的函式而出現編譯錯誤。
2.運算子過載
用法
返回值 operator 運算子(引數列表)
{
//code
}
3.巨集多型
帶變數的巨集可以實現一種初級形式的靜態多型
4.動態多型性
1.虛表和虛擬函式
#include<iostream>
using namespace std;
class animal
{
public:
void sleep(){
cout<<"animal sleep"<<endl;
}
virtual void breathe(){
cout<<"animal breathe"<<endl;
}
};
class fish:public animal
{
public:
void breathe(){
cout<<"fish bubble"<<endl;
}
};
int main()
{
fish fh;
animal *pAnimal=&fh;
pAnimal->breathe();
}
編譯器為每個類的物件提供一個虛表指標,這個指標指向物件所屬類的虛表。
虛表指標在建構函式中進行虛表的建立和虛標指標的初始化。
對於虛擬函式呼叫來說,每一個物件內部都有一個虛表指標,該虛表指標被初始化為本類的虛表。所以在程式中,不管你的物件型別如何轉換,但該物件內部的虛表指標是固定的,所以呢,才能實現動態的物件函式呼叫,這就是C++多型性實現的原理。
總結(基類有虛擬函式):
1、每一個類都有虛表。
2、虛表可以繼承。如果基類3個虛擬函式,那麼基類的虛表中就有三項(虛擬函式地址),派生類也會有虛表,至少有三項,如果重寫了相應的虛擬函式,那麼虛表中的地址就會改變,指向自身的虛擬函式實現。如果派生類有自己的虛擬函式,那麼虛表中就會新增該項。
3、派生類的虛表中虛擬函式地址的排列順序和基類的虛表中虛擬函式地址排列順序相同。
2.虛擬函式和純虛擬函式
1.引入原因
虛擬函式:為了方便使用多型,在基類中定義虛擬函式
純虛擬函式:為了實現多型性,自己不實現過程,讓繼承他的子類實現(抽象類即包含純虛擬函式的類)
純虛擬函式在基類只定義函式體,沒有實現過程
virtual void Eat()=0;直接=0
2.區別
(1)虛擬函式中的函式是實現的,哪怕是空實現,它的作用是這個函式在子類裡面可以被過載,執行時動態繫結實現動態
純虛擬函式是個介面,是個函式宣告,在基類中不實現,要等到子類中去實現
(2) 虛擬函式在子類裡可以不過載,但是虛擬函式必須在子類裡去實現。
四.模板
1.定義
template<typename T>.//在模板定義語法中關鍵字class與typename的作用完全一樣
函式模板必須由編譯器根據程式設計師的呼叫型別例項化為可執行的函式
2.類模板和模板類
1.類模板
一個類模板(類生成類)允許使用者為類定義個一種模式,使得類中的某些資料成員、預設成員函式的引數,某些成員函式的返回值,能夠取任意型別(包括系統預定義的和使用者自定義的)。
一個類中的資料成員的資料型別不能確定,或者是某個成員函式的引數或返回值的型別不能確定,就必須將此類宣告為模板,它的存在不是代表一個具體的、實際的類,而是代表一類類。
類模板的使用即為將一個類模板例項化為一個具體的類,格式:類名<實際的型別>
一個類定義中,只要有一個函式模板,則這個類是類模板;類模板的成員函式不全是是函式模板,當類模板例項化後,成員函式也隨之例項化。
2.模板類
類模板例項化後的產物
五.覆蓋
1.在派生類中覆蓋基類中的同名函式,要求兩個函式的引數個數、引數型別、返回型別都相同。
2.基類函式必須是虛擬函式
六.隱藏
1.定義
派生類中的函式遮蔽了基類中的同名函式
2.注意
1.2個函式名稱相同,引數相同,但基類函式不是虛擬函式,父類函式被隱藏(和覆蓋的區別在於基類函式是否是虛擬函式)。
2.2個函式名稱相同,引數不同,無論基類函式是否是虛擬函式,基類函式都會被隱藏(和過載的區別在於兩個函式不在同一類中)。
七.例題
1.有這樣一個類: class Eye { public: void Look(void); }; 現在希望定義一個Head類,也想實現Look的功能,應該使用( )的方法,實現程式碼重用。
A 繼承 B 組合 C 模板 D 多型
正確答案:B
2. 下面有關繼承、多型、組合的描述,說法錯誤的是?
A. 封裝,把客觀事物封裝成抽象的類,並且類可以把自己的資料和方法只讓可信的類或者物件操作,對不可信的進行資訊隱藏
B. 繼承可以使用現有類的所有功能,並在無需重新編寫原來的類的情況下對這些功能進行擴充套件
C. 隱藏是指派生類中的函式把基類中相同名字的函式遮蔽掉了
D. 覆蓋是指不同的函式使用相同的函式名,但是函式的引數個數或型別不同
正確答案: B D
3..下列關於模板的說法正確的是 ( )
A 模板的實參在任何時候都可以省略
B 類模板與模板類所指的是同一概念
C 類模板的引數必須是虛擬型別的
D 類模板中的成員函式不全是模板函式
正確答案: D