1. derived classes 内的名称会遮掩 base classes 内的名称。在 public 继承下从来没有人希望如此。
2. 为了让被遮掩的名称再见天日,可使用 using 声明式或转交函数(forwarding functions)。
int x; // global变量
void someFunc()
{
double x; // local变量
std::cin >> x; // 读一个新值赋给local变量x
}
这个读取数据的语句指涉的是 local 变量 x,而不是 global 变量 x,因为内层作用域的名称会遮掩外围作用域的名称。
在继承中
class Base {
private:
int x;
public:
virtual void mf1() = 0;
virtual void mf2();
void mf3();
...
};
class Derived: public Base {
public:
virtual void mf1();
void mf4();
...
};
上述代码的实际运作方式是,derived class 作用域被嵌套在 base class 作用域内,如图所示:
此例内含一组混合了 public 和 private 名称,以及一组成员变量和成员函数名称。这些成员函数包括 pure virtual,、impure virtual 和 non-virtual 三种,这是为了强调我们谈的是名称,和其他无关。这个例子也可以加入各种名称类型,例如 enums、nestedclasses 和 typedefs。整个讨论中唯一重要的是这些东西的名称,至于这些东西是什么并不重要。本例使用单一继承,然而一旦了解单一继承下发生的事,很容易就可以推想 C++ 在多重继承下的行为。
假设 derived class 内的 mf4 的实现代码为:
void Derived::mf4()
{
...
mf2();
...
}
当编译器看到 mf2 时,它的做法是查找各个作用域,看有没有某个名为 mf2 的声明式。首先查找 local 作用域(也就是 mf4 覆盖的作用域)。若没有找到再查找外围作用域,也就是 class Derived 覆盖的作用域。若还是没找到则再往外围移动,也就是 base class,在这里找到了一个名为 mf2 的东西就停止查找。如果在 Base 内还是没有 mf2,查找动作便继续下去,首先查找 Base 所在的那个 namesapce,最后往 global 作用域去找。
在知道了什么是名称遮掩规则后再来看下面这段代码:
class Base {
private:
int x;
public:
virtual void mf1() = 0;
virtual void mf1(int);
virtual void mf2();
void mf3();
void mf3(double);
...
};
class Derived: public Base {
public:
virtual void mf1();
void mf3();
void mf4();
...
};
由于名称遮掩规则,所以 base class 内所有名为 mf1 和 mf3 的函数都被 derived class 内的 mf1 和 mf3 函数遮掩掉了。从名称查找观点来看,Base::mf1 和 Base::mf3 不再被 Derived 继承!
Derived d;
int x;
...
d.mf1(); // 没问题,调用Derived::mf1
d.mf1(x); //错误!因为Derived::mf1遮掩了Base::mf1
d.mf2(); // 没问题,调用Base::mf2
d.mf3(); // 没问题,调用Derived::mf3
d.mf3(x); // 错误!因为Derived::mf3遮掩了Base::mf3
若你想继承重载函数,可使用 using 声明:
class Base {
private:
int x;
public:
virtual void mf1() = 0;
virtual void mf1(int);
virtual void mf2();
void mf3();
void mf3(double);
...
};
class Derived: public Base {
public:
// 让Base class内名为mf1和mf3的所有东西在Derived作用域内都可见
using Base::mf1;
using Base::mf3;
virtual void mf1();
void mf3();
void mf4();
...
};
Derived d;
int x;
...
d.mf1(); // 没问题,仍然调用Derived::mf1
d.mf1(x); //现在没问题!调用Base::mf1
d.mf2(); // 没问题,仍然调用Base::mf2
d.mf3(); // 没问题,仍然调用Derived::mf3
d.mf3(x); // 现在没问题!调用Base::mf3
有时你并不想继承 base classes 的所有函数,此时不能用 public 继承,因为“不想继承 base classes 的所有函数”违反了 public 继承的 is-a 关系,因此使用 private 继承更合适。假设 Derived 以 private 形式继承 Base,而 Derived 唯一想要继承的 mf1 是那个无参数的版本。因为 using 声明式会令继承而来的某给定名称的所有同名函数在 derived class 中都可见,所有 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;
d.mf1(); // 正确,调用的是Derived::mf1
d.mf1(x); // 错误!Base::mf1()被遮掩了
以上是有关继承和名称遮掩的所有内容!但是当继承结合 templates 时,“继承名称被遮掩”又会有新的形式,后面的条款会讨论。
评论区