这个题材和继承其实无关,而是和作用域有关。我们都知道在下面的代码中:
int x; // global
void someFunc() {
double x; // local
std::cin >> 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();
...
};
假设派生类内的mf4的实现部分长这样:
void Derived::mf4() {
...
mf2();
...
};
当编译器看到名称mf2时,查找各作用域,看看有没有某个名为mf2的声明式。首先查找local作用域,在那没找到任何名为mf2的东西。于是查找其外围作用域,也就是派生类覆盖的作用域。如果还是没有找到,于是再往外围移动,本例为基类。在那找到一个名为mf2的东西了,于是停止查找。
再次考虑一个例子,这次让我们重载mf1和mf3,并且添加一个新的mf3到派生类区。Derived重载了mf3,那是一个继承而来的non-virtual函数。
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();
...
};
基类内所有名为mf1和mf3的函数都被派生类内的mf1和mf3遮蔽掉了。从名字查找来看,Base::mf1和Base::mf3不再被Derived继承!
Derived d;
int x;
...
d.mf1(); // Derived::mf1
d.mf1(x); // error: no Derived::mf1(int)
d.mf2(); // Base::mf2
d.mf3(); // Derived::mf3
d.mf3(x); // error: no Derived::mf3(int)
如你所见,上述规则都适用,即使基类和派生类内的函数有不同的参数类型也适用,而且不论函数是virtual还是non-virtual一起适用。
你可以适用 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:
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(int)
d.mf2(); // Base::mf2
d.mf3(); // Derived::mf3
d.mf3(x); // Base::mf3(int)
这意味着如果你继承基类并加上重载函数,而你又希望重新定义或重写其中的一部分,那么你必须为那些原本会被遮蔽的每个名字引入一个 using
声明。
有时候你并不想要继承基类的所有函数,但在public继承下,这绝不可能发生,因为它违反了public继承下的is-a关系。然而在private继承下它却可能是有意义的。例如假设Derived以private继承Base,而Derived唯一想继承mf1的无参数版本。 using
声明在这里派不上用场,因为 using
声明会导致继承来的名称在派生类中都可见。我们需要不同的技术,即一个简单的转发函数(forwarding function):
class Base {
private:
int x;
public:
virtual void mf1() = 0;
virtual void mf1(int);
... // 与此前相同
};
class Derived : private Base {
public:
virtual void mf1() { // forwarding function
Base::mf1();
}
void mf3();
void mf4();
...
};
...
Derived d;
int x;
d.mf1(); // Derived::mf1()
d.mf1(x); // error: Base::mf1(int)
请记住
- 派生类内的名称会遮蔽基类内的名称。在public继承下从来没有人希望如此。
- 为了让被遮蔽的名称可见,可使用
using
声明或转发函数。