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

本文详细解析C++ Primer第五版中关于类中函数调用的过程,特别是针对P550页的疑问。首先,确定对象的静态类型,然后在该类型中查找函数。如果找不到,将搜索基类,直至找到或到达基类顶端。对于虚函数,调用版本由对象的动态类型决定。通过示例代码解释为何在特定情况下会调用基类的虚函数版本,以及未覆盖虚函数时的行为。
1万+

被折叠的 条评论
为什么被折叠?



