Item 33:避免隐藏继承来的名称
隐藏名称是作用域的问题。 在 C++ 中每一对{}
都会开启一个新的作用域,并嵌套在当前作用域中。
int x;
void func(){
double x;
cin>>x; // read a new value for local x
}
可以看到 double x 隐藏了 int x,因为C++的名称隐藏规则隐藏的是名称,和类型无关!
继承作用域
子类可以访问父类中的名称,是因为子类的作用域是嵌套在父类的作用域中的。 这一点也很符合直觉:
class Base{
public:
void func_base();
};
class Derived{
public:
void func_derived(){
func_base();
}
};
在 func_derived() 中调用 func_base() 时:
- 编译器首先检查当前作用域内是否有名称 func_base(当然C++是不允许在函数里定义函数的)
- 如果没有找到,然后去父作用域 Derived 中寻找名称 func_base
- 如果仍然未找到,然后去再上一级作用域 Base 中寻找 func_base,找到了!然后调用 Base::func_base()。
- 如果还没找到,编译器还会去 Derived 所在命名空间下、全局作用域下寻找。
隐藏父类的名称
子类中重写(override)与父类方法同名的方法,将会隐藏父类中所有同名的重载方法。
class Base{
public:
virtual void func()=0;
void func(int);
};
class Derived: public Base{
public:
virtual void func();
};
...
Derived d;
d.func(1); // Error!
Derived 中声明的 func 方法,隐藏了父类中所有的 func 名称,包括所有的重载函数。
继承所有重载方法
当你从父类继承来了一系列的重载方法,而只想重写其中的一个时,可以用 using,否则其他重载方法会被隐藏。
class Derived: public Base{
public:
using Base::func;
virtual void func();
};
...
d.func(1); // OK
继承一个重载方法
在 public 继承中,子类和父类是 "is-a" 的关系,所以通常我们希望从父类继承所有的方法。 但如果是 private 继承, 可能你只想要其中的一个,这时可以定义一个转发函数:
public:
virtual void mf1() = 0;
virtual void mf1(int);
};
class Derived : private Base {
public:
virtual void mf1() {
Base::mf1(1); // 这是一个inline函数
}
};
总结
- derived classes 中的名字覆盖 base classes 中的名字,在 public 继承中,这从来不是想要的。
- 为了使隐藏的名字重新可见,使用 using 声明或者转调函数。