zoukankan      html  css  js  c++  java
  • C++虚函数

    转自:http://blog.csdn.net/haoel/article/details/1948051

    测试代码:

    class Base {
    
    public:
    
        virtual void f() { cout << "Base::f" << endl; }
    
        virtual void g() { cout << "Base::g" << endl; }
    
        virtual void h() { cout << "Base::h" << endl; }
    
    };
    
    
        Base b;
        Fun pFun = NULL;
        cout << "虚函数表地址:" << (int*)(&b) << endl;
        cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&b) << endl;
        // Invoke the first virtual function  
        pFun = (Fun)*((long long*)*(long long*)(&b));
        pFun();
    
        pFun = (Fun)*((long long*)*(long long*)(&b) + 1);
        pFun();
    
    
        pFun = (Fun)*((long long*)*(long long*)(&b)+2);
        pFun();
    

    其中:*(long long*)(&b)是把对象b最前面8字节内容取出;((long long*)*(long long*)(&b)),把取出的内容看作地址,也就是虚函数表的开始地址,即Base::f()所在位置;*((long long*)*(long long*)(&b))把虚函数表第一个位置内容取出;(Fun)*((long long*)*(long long*)(&b)内容作为地址,赋给函数指针。
    结果:
    虚函数表地址:0000006A8A65FB78
    虚函数表 — 第一个函数地址:000000004CEA0498
    Base::f
    Base::g
    Base::h

    这里写图片描述
    其它成员指的是member,不包括member function。成员函数存放在代码区域,同过对象空间内的指针调用。
    注意:在上面这个图中,我在虚函数表的最后多加了一个结点,这是虚函数表的结束结点,就像字符串的结束符“/0”一样,其标志了虚函数表的结束。这个结束标志的值在不同的编译器下是不同的。

    一般继承(无虚函数覆盖)
    这里写图片描述
    我们可以看到下面两点:
    虚函数按照其声明顺序放于表中。
    父类的虚函数在子类的虚函数前面。

    一般继承(有虚函数覆盖)
    这里写图片描述
    我们从表中可以看到下面两点:
    覆盖的f()函数被放到了虚表中原来父类虚函数的位置。
    没有被覆盖的函数依旧。

    多重继承(没有虚函数覆盖)
    假设有下面这样一个类的继承关系。注意:子类并没有覆盖父类的函数。
    这里写图片描述
    这里写图片描述
    我们可以看到:
    每个父类都有自己的虚表。
    子类的成员函数被放到了第一个父类的表中。(所谓的第一个父类是按照声明顺序来判断的)
    这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。

    多重继承(有虚函数覆盖)
    这里写图片描述
    这里写图片描述
    我们可以看见,三个父类虚函数表中的f()的位置被替换成了子类的函数指针。这样,我们就可以任一静态类型的父类来指向子类,并调用子类的f()了。如:

        Derive d;
        Base1 *b1 = &d;
        Base2 *b2 = &d;
        Base3 *b3 = &d;
    
        b1->f(); //Derive::f()
        b2->f(); //Derive::f()
        b3->f(); //Derive::f()
    
        b1->g(); //Base1::g()
        b2->g(); //Base2::g()
        b3->g(); //Base3::g()
    
        long long** p = (long long**)&b;
        pFun = (Fun)p[0][0];
        pFun();
        pFun = (Fun)p[0][1];
        pFun();
        pFun = (Fun)p[0][2];
        pFun();

    此处注意数组指针和二级指针的区别。
    注:如果基类的析构函数声明为虚函数,那么在虚函数表base1中,Deriver::g1前会有base1析构函数的地址;在vs的虚函数表中base1析构函数后的地址不会显示,但确实存在。

  • 相关阅读:
    一位测友的真实面试题
    内部cms系统测试
    po模式
    描述器
    monkey命令
    进程管理工具supervisor
    webpack打包绝对路径引用资源和element ui字体图标不显示的解决办法
    pycharm flask debug调试接口
    应对ADT(Eclipse)的No more handles解决办法
    收集整理Android开发所需的Android SDK、开发中用到的工具、Android开发教程、Android设计规范,免费的设计素材等。
  • 原文地址:https://www.cnblogs.com/ggzone/p/10121254.html
Copyright © 2011-2022 走看看