33避免遮掩继承而来的名称:
C++的名称遮掩规则
名称遮掩规则做的事就是:遮掩名称。
- 假如这个名称是一个变量,那么无论这两个变量的类型是否相同,都会被遮掩。
- 假如这个名称是一个函数,那么无论函数的参数有几个,无论这个同名的函数有几个重载的版本,这些函数全都会被遮掩。
派生类的成员函数查找名称的顺序:
- 先在成员函数体内查找(local作用域)。
- 找不到的情况下,在派生类内查找。
- 找不到的情况下,在派生类所继承的基类内查找。
- 找不到的情况下,在派生类所属namespace查找。
- 找不到的情况下,在global作用域查找。
- 找不到的情况下,则会报错。
具体问题:
这章讨论的是,在继承的情况下,父类子类同名函数的隐藏问题。考虑下面这段代码:
class Base {
private:
int x = 0;
public:
virtual void mf1() = 0; //纯虚函数
virtual void mf1(int x){} //虚函数
virtual void mf2(){} //虚函数
void mf3(){} //普通函数
void mf3(double x){} //普通函数
};
class Derived : public Base {
public:
void mf1() override {} //重写了纯虚函数
void mf3(){} //普通函数
void mf4(){} //普通函数
};
在上面这段代码当中,base class当中所有的mf1和mf3的函数都被derived class内的mf1以及mf3函数遮掩掉了。Base::mf1和Base::mf3不会被Derived继承:(参考下面这段调用)
Derived d;
int x(0);
...
d.mf1(); // 正确,调用Derived::mf1()
d.mf1(x); // 错误,因为Derived::mf1()遮掩了Base::mf1(int x)
d.mf2(); // 正确,调用了Base::mf2()
d.mf3(); // 正确,调用了Derived::mf3()
d.mf3(x); // 错误,Derived::mf3()遮掩了Base::mf3(double x)
如果我们想要避免这样的遮掩呢,换句话说,调用父类的方法,这时候可以使用using:
class Derived : public Base {
public:
void mf1() override {} //重写了纯虚函数
void mf3(){} //普通函数
void mf4(){} //普通函数
using Base::mf1;
using Base::mf3; // 让Base class内名为mf1和mf3的所有东西在Derived作用域内都可见
};
现在我们进行调用:
Derived d;
int x(0);
d.mf1(); // 正确,调用Derived::mf1()
d.mf1(x); // 正确,调用Base::mf1(int x)
d.mf2(); // 正确,调用了Base::mf2()
d.mf3(); // 正确,调用了Derived::mf3()
d.mf3(x); // 正确,调用Base::mf3(int x)
现在更进一步,我们知道在Base类当中有两个mf3函数,分别拥有不同的参数,如果我们想让部分被遮掩呢?(这样更加灵活),这个时候,using可能排不上用场,我们需要一个新的用法:
class Base {
public:
virtual void mf1() = 0;
virtual void mf1(int);
... // 与前面相同
};
class Derived: private Base {
public:
virtual void mf1() // 转交函数
{ Base::mf1(); }
...
Derived d;
int x(0);
d.mf1(); // 正确,调用了Derived::mf1(),同时函数内部调用了Base::mf1()
d.mf1(x); // 错误
这样的技法我们称之为转交函数(forwarding function)
请记住:
- derived class内的名称会遮掩base classes内的名称,在public继承下从来没有人会希望如此。
- 为了让被遮掩的名称重见天日,可以使用using声明式或者转交函数(forwarding functions)。
Comments NOTHING