原文网址: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 // // main.cpp // 2013-7-17作业2 // // Created by 丁小未 on 13-7-17. // Copyright (c) 201 ...

  2. c/c++:重载 覆盖 隐藏 overload override overwrite

    http://www.cnblogs.com/qlee/archive/2011/07/04/2097055.html 成员函数的重载.覆盖与隐藏成员函数的重载.覆盖(override)与隐藏很容易混 ...

  3. c++ 继承 虚函数与多态性 重载 覆盖 隐藏

    http://blog.csdn.net/lushujun2011/article/details/6827555 2011.9.27 1) 定义一个对象时,就调用了构造函数.如果一个类中没有定义任何 ...

  4. c++中 重载 覆盖 隐藏的区别 附加 mutable笔记

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

  5. c++虚函数,纯虚函数,抽象类,覆盖,重载,隐藏

    C++虚函数表解析(转) ——写的真不错,忍不住转了  http://blog.csdn.net/hairetz/article/details/4137000 浅谈C++多态性  http://bl ...

  6. C++中的重载,隐藏,覆盖,虚函数,多态浅析

    直到今日,才发现自己对重载的认识长时间以来都是错误的.幸亏现在得以纠正,真的是恐怖万分,雷人至极.一直以来,我认为重载可以发生在基类和派生类之间,例如: class A { public: void ...

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

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

  8. 【C++】三大概念要分清--重载,隐藏(重定义,覆盖(重写)

    { c++三大概念要分清--重载,隐藏(重定义),覆盖(重写)} 重载 •  概念:在同一个作用域内:函数名相同,参数列表不同(参数个数不同,或者参数类型不同,或者参数个数和参数类型都不同),返回值类 ...

  9. c++三大概念要分清--重载,隐藏(重定义),覆盖(重写)

    重载,隐藏(重定义),覆盖(重写)—这几个名词看着好像很像,不过其实一样都不一样!! 综述: 说明:覆盖中的访问修饰符可以不同是指可以不用显示地用virtual:当访问修饰符改为const或者stat ...

随机推荐

  1. row_number() OVER (PARTITION BY COL1 ORDER BY COL2)

    select *,ROW_NUMBER() over(partition by deviceID order by RecordDate desc row_number() OVER (PARTITI ...

  2. cognos10.2.2使用ODBC连接oracle92数据库(BMT-IMP-0016)

    对于Cognos Server 64位Windows系统,注意!请下载32位程序.即:   因为Cognos只认32位ODBC程序.这里千万要注意.对于64位的Windows系统的ODBC有两个程序, ...

  3. (三)映射对象标识符(OID)

    所有项目导入对应的hibernate的jar包.mysql的jar包和添加每次都需要用到的HibernateUtil.java 第一节:Hibernate 用对象标识符(OID)来区分对象 例子: h ...

  4. mongodb write 【摘自网上,只为记录,学习】

    mongodb有一个write concern的设置,作用是保障write operation的可靠性.一般是在client driver里设置的,和db.getLastError()方法关系很大 一 ...

  5. redis基本数据类型【2】-Hash类型

    一.概述 1.散列是一种典型的字典结构,filed和value的映射,但value只能存储字符串,不支持其他类型 2.一个散列类型最多包含 2^32 -1个字段 3.散列适合存储对象:使用对象和ID构 ...

  6. [jquery] jQuery点滴[持续更新]

    001.查看jquery的版本. $(function(){ console.log($()); //jquery console.log($().jquery); }); 002.(new Func ...

  7. ffmpeg只使用h264编译参数

    --disable-everything --enable-decoder=h264 --enable-demuxer=h264 --enable-parser=h264 --disable-ffpl ...

  8. Ubuntu不卸载ibus前提下安装搜狗输入法

    第一步 在命令行中输入以下行命令安装fictx框架 sudo apt-get install fcitx fcitx-config-gtk im-switch 第二步 去 http://pinyin. ...

  9. APUE学习笔记-文件I/O

    这次回顾APUE中第三四章的内容,主要是文件I/O操作相关的接口函数.    UNIX系统的文件I/O是不带缓冲的I/O,不带缓冲是指每个read和write都调用系统内核的一个系统调用. 1.文件描 ...

  10. iOS面试题6.30总结

    越来越多的人投入iOS这个行业中,但是作为刚才学校毕业的学生,我们没有任何经验.或者经验很少.但是这也不能阻挡我们对苹果的热情,想投入iOS的开发中.而作为进入企业的第一步,我们要参加面试.面试中我们 ...