名字查找与继承

C++函数解析过程

理解上面得的名字查找过程,再来理解隐藏(hide)、覆盖(override)、重载(overload)就容易多了。此外还要记得:名字查找先于参数检查。

  • overload: 重载 发生在同一个域内,同一个域没有同名但是参数不同的多个函数。
  • override:覆盖 发生在多态继承的继承体系中,派生类会override基类的virutal函数。
  • hide: 隐藏 发生在不同域之间,深层次的名称会hide外层的名称

一个例子

来看一个例子。

#include <iostream>

class Base 
{
  public:
    virtual int fcn()
    {
      std::cout << "int Base::fcn()" << std::endl;
    }
};

class D1 : public Base
{

  public:
    //using Base::fcn; // 可以使用using让被隐藏的Base::fcn重见天日
    int fcn(int) // 以藏了 Base::fcn
    {
      std::cout << "int D1::fcn(int)" << std::endl;
    }
    virtual void f2()
    {
      std::cout << "void D1::f2()" << std::endl;
    }

};

class D2 : public D1
{
  public:
    int fcn(int) // 隐藏(hide)了D1::fcn
    {
      std::cout << "int D2::fcn(int)" << std::endl;
    }
    int fcn() // 覆盖(override)了Base::fcn
    {
      std::cout << "int D2::fcn()" << std::endl;
    }
    void f2() // 覆盖(override)了D1::f2
    {
      std::cout << "void D2::f2()" << std::endl;
    }
};

分析1

  Base b;
  D1 d1;
  D2 d2;

  b.fcn(); // Base::fcn
  d1.fcn(1); // int D1::fcn(int)
  d1.f2(); // void D1::f2()
  //d1.fcn(); // compile error : int D1::fcn(int) hide Base::fcn
  d1.Base::fcn(); // compile error : int D1::fcn(int) hide Base::fcn

其中D1的int fcn(int)隐藏了(hide)int Base::fcn()。我一开始有一个错误的观念,以为隐藏就是不要某个函数了,这么理解是错误的。public继承意味着“is-a”的关系(见Effective C++ rule32), 派生类会包含基类的所有东西,隐藏并不是说剔除了某个函数,而是说名字被隐藏了,注意结合上面的名字查找过程来理解。

当编译器看到d1.fcn()这句时,编译器会先确定d1的静态类型,此时为D1, 然后在D1内找名字fcn, 它找到了一个fcn:int fcn(int), 就不会在往基类那里找了, 接着进行参数匹配,此时编译器就会报错,因为参数检查没有通过。

那么如果我们想通过D1调用到Base::fcn怎么办呢? 可以使用using或者给fcn加上Base域或者使用Effective c++ rule34中提到的转交函数。

分析2

  Base* bpb, *bpd1, *bpd2;
  bpb = &b;
  bpd1 = &d1;
  bpd2 = &d2;

  bpb->fcn();
  bpd1->fcn();
  bpd2->fcn();

结果:

int Base::fcn()
int Base::fcn()
int D2::fcn()

此例是让基类指针分别指向三种对象,bpd1指向D1类的对象,解析过程是这样的:

  • bpd1的静态类型,为Base
  • 在Base内找名字fcn, 结果找到了int Base::fcn()
  • 参数检查,检测调用是否合法,结果合法
  • 发现fcn为虚函数,而且我们是通过指针调用的,所以需要确定bpd1的动态类型,发现动态类型为D1,然后又发现D1继承了Base的fcn, 所以最终调用的是Base::fcn。(不要以为hide还会发生, 因为hide只会在名字查找时发生, 而此时名字查找已经在第二步结束了)

最后

不要hide(隐藏)基类的名称!

你可能感兴趣的:(名字查找与继承)