函数调用过程
即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
。