原文网址:http://blog.sina.com.cn/s/blog_492d601f0100jqqm.html

再次把林锐博士的《高质量c++编程指南》翻出来看的时候,再一次的觉得这是一本难得的好书。实践派写的东西跟理论派和翻译派写的书有着本质的 区别,每次读这本书都觉得为什么自己读了这么多遍,还是会犯一些上面讲的小错误,编代码有时候莫名其妙又会把自己转糊涂了。这本书浅显易懂,而且提到了编 程过程中应该注意的很多细节,里面展开来讲的细节又偏偏是我觉得最为薄弱的环节,如果大家想学或者正在学习c++。建议大家用心的把这本并不长也不高深的 书好好的读几遍。

关于c++语言中重载、覆盖、隐藏这三个容易混淆的概念,我依然以林博士书中的例子发散开来回忆一下。先列举这三个概念必须满足的条件:
函数重载的特征:
1)处在相同的空间中(即相同的作用范围内,比如一个类中)。
2)函数名相同。
3)参数不同(相同位置参数的类型不同,或者参数的个数不同)。
4)virtual关键字可有可无。
覆盖的特征:
1)不同的范围(例如分别位于基类与派生类)。
2)函数名相同。
3)参数相同(参数个数与类型均相同)。
4)基类函数必须有virtual关键字(派生类可有可无,因为基类函数被声明为虚函数,派生类同名函数一定也是虚函数)。
隐藏的特征:
1)不同的范围(例如分别位于基类与派生类)。
2)函数名相同。
3)参数可相同也可不同(注意此处还有两种情况)。
4)virtual关键字可有可无。
注意:隐藏与覆盖的区别就在于如下两条:
1)如果派生类的函数与基类的函数同名,但是参数不同(不可能构成覆盖)。此时无论有无virual关键字,基类的函数将被隐藏。(不可能构成重载,因为重载必须在同一个类中)
2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virual关键字(如果有virual关键字,则满足覆盖的条件)。此时基类的函数被隐藏。
隐藏:
下面是林博士原书中的例子:
#include <iostream.h>
class Base
{
public:
void g(float x){cout << "Base::g(float)" << x <<endl;}       //注意c++中将在类声明中定义了实现的函数自动默认为内联函数
void h(float x){cout << "Base::h(float)" << x <<endl;}
}
class Derived : public Base
{
public:
void g(int x){cout << "Derived::g(int)" << x <<endl;}

void h(float x){cout << "Derived::h(float)" << x <<endl;}
}
void main(void)
{
Derived d
Base* pb = &d;
Derived* pd = &d;
//behavior depends on type of pointer
pb->g(3.14f);     //Base::g(float) 3.14;
pd->g(3.14f);     //Derived::g(int) 3;
pb->h(3.14f);     //Base::h(float) 3.14;
pd->h(3.14f);     //Derived::h(float) 3.14;
}
参照隐藏规则,派生类的成员函数隐藏了基类的同名函数。所谓隐藏就是指派生类类型的对象、引用、指针访问基类和派生类都有的同名函数的时候,访问的是派生 类的函数,隐藏了基类同名函数。派生类既然自动继承了基类的成员,那么基类成员就可以被派生类直接访问,那么为什么访问的是派生类的成员函数呢?所以隐藏 规则实际上就是默认的c++名字解析过程。
在继承机制下,派生类的类域被嵌套在基类的类域中,派生类的名字解析过程如下:
1)首先在派生类中查找改名字。
2)如果第一步未查找到,及派生类的类域对改名字无法进行解析,则编译器在外围基类类域查找改名字的定义。
所以准确来说,当派生类和基类有同一名字的成员时,派生类成员是隐藏了对基类成员的直接访问。那么如果访问到基类同名成员呢?加上类作用域限定例如:Base::g(float)就可以访问了。
覆盖:
覆盖规则造成的调用现象,其实就是类的虚函数实现原理生成的。为了达到动态绑定(后期绑定)的目的,C++编译器通过某个表格(一般称为vtable), 在执行期"间接"调用实际上欲绑定的函数。每一个内含虚函数的类,C++编译器都会为它做出一个虚函数表,表中的每一个元素都指向一个虚函数的地址。 
 
   举个例子:
    class base{
    public:
        func();
        virtual vfunc1();
        virtual vfunc2();
        virtual vfunc3();
    private:
        int _data1;
        int _data2;
    };
    base对象实例在内存中占据的空间是这样的:
     base对象实例          vtable
--------------------------------------------------------------------------
         vptr ---------> (*vfunc1)() -----------> base::vfunc1();
        _data1           (*vfunc2)() -----------> base::vfunc2();
        _data2           (*vfunc3)() -----------> base::vfunc3();
--------------------------------------------------------------------------
 
    当派生类改写了虚函数时,虚函数表相应的被修改了:
    class derived: public base{
    public:
        vfunc2();
    };
    derived对象实例              vtable
--------------------------------------------------------------------------
         vptr  ---------> (*vfunc1)() -----------> base::vfunc1()      
        _data1;           (*vfunc2)() -----------> derived::vfunc2()     ****注意,这里变了!!!***
        _data2;           (*vfunc3)() -----------> base::vfunc3()
--------------------------------------------------------------------------
 
    所以当你写下如下程序的时候:
    void main(void)
    {
        Derived d;
        Base *pb = &d;
        pb->vfunc2(); // Derived::vfunc2(void)
    } 
    就不难理解为何pb->vfunc2()调用的是derived::vfunc2()了,因为pb实际上指向派生类derived的实例,而派生类中的虚函数表已经被修改了。
 
    总结:简单来说,隐藏规则就是C++的名字解析过程,自里向外解析,这个好理解;而覆盖规则其实就是C++虚函数表的实现原理。

【转】c++重载、覆盖、隐藏——理不清的区别相关推荐

  1. c++ 重载 覆盖 隐藏的区别和执行方式

    成员函数被重载的特征(1)相同的范围(在同一个类中): (2)函数名字相同: (3)参数不同: (4)virtual 关键字可有可无. 覆盖是指派生类函数覆盖基类函数,特征是 (1)不同的范围(分别位 ...

  2. 函数重载 覆盖 隐藏

    重载overload 函数名相同,但形参的个数或形参的类型不同. 仅返回值不同,不能定义为重载函数. class Base { public:void baseFun(int) { cout < ...

  3. java重载覆盖隐藏_重载,覆盖以及隐藏 - osc_4dki3x9l的个人空间 - OSCHINA - 中文开源技术交流社区...

    重载是指同名函数具有不同的参数表.在同一访问区域内声明的几个具有不同参数列表(参数的类型.个数.顺序不同)的同名函数,程序会根据不同的参数列来确定具体调用哪个函数.对于重载函数的调用,编译期间确定,是 ...

  4. 问题:1.sizeof;2.重载覆盖隐藏;3.内存管理

    1-1.问:"char a[10]; int len = strlen(a);",len等于多少? 解析:strlen计算从数组a的第一个元素开始到'\0'总共有多少个字符(不包含 ...

  5. 覆盖与重载与隐藏——SAP电面(3)

    参考:http://man.chinaunix.net/develop/c&c++/c/c.htm#_Toc520634042 8.2.1 重载与覆盖 成员函数被重载的特征: (1)相同的范围 ...

  6. C++中的覆盖(重写)、重载、隐藏(重定义)、多态!

    例一: 首先声明Base类型的指针指向实际类型为Derived的对象,先调用基类构造函数,再调用派生类构造函数.输出Base, Derived. base->echo(); 指针是base类型, ...

  7. C++ 函数重载、隐藏与覆盖的区别

    代码编译运行环境:VS2017+Debug+Win32 1.函数重载(Function Overload) 1.1 定义 C++ 规定在同一作用域中,同名函数的形式参数(指参数的个数.类型或者顺序)不 ...

  8. 深入理解成员函数的重载、隐藏、覆盖规则(二)

    本文作者:islwj 本文出处:http://blog.csdn.net/islwj 声明: 本文可以不经作者同意, 任意复制, 转载, 但任何对本文的引用都请保留文章开始前三行的作者, 出处以及声明 ...

  9. 第九天2017/04/18(3、重载/覆盖 PK 重写/重定义、父类子类混搭风、抽象类)

    1.重载/覆盖 PK 重写/重定义 [预备知识] 函数重载必须在同一个类中发生子类无法重载父类的函数,父类同名的函数将会被名称覆盖重载是在编译期间根据参数类型和个数决定函数调用重载只放在同一个类之中, ...

最新文章

  1. 狂宴终有尽时,留一份清醒一份醉 比特币现金BCH凸显投资价值
  2. 如何在MDI中相同的子窗体只保留一个实例
  3. 致首次创业者:如果做到了这三点,想不成功都难(转)
  4. ecies算法c语言实现,Bouncy Castle算法库中ECIES算法调用示例
  5. MySQL日志文件之错误日志和慢查询日志详解
  6. java io读取文件夹_JavaIO利用迭代读取文件夹所有目录及文件
  7. 计算机图形学颜色表示,计算机图形学 颜色表示模型.ppt
  8. (王道408考研数据结构)第二章线性表-第三节2:双链表的定义及其操作(插入和删除)
  9. 开源性能测试工具JMeter快速入门(二)
  10. inno setup安装之前关闭mysql_innosetup安装之前关闭进程
  11. JavaWeb项目服务端获取客户端的IP地址
  12. JLink驱动设备管理器中显示黄色感叹号
  13. 旅游日记——2000元北京6天5夜游
  14. 计算机考研数据库题库
  15. xp系统打印机服务器报错,XP系统打印机显示Spoolsv.exe应用程序错误如何办?
  16. PostgreSQL中的索引—9(BRIN)
  17. 基于MFC和c++的销售管理系统,课程设计,实训
  18. SEO优化基础:SEO的优势与劣势
  19. Vue3+Element-Plus 登录成功后主页退出功能实现 十八
  20. (每日一练python)组合总和

热门文章

  1. Drupal的高速缓存配置APC
  2. window7共享xp或者2003的文件设置
  3. JBoss下布署Spring2.5和Struts2系统
  4. 我的世界java版gamemode指令_我的世界切换生存和创造模式的命令是什么?
  5. 怎么复制黑苹果config配置_估计是最后一次折腾黑苹果,技嘉 Z490 Vision D 的 OC 配置分享...
  6. Windows核心编程 第八章 用户方式中线程的同步(下)
  7. UVA11054Gergovia的酒交易
  8. C语言经典例1-无重复数字的三位数
  9. 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 五 )
  10. 【鸿蒙 HarmonyOS】UI 组件 ( 拖动条 Slider 组件 )