面向對象:封裝、繼承、多態

概念太多,總結一下,備忘。

1.三大特性

封裝

隱藏對象的屬性和實現細節,僅對外提供公共訪問方式。

繼承

代碼重用,子類繼承父類的函數

構造函數的覆蓋:
(1)父類沒有構造函數或只有無參構造函數。
子類無需顯示調用父類的構造函數,系統自動在調用子類構造函數前調用父類的構造函數。
(2)父類只有有參構造函數
子類必須顯示調用父類構造函數
(3)父類有無參和有參的構造函數
子類不顯示調用父類構造函數時,系統默認調用父類無參構造函數。

拷貝構造函數:用基於同一類的一個對象構造和初始化另一個對象
CExample(const CExample& C)
調用時機:
(1)一個對象以值傳遞的方式傳入函數體
(2)一個對象以值傳遞的方式從函數返回
(3)一個對象需要通過另一個對象進行初始化

Class A{
    A (const A& a){//拷貝構造函數
        this.x = a.x;
    }
};
void fun1(A a){//(1)對象以值傳遞的方式傳入函數體
    cout<<a.x<<endl;
}
A fun2(int x){//(2)對象以值傳遞的方式從函數返回
    A a(x);
    return a;
}
A a;
A b(a);//(3)對象需要通過另一個對象進行初始化
A b=a;//(3)對象需要通過另一個對象進行初始化

多態

多態分爲:編譯時多態運行時多態。區別在於函數地址是編譯時綁定還是運行時綁定。

編譯時多態:
通過函數重載實現。

運行時多態:
通過虛函數實現。
核心理念就是通過基類訪問派生類定義的函數。
將子類類型的指針賦值給父類類型的指針,只能訪問子父類共有的方法,不能訪問子類特有的方法。
而且只有虛函數是調用子類的(虛函數表),其他函數依舊調用父類的(地址偏移量固定)

2.虛函數vs純虛函數

虛函數

主要作用是“運行時多態”。virtual void fun()
使用virtual關鍵字的父類函數。子類可以重寫父類的虛函數來實現子類的特殊化。

純虛函數

在基類中聲明的虛函數,沒有定義,virtual void fun()=0
聲明瞭純虛函數的類是一個抽象類,不能實例化,只能作爲基類。
子類中重新聲明函數,不要後面的=0

3.重載(overload)、覆蓋(override)、隱藏

重載:“讓類以統一的方式處理不同類型數據的一種手段”

同一個類中
函數名相同
參數列表不同

覆蓋:“子類在繼承父類時,重寫(重新實現)父類中的方法”

不同類中
父類中的方法有virtual關鍵字,並且非私有
函數名相同
參數列表相同
返回類型相同
作用域只能擴大(public>protected>private)

隱藏:“子類的函數屏蔽了同名的父類函數。注意只要同名函數,不管參數列表是否相同,基類函數都會被隱藏。”

不同類中
函數名相同
沒有virtual修飾

4.虛函數表

虛函數是通過虛函數表實現的。虛函數表(virtual table)實現原理參考了博客C++ 虛函數表解析

首先明確:
1)只有包含虛函數的類纔會有虛函數表
2)同屬於一個類的對象實例共享虛函數表
3)虛函數表實際上是一個指針數組,存放了虛函數的地址。
4)爲了保證效率,虛函數表指針存在對象實例的最前面的位置。

總結幾種情況:

//基類
class Base {
     public:
            virtual void f() { cout << "Base::f" << endl; }
            virtual void g() { cout << "Base::g" << endl; }
            virtual void h() { cout << "Base::h" << endl; }

};

基類的虛函數表
這裏寫圖片描述

(1)一般繼承(無虛函數覆蓋)

class Derive:public Base {
     public:
            virtual void f1() { cout << "Derive::f1" << endl; }
            virtual void g1() { cout << "Derive::g1" << endl; }
            virtual void h1() { cout << "Derive::h1" << endl; }

};

派生類沒有重寫基類的任何虛函數,派生類的虛函數表爲:
這裏寫圖片描述
注意:
1)虛函數按照聲明順序放在表中
2)基類虛函數在派生類虛函數前面

(2)一般繼承(有虛函數覆蓋)

class Derive:public Base {
     public:
            void f() { cout << "Derive::f" << endl; }
            virtual void g1() { cout << "Derive::g1" << endl; }
            virtual void h1() { cout << "Derive::h1" << endl; }

};

其中派生類重寫了基類中的f()函數,派生類的虛函數表爲
這裏寫圖片描述

注意:
1)覆蓋的派生類f()函數被放到了虛表中原來基類虛函數的位置
2)沒有被覆蓋的函數不變

(3)多重繼承(無虛函數覆蓋)
繼承關係
這裏寫圖片描述

子類的虛函數表爲:
這裏寫圖片描述

注意:
1) 每個基類都有自己的虛表。
2) 派生類的成員函數被放到了第一個基類的表中。(所謂的第一個父類是按照聲明順序來判斷的)

(4)多重繼承(有虛函數覆蓋)
子類中覆蓋了父類的f()函數
這裏寫圖片描述

子類的虛函數表:
這裏寫圖片描述

注意:
三個父類虛函數表中的f()的位置被替換成了子類的函數指針

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章