这里所说的如果派生类希望所有的重载版本对于它来说都是可见的,那么它就需要覆盖所有的版本,或者一个也不留是什么意思呢?
实验现象
#include <iostream>
class base {
public:
virtual int func() {return 0;}
virtual int func(int i) {return 1;}
};
class D1 : public base {
public:
int func() { return 2; }
};
class D2 : public D1 {
public:
int func(int i) { return 3;}
};
int main()
{
D1 d1;
D2 d2;
base *b1 = &d1;
base *b2 = &d2;
b1->func(1);
b2->func();
return 0;
}
基类base中有重载函数func的两个版本,D1继承自base,覆盖了func()的版本,D2继承自D1,覆盖了func(int)的版本。
- 主函数里使用动态绑定,base指针绑定D1对象,可以使用fun(int)版本,同样base指针绑定D2对象,也可以使用fun()版本。
- 如果在D1调用func(int)版本,比如
int funcc() {return func(1);}
,那么会编译报错,显示no
matching function for call to ‘D1::func(int)’。如果改成int funcc() {return func();}
就没错了。
同理,在D2中使用func()也会报错。 - 如果直接用d1调用fun(int)版本,或者直接用d2调用func()版本,编译会报错。错误也是no matching function for call to ‘XXX’
- 如果修改类D1和D2,把覆盖的虚函数去掉,如下所示:
class D1 : public base {
public:
//int func() { return 2; }
};
class D2 : public D1 {
public:
//int func(int i) { return 3;}
};
那么直接用d1调用不管哪个func版本都不会报错,同理d2也是。
原理
派生类的作用域嵌套在基类的作用域之内。 D1只覆盖了无参版本的func函数,那么D1的作用域之内,func(int)版本的函数就被隐藏了。使用b1调用func(int)函数,编译器首先在D1作用域内找名字func,找到了名字func,就不会继续往外层作用域查找,此时参数不匹配,于是编译器报错。
同样,D1中的funcc()函数调用func(int)函数,也是先从D1作用域开始查找名字func,也只能找到无参版本,找到无参版本后发现参数不匹配,于是报错。
而如果使用动态绑定,即使用指针d1访问func(),查找名字将从静态类型的作用域开始,即从base的作用域开始查找,而base的作用域内两个版本的func都可见(其中无参版本的是被覆盖后的,有int参数的是原始的),所以找到名字func后还会进行参数匹配,不会出现编译错误。
修改类D1和D2,把覆盖的虚函数去掉,那么D1和D2作用域内就没有名字func了,使用b1调用func(int)或者func()函数,都无法在D1作用域内找到函数名,于是在外层作用域中继续寻找,也就是在base作用域中查找,能找到重载的两个版本的函数,接着进行正常的函数匹配,也不会出现编译错误。
而书上所说的using声明符,相当于将base作用域里的几个重载版本的函数都强行加到了里层的D1作用域里,也能解决2和3的问题。