1.繼承會將所有的成員繼承下來,但是繼承方式限定的是繼承下來成員的可見類型(如果是private繼承,那么他不論哪里都是不可見的;如果是protected繼承在類中是可見的,在類外是不可見的;如果是public繼承,在任何地方都是可見的)
2.對于靜態成員變量,不計入字節大小
3.虛函數類外定義時,不必加virtual
4.編譯時多態——靜態多態——模板和重載
運行時多態——動態多態——虛函數
友元函數 構造函數 static靜態函數 不能用virtual關鍵字修飾;
普通成員函數 和析構函數 可以用virtual關鍵字修飾;
靜態成員變量類內聲明類外初始化
靜態成員變量為什么不能設置為虛函數:
靜態成員函數不屬于任何成員,屬于整個類,不能使用this來訪問
virtual構成的虛函數,恰恰是使用this指針訪問,this->vfptr-> 虛函數地址
靜態成員函數沒有this指針,實現多態就是需要不同的對象,調用不同的子類進行訪問不同的重寫函數
class A
{static int _tem;//靜態成員變量類內聲明,類外初始化
};int A::_tem = 0;//在類外初始化的時候不加static
靜態成員變量會繼承使用權,但是不能被包含
靜態成員變量不存儲在類中,計算類的大小的時候不計算他的字節大小
重載:同一個作用域中,函數名相同,參數類型不同,參數數量不同,參數順序不同
重定義(隱藏):繼承中,函數名相同(可使用作用域進行訪問)
重寫(覆蓋):繼承的虛函數中,子類重寫父類的虛函數
派生類的構造函數,不寫會調用默認的構造函數,但是如果自己寫,就要自己調用父類的構造函數(在調用父類的構造函數時,要將父類當成一個整體)
class Base
{
public:Base(){puts("Base()");}Base(int a):_a(a){puts("Base(int a)");}Base(const Base& tem){puts("Base(const Base& tem)");}Base& operator=(const Base& tem){puts("Base& operator=(const Base& tem)");_a = tem._a;return *this;}~Base(){puts("Base");}
private:int _a;
};
class Son:public Base
{
public:Son(){puts("Son()");}Son(int a, int b):Base(a),_b(b){puts("Son(int a,int b)");}Son(const Son& tem):Base(tem){//父類接受子類的對象/引用/指針puts("Son(const Son& tem)");}Son& operator=(const Son& tem){puts("Son& operator=(const Son& tem)");Base::operator=(tem);return *this;}~Son(){//在析構函數的時候,可以不手動調用析構函數//編譯器會自動調用父類的析構函數puts("~Son()");}
private:int _b;
};
inline能不能是虛函數:可以(內斂函數沒有地址)
多態調用:內斂不起作用(多態調用中,虛函數存在虛函數表中,需要地址,但是內聯函數沒有地址,所以內斂不起作用)
普通調用:內斂起作用
構造函數能不能是虛函數?不能
虛表是在編譯時生成,構造的時候進行初始化
如果構造函數是虛函數,那么在實例化對象的時候,如何應該去虛表中找構造函數的地址,但是這時候虛表還沒有初始化
多態調用和普通函數的時間效率?
具體要看是否構成多態,調用需要到需表中找地址進行調用,普通成員函數可以直接調用
多態的本質——虛表
當父類的指針/引用接收子類的地址/對象時,因為是繼承會進行切割,將子類的父類那部分切出來,剩下的就是去虛表中找地址進行調用就行了
父類=子類;會將父類的那部分切出來拷貝給父類,但是不會拷貝虛函數表指針
虛函數指針如果進行了拷貝,那么父類對象的虛函數表指針會發生改變,當使用父類對象調用父類的虛函數時,就會發生錯誤
虛函數繼承,繼承的是接口,參數類型不會改變;當子類對象要調用父類的函數時,使用切片的手法進行調用
普通函數繼承,繼承的是實現
子類和父類都有虛函數,子類的虛函數會存到哪里
從右表中可以看到,應該是有三個虛函數,他們在同一個虛表中
子類中沒有虛表,子類的虛函數存到父類的虛表中
可以記成向上合并