class B { public: void mf(); ... }; class D: public B { ... };
甚至对B,D或mf一无所知,也可以定义一个类型D的对象x,
D x; // x是类型D的一个对象
那么,如果发现这么做:
B *pB = &x; // 得到x的指针
pB->mf(); // 通过指针调用mf
和下面这么做的执行行为不一样:
D *pD = &x; // 得到x的指针
pD->mf(); // 通过指针调用mf
你一定就会感到很惊奇。
如果mf是非虚函数而D又定义了自己的mf版本,行为就不会相同:
class D: public B { public: void mf(); // 隐藏了B::mf; 参见条款50 ... }; pB->mf(); // 调用B::mf pD->mf(); // 调用D::mf
名字查找与继承:(函数调用步骤)
假设调用p->mem()
1.首先确定p的静态类型
2.在p的静态类型对应的类中查找,如果找不到,则依次在直接基类中不断查找直至到达继承链的顶端,如果找遍了该类及其基类仍然找不到,编译器将报错
3.一旦找到mem,就进行常规的类型检查,以确认对于当前找到的mem,本次调用是否合法
4.假设调用合法,则编译器根据调用的是否是虚函数而产生不同的代码:
如果mem是虚函数且我们是通过引用或指针进行的调用,则编译器产生的代码将在运行时确定到底运行该虚函数的哪个版本,依据是对象的动态类型;
反之,则编译器将产生一个常规函数调用,即静态绑定