函數調用過程
即c++ primer 第五版中P549所談,這裏強調幾點。
假定是p->mem()
或者obj.mem()
- 首先確定靜態類型,即
p
的類型。 - 然後在這個靜態類型中名字查找函數,即查找有沒有
mem
。即名字查找
。 - 若沒有則去靜態類型(即p的類型)的直接基類中找,仍沒有繼續向基類中找。直到基類頂端。仍沒有的話,就返回查找失敗,發生錯誤。
- 若找到了,接着類型檢查,如果類型匹配就看這個函數是否是虛函數。
- 若類型不匹配,則直接返回類型匹配錯誤。
因爲編譯器名字查找,只要查到了,就停止名字查找了。
- 若是虛函數,並且用指針或者引用調用,那麼調用這個虛函數的哪個版本,依據是對象的動態類型。
- 否則,就常規調用,即用靜態類型來調用。
P550疑問
主要對P550的bp2->fcn();
和bp2->f2();
有疑問。可以看如下代碼:
class Base{
friend class Pal;
public:
virtual int fcn(){ std::cout << "基類的虛函數" << std::endl; return 1; }
private:
char priv_mem;
protected:
int prot_mem;
};
class D1 :public Base{
public:
int fcn(int a){ std::cout << "D1類的有參數的fcn" << std::endl; return a; }
virtual void f2(){ std::cout << "D1類的虛函數f2()" << std::endl; };//新的虛函數
};
class D2 :public D1{
public:
int fcn(int a){ std::cout << "D2類的有參數的fcn" << std::endl; return a; };
int fcn(){ std::cout << "D2類的無參數的fcn" << std::endl; return 1; };
void f2(){ std::cout << "D2的f2()" << std::endl; };
};
int main(int argc, const char *argv[])
{
Base bobj; D1 d1obj; D2 d2obj;
Base *bp1 = &bobj, *bp2 = &d1obj, *bp3 = &d2obj;
bp1->fcn();
bp2->fcn();
bp3->fcn();
//bp2->f2(); //錯誤 Base沒有名爲f2的成員
system("pause");
return 0;
}
1.首先解釋bp2->fcn()
爲什麼調用的是基類的那個虛函數。
根據函數調用規則,先在靜態類型即Base類
中找fcn,名字查找,找到了。接着進行類型檢查,確實是無參類型,再看是否是虛函數,是的,那麼依據動態類型來調用。動態類型是D1類
,即調用D1::fcn();
。但是D1
類沒有覆蓋fcn()虛函數,是直接繼承的,故調用的仍是Base::fcn()
。
2.再看bp2->f2();
依然先名字查找,靜態類型爲Base
,沒有f2
,故報錯Base類無成員f2
。