一、名字覆盖规则
名称的遮掩可以分成变量的遮掩与函数的遮掩两类;
查找规则:一般先查找局部作用域,然后再查找外围作用域;对于含有继承关系的类,先查找局部作用域,再查找派生类覆盖的作用域,然后再查找基类覆盖的作用域,如果都没有找到,最后查找全局作用域;
二、基类成员函数被覆盖
(1)看一段代码:
#include <iostream>
using namespace std;
class Base
{
private:
int x;
public:
virtual void mf1() = 0;
virtual void mf1(int ) { cout << "Base mf1(int )" << endl; }
virtual void mf2() { cout << " Base mf2()" << endl; }
void mf3() { cout << "Base mf3()" << endl; }
void mf3(double ) { cout << "Base mf3(double )" << endl; }
};
class Derived: public Base
{
public:
virtual void mf1() { cout << "Derived mf1()" << endl; }
void mf3() { cout << "Derived mf3()" << endl; }
void mf4() { cout << "Derived mf4()" << endl; }
};
int main(void)
{
Derived d;
int x = 0;
d.mf1(); // 调用派生类mf1()
//d.mf1(x); // 错误,同名覆盖
d.mf2(); // 调用基类mf2()
d.mf3(); // 调用派生类mf3()
//d.mf3(x); //错误,同名覆盖,派生类中没有带参数的mf3(int)形式,基类中才有,但是已经被派生类名字覆盖了
return 0;
}
这段代码中,基类所有名为mf1和mf3的函数都会被派生类的mf1和mf3函数遮掩掉,即使基类中有带参数形式的mf1和mf3也会被屏蔽掉,而且不论是否为虚函数都适用
从上面分析可知,基类中的mf1和mf3重载函数版本形式被屏蔽了,条款32说明,public继承意味着基类和派生类之间的关系是Is-has的关系,即适用于基类的,派生类总适用,那么这里的代码岂不违反了is-has的关系?
因此,需要解决对继承而来的名称造成名字遮掩的行为。
(2)解决方式
class Base
{
private:
int x;
public:
virtual void mf1() = 0;
virtual void mf1(int ) { cout << "Base mf1(int)" << endl; }
virtual void mf2() { cout << " Base mf2()" << endl; }
void mf3() { cout << "Base mf3()" << endl; }
void mf3(double ) { cout << "Base mf3(double)" << endl; }
};
class Derived: public Base
{
public:
using Base::mf1;
using Base::mf3;
virtual void mf1() { cout << "Derived mf1()" << endl; }
void mf3() { cout << "Derived mf3()" << endl; }
void mf4() { cout << "Derived mf4()" << endl; }
};
int main(void)
{
Derived d;
int x = 0;
d.mf1(); // 调用派生类mf1()
d.mf1(x); // 正确,调用基类mf1(int)
d.mf2(); // 调用基类mf2()
d.mf3(); // 调用派生类mf3()
d.mf3(x); //正确,调用基类mf3(doubel)
return 0;
}
这意味着想继承基类以及加上其重载形式,又希望重新定义或者覆写其中一部分,那么你必须为那些原本会被遮掩的每个名称引入一个using声明式
(3)如果你并不想继承Base class的所有函数,在public绝对不可能发生,因为违反了Is-has的关系,那么可以选择采用private。假设Derived 以private形式继承Base,而Derived唯一想要继承的mf1是那个无需参数版本。using 声明式在此派不上用场,因为using声明式会令继承而来的某给定名称之所有同名函数在Derived class中可见。所以我们得采用一个简单的转交函数(forwarding function):
class Base
{
private:
int x;
public:
virtual void mf1() = 0;
virtual void mf1(int ) { cout << "Base mf1(int)" << endl; }
virtual void mf2() { cout << " Base mf2()" << endl; }
void mf3() { cout << "Base mf3()" << endl; }
void mf3(double ) { cout << "Base mf3(double)" << endl; }
};
void Base::mf1(){}
class Derived: private Base
{
public:
virtual void mf1() {
Base::mf1();
}
void mf3() { cout << "Derived mf3()" << endl; }
void mf4() { cout << "Derived mf4()" << endl; }
};
int main(void)
{
Derived d;
int x = 0;
d.mf1(); // 调用派生类mf1(),实际调用基类的mf1()
d.mf3(); // 调用派生类mf3()
return 0;
}
三、总结
1.派生类内的名称会遮掩基类的名称。在public继承下从来没有人希望如此。
2. 为了让被遮掩的名称再见天日,可使用using声明式或转交函数(forwarding functions)。
注:本内容来自《Effective C++ 3rd(侯捷译)》,仅作学习,无任何商业用途