在编译时进行名字查找
一个对象、引用或指针的静态类型,决定了该对象的哪些成员是可见的。即使静态类型与动态类型可能不一致(当使用基类的引用或指针时会发生),但是我们能使用能使用哪些成员仍然是由静态类型决定。
名字查找与继承
理解函数调用的解析过程对于理解C++的继承至关重要,假定我们调用p->mem()(或者obj.mem()),则依次执行以下四个步骤:
- 首先确定p的静态类型。因为我们调用的是一个成员,所以该类型必须是类类型。
- 在p的静态类型对应的类中查找mem。如果找不到,则依次在直接基类中不断查找直到继承链的顶端。如果找遍了该类及其基类,则编译器报错。
- 一旦找到了mem,就进行常规的类型检查(函数形参类型),以确认对于当前找到的mem,本次调用是否合法。
- 假设调用合法,则编译器将根据调用的是是否为虚函数而产生不同代码。
- 反之,如果mem不是虚函数或者我们是通过对象(而非引用或指针)进行的调用,则编译器产生一个常规函数调用
- 如果mem是虚函数且是通过引用或指针来调用的,则编译器产生的代码将在运行时确定到底 运行该虚函数的哪个版本,依据的是对象的动态类型。
名字查找早于类型检查
struct Base{
int memfcn();
};
struct Derived:Base{
int memfcn(int); //隐藏基类的memfcn
};
Derived d; Base b;
b.memfcn(); //正确,调用Base::memfcn
d.memfcn(10); //调用derived::memfcn
d.memfcn(); //错误:参数列表为空的memfcn被隐藏了
d.Base::memfcn(); //正确:调用Base::memfcn
即使派生类成员和基类成员的形参不一致,基类成员同样会被隐藏掉。