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 声明或者转调函数。
posted @ 2020-02-25 20:07  刘-皇叔  阅读(88)  评论(0编辑  收藏  举报