15.5.3 作用域與成員函數
在基類和派生類中使用同一名字的成員函數,其行為與數據成員一樣:在派生類作用域中派生類成員將屏蔽基類成員。即使函數原型不同,基類成員也會被屏蔽。
class Base
{
public:
int Get(){
return 0;
}
};
class Child:public Base
{
public:
int Get(int i){
return i;
}
};
Child c=Child();
cout<<c.Get(10)<<endl; //Get(int) hide Base::Get()
回憶一下,局部作用域中聲明的函數不會重載全局作用域中定義的函數,同樣,派生類中定義的函數也不重載基類中定義的成員。通過派生類對象調用函數時,實參必須與派生類中定義的版本相匹配,只有在派生類根本沒有定義該函數時,才考慮基類函數。
重載函數
像其他任意函數一樣,成員函數(無論虛還是非虛)也可以重載。派生類可以重定義所繼承的0個或多個版本。
如果派生類重定義了重載成員,則通過派生類型只能訪問派生類中重定義的那些成員。
class Base
{
public:
int Get(){
return 0;
}
double Get(double i){
return i;
}
};
class Child:public Base
{
public:
using Base::Get;
int Get(int i){
return i;
}
};
Child c=Child();
cout<<c.Get()<<endl; //0
cout<<c.Get(10.2)<<endl; //10.2
如果派生類想通過自身類型使用所有的重載版本,則派生類必須要麼重定義所有重載版本,要麼一個也不重定義。
派生類不用重定義所繼承的每一個基類版本,它可以為重載成員提供using聲明,一個using聲明只能指定一個名字,不能指定形參表。
15.5.4 虛函數與作用域
要獲得動態綁定,必須通過基類的引用或指針調用虛函數。
class Base
{
public:
virtual int Get(){ //must be virtual func.
return 0;
}
};
class Child:public Base
{
public:
int Get(){
return 1;
}
};
Base c1=Child();
cout<<c1.Get()<<endl; //0
Base *c2=new Child();
cout<<c2->Get()<<endl; //1
通過基類調用被屏蔽的虛函數
通過基類類型的引用或指針調用函數時,編譯器將在基類中查找該函數而忽略派生類。
確定函數調用遵循以下四個步驟:
(1)首先確定進行函數調用的對象、引用或指針的靜態類型。
(2)在該類中查找函數,如果找不到,就在直接基類中查找,如此循著類的繼承鏈往上找,直到找到該函數或者查找完最後一個類。如果不能在類或者相關基類中找到該名字,則調用是錯誤的。
(3)一旦找到了該名字,就進行常規類型檢查,查看如果給定找到的定義,該函數調用是否合法。
(4)假定函數調用合法,編譯器就生成代碼。如果函數是虛函數且通過引用或指針調用,則編譯器生成代碼以確定根據對象的動態類型運行哪個版本函數,否則,編譯器生成代碼直接調用函數。
摘自 xufei96的專欄