Effective C++——Item33: 避免隐藏继承的名字

发布时间 2023-10-07 22:15:02作者: 偷不走的影子

Effective C++——Item33: 避免隐藏继承的名字

一、从原理理解隐藏

  1. 从变量作用域看隐藏

全局变量x和局部变量x的类型是不同的,但C++的隐藏规则:只隐藏名字(hiding names)。

int x;                        // global variable
void someFunc()
{
  double x;                   // local variable
  std::cin >> x;              // read a new value for local x
}
  1. 对于继承而言,隐藏的原理即为:派生类作用域内嵌在基类作用域中。

  2. 这是为了强调我们正在谈论名称。该示例还可以包含类型名称,例如枚举、嵌套类和 typedef。在这次讨论中唯一重要的是它们的名字。

  3. 从例子理解名字(names)的查找过程

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() {
    //..
    mf2();
    //..
  }
};

在mf4()中查找mf2()的过程:

  1. 局部作用域mf4()函数
  2. 包含作用域Derived类
  3. 下一个包含作用域Base类
  4. Base类的namespace中查找

二、从实例理解隐藏

  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() {
    Derived d;
    d.mf1(); // call Derived::mf1
    d.mf1(1); // error, Derived::mf1 hides Base::mf1, can't find Base::mf1(int)
    d.mf2(); // ok, call Base::mf2
    d.mf3(); //ok, hides Base::mf3, call Derived::mf3(),
    d.mf3(1.0); // error, Derived::mf3() hides Base::mf3, can't find Base::mf3(double)
    return 0;
}
  1. 如何使被隐藏的函数可见
  • using声明——使得所有某个名字的事物都可见

    using Base::mf1;
    using Base::mf3;

使得Base类中所有名为mf1、mf3的事物都在Derived类中可见。

#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:
  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() {
    Derived d;
    d.mf1(); // ok
    d.mf1(1); // ok, call Base::mf1(int)
    d.mf2(); // ok, call Base::mf2
    d.mf3(); //ok, call Derived::mf3(),
    d.mf3(1.0); // ok, Base::mf3(double)
    return 0;
}
  • 内联转发函数——不希望全部可见
class Base {
public:
  virtual void mf1() = 0;
  virtual void mf1(int);
};

class Derived: private Base {
public:
  virtual void mf1()                   // forwarding function; implicitly inline
  { 
    Base::mf1(); 
  }                    
};
...
Derived d;
int x;
d.mf1();                               // fine, calls Derived::mf1
d.mf1(x);                              // error! Base::mf1() is hidde

三、重载、重写、隐藏的区别

  1. 函数重载发生在相同作用域

重载时机:参数数量、类型、返回值、是否const函数

  1. 函数隐藏发生在不同作用域

隐藏时机:隐藏上一级同名变量、函数(不管参数类型、返回类型)

  1. 函数覆盖就是函数重写。准确地叫做虚函数覆盖和虚函数重写,也是函数隐藏的特例。