继承中类成员的名字查找
要理解继承中成员函数的调用过程首先我们必须先了解一些概念
- 静态类型 : 静态类型是指在编译是已知的的,是变量声明时的类型或表达式生成的类型
- 动态类型 : 动态类型则是变量或表达式表示的内存中的对象的类型,动态类型直到运行时才可知
- 继承中的类作用域 : 每个类定义自己的作用域,派生类的作用域嵌套在其基类的作用域之中
接下来正式介绍其中的函数调用中的名字查找
假定我们调用p类的成员函数mem(), 分为以下4步:
- 首先确定p的静态类型
- 在其静态类型中查找mem,如果过找不到,则依次在其直接基类中查找直至继承链的顶端,如果找不到则是编译错误
- 一旦找到了,进行常规的类型检查(检查参数的个数,类型等)用于确认调用合法
- 假如调用合法,在根据调用的是否是虚函数产生不同的代码
- 如果mem()是虚函数,而且是通过指针或引用调用的,则根据动态类型在运行时决定调用哪个版本的虚函数
- 如果不是虚函数,或者是由对象直接调用,则直接就是常规的函数调用
值得一提的是第二步与第三步之间体现出的思想——-名字查找先于类型检查,这很好的解释了子类对其基类函数的隐藏
|
|
继承类型
在继承体系分为3种函数:
- non virtual 非虚函数
- virtual 虚函数
- pure virtual 纯虚函数
这三种函数对应不同的继承类型,继承接口,继承实现
其中非虚函数要求其子类继承接口和强制继承其实现(要求不要重写,以公有继承为例,重写违背了is-a 的要求)
虚函数要求其子类继承接口和一份默认实现(子类可以选择自由选择是否重写)
纯虚函数要求函数继承接口(要求子类种一定要有该函数和一份自己的实现)
|
|
上面的例子fly()是一个虚函数,ModelB中是并没由写fly()函数,继承了一份默认的实现,但是该代码可以顺利通过编译,可以调用基类中的fly(), 这是不合逻辑的。我们可以通过将基类fly函数定义为纯虚函数来在编译的时候就发现该错误。
|
|
分析一下两份代码的成员函数调用:
都是查找到了基类中的fly函数,由于第二份声明为纯虚函数在尝试调用是发生了编译错误.