《Effective C++》的第六章節繼承與面向對象設計花了大部分的篇幅在介紹繼承遮掩(Hiding Inherited Name),那我也效仿下大師,做個小的總結。
public繼承的目的是要建立父子類的is-a關系,也就是說用到父類的地方,在子類一定能用。現實的代碼編寫中,我們主要也是用public繼承,所以每個人都有自己一套繼承的寫法和調用,直到看到Effecitve C++時,才會發現還有很多其它的用法,在這里我并不鼓勵大家嘗試各種寫法,畢竟代碼要穩定,我只是想把一些可能的形式表現出來,供大家參考。
class Base
{
public:
virtual void fn() = 0;
virtual void fn(int i){printf("\n Base: fn(int)\n");};
virtual void fn2() {printf("\n Base: void fn2(int)\n");}
void fn3() {printf("\n Base: fn3()\n");}
void fn4(){printf("\n Base: fn4()\n");}
};
class ClassA : public Base
{
public:
ClassA(int n, int d);
// using Base::fn2;
virtual void fn(){printf("\n ClassA: fn()\n");};
virtual void fn(int i){printf("\n ClassA: fn(INT)\n");};
virtual void fn2(int i) {printf("\n ClassA: fn2(INT)\n"); }
void fn3() {printf("\n ClassA: fn3()\n");}
void fn4(int i){printf("\n ClassA: fn4()\n");}
};
int main()
{
Base* pBase1 = new ClassA(10, 20);
pBase1->fn(); //OK,ClassA: fn()
pBase1->fn(11); //OK,ClassA: fn(INT)
pBase1->fn2(); //OK,Base: void fn2(int)
pBase1->fn2(2); //NO, 不能訪問
pBase1->fn3(); //OK,Base: fn3()
pBase1->fn4(); //OK,Base: fn4()
pBase1->fn4(2); //NO,不能訪問
printf("\n============================================\n");
ClassA* pDerived = new ClassA(10, 20);
pDerived->fn(); //OK,ClassA: fn()
pDerived->fn(1); //OK,ClassA: fn(INT)
pDerived->fn2(); //NO,不能訪問
pDerived->fn2(2); //OK,ClassA: fn2(INT)
pDerived->fn3(); //OK,ClassA: fn3()
pDerived->fn4(); //NO,不能訪問
pDerived->fn4(2); //NO,ClassA: fn4(INT)
printf("\n============================================\n");
return 0;
}
子類父類同名virtual函數(參數相同), 用子類的指針,引用,對象訪問時,子類會覆蓋父類方法(只能訪問子類方法)。
子類父類同名virtual函數(參數相同), 用父類的指針,引用,對象訪問時,子類會覆蓋父類方法(只能訪問子類方法)。
子類父類同名virtual函數(參數不同), 用子類的指針,引用,對象訪問時,子類會覆蓋父類方法(只能訪問子類方法)。
子類父類同名virtual函數(參數不同), 用父類的指針,引用,對象訪問時,父類會覆蓋子類方法(只能訪問父類方法)。
子類父類同名virtual函數(函數類型不同const/non-const), 用子類的指針,引用,對象訪問時,子類會覆蓋父類方法(只能訪問子類方法)。
子類父類同名virtual函數(函數類型不同const/non-const), 用父類的指針,引用,對象訪問時,父類會覆蓋子類方法(只能訪問父類方法)。
結論:參數和函數類型是c++編譯器判斷要不要多態的關鍵因素。注: 返回類型不同時,編譯器會報錯,virtual不能和static連用。靜態成員函數,沒有隱藏的this指針,virtual函數一定要通過對象來調用,既要this指針。
改進::如果子類指針想訪問到父類,可以在子類里加入:using 父類名::函數名;如pDerived->fn2(); 訪問父類方法,在ClassA里面加入using Base::fn2,就可以訪問了。如果父類指針想訪問到子類,就需要指針轉換了。
子類父類同名non-virtual函數(無論參數/返回/函數類型(const或static)),用子類的指針,引用,對象訪問,子類會覆蓋父類方法(只能訪問子類方法)。
子類父類同名non-virtual函數(無論參數/返回/函數類型(const或static)),用父類的指針,引用,對象訪問,父類會覆蓋子類方法(只能訪問父類方法)。
結論: non-virtual函數,既沒有任何多態效果,如果父類要訪問子類,只用指針轉換。
所謂大道至簡,想必大家看著這個都煩,我也是。想了想應該這樣表達最簡單:
子類public繼承父類的函數,唯有滿足(參數,返回值,函數類型相同&父類是virtual)函數,父類的指針,引用(也指針實現的)能夠多態的訪問子類,否則父類指針只能訪問父類的方法。
子類public繼承父類的函數,子類的方法名會遮掩父類的相同名的方法。子類要想訪問父類的方法,使用using 父類名::函數名。
具體的原因我覺得可能還是得找時間拜讀下候杰譯的《C++對象模型》,看看到底這個東西是怎么設計的。