名字查找与继承

来源:互联网 发布:出题软件手机版 编辑:程序博客网 时间:2024/06/06 09:54

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(隐藏)基类的名称!