33 Avoid hiding inherited names

int x;  // global variable

void someFunc()
{
  double x;  // local variable
  std::cin >> x; // read a new value for local x
}

The statement reading into x refers to the local variable x instead of the global variable x, because names in inner scopes hide names in outer scopes.

Enter inheritance. We know that we are inside a derived class member function and we refer to something in a base class, compilers can find what we are referring to because derived classes inherit the things declared in the base classes. The way that actually works is that the scope of a derived class is nested inside its base class's scope :

class Base{
private:
  int x;

public:
  virtual void mf1() = 0;
  virtual void mf2(){cout << "this is base" << endl;}
  void mf3();

...
};
class Derived : public Base{
public:
  virtual void mf1();
  void mf4();
}

Suppose mf4 in the derived class is implemented, in part, like this :

void Derived::mf4()
{
...
mf2();
...
}

When the compilers see the use of the name mf2 here, they have to figure out what it refers to. They do that by searching scopes for a declaration of something named mf2. First they look in the local scope, but they find no declaration for anything called mf2. They then search the containing scope, that of the class Derived. They still find nothing named mf2, so they move on to the next named mf2, so the search tops. If there were no mf2 in Base, the search would continue, first to the namespace containing Base, if any, and finally to the global scope.

int main()
{
    Derived d;
    d.mf1();
    d.mf4();
}
this is derived !
this is base

And is class Base don't have function mf2, we define mf2 outside the class,

void mf2()
{
    cout << "outside the class !" << endl;
}

The result would be :

this is derived !
outside the class !

Consider the previous example again, except this time let us overload mf1 and mf3 , and let us add a version of mf3 to be Derived.

class Base{
private:   
    int x;
public:
    virtual void mf1() = 0;
    virtual void mf1(int a){cout << "Base mf1 " << a << endl;}

    virtual void mf2() {cout << "Base mf2 " << endl;}
    
    void mf3() {cout << "Base mf3 " << endl;}
    void mf3(double a){cout << "Base mf3 " << a << endl;}
};
class Derived : public Base{
public:
  virtual void mf1();
  void mf3();
  void mf4();
};

The scoped-based name hiding rule hasn't changed, all functions named mf1 and mf3 in the base class are hidden by the functions named mf1`` andmf3in the derived class. From the perspective of name lookup,Base::mf1andBase::mf3are no longer inherited byDerived` !

int main()
{
   Derived d;
   int n = 10;

   d.mf1();  //fine,Derived mf1
   d.mf1(n);  // error
   d.mf2(); // fine, Base mf2 
   d.mf3(); // fine Derived mf3
   d.mf3(n); // error
}

We can see that Derived::mf1 hides Base::mf1 and Derived::mf3 hides Base::mf3.

The rational behind this behavior is that it prevents you from accidentally inheriting overloads from distant base classes when you create a new derived class in a library or application framework.
Unfortunately, you typically *want to * inherit the overloads. In fact, if you are using public inheritance and you don't inherit the overloads, you are violating the is-a relationship between base and derived classes.

You can do it with using declarations :

The Base class is the same.
And modify Derived a little bit :

class Derived : public Base{
public:
  using Base::mf1;
  using Base::mf2;
...
};

And it works well at this time !

    Derived d;
    int n = 10;
    d.mf1();   // Derived mf1 
    d.mf1(n);  // Base mf1 10
    d.mf2();  // Base mf2 
    d.mf3();  // Derived mf3
    d.mf3(n);  // Base mf3 10

This means that if you inherit from a base class with overloaded functions and you want to redefine or override only some of them, you need to include a using declaration for each name you would otherwise be hiding.

We can also use inline forwarding functions to import inherited names into the scope of a derived class.

class Derived : public Base{
public:
  virtual void mf1() { cout << "Derived mf1 " << endl; Base::mf1();}
    virtual void mf1(int n) { cout << "Derived mf1 " << endl; Base::mf1(n);}
};

Conclusion

  • Names in derived classes hide names in base classes. Under public inheritance , that is never desirable.
  • To make hidden names visible again, employ using declarations or forwarding functions .
最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容