• 详谈C++虚函数表那回事(一般继承关系)


    沿途总是会出现关于C++虚函数表的问题,今天做一总结:

    1.什么是虚函数表:

    虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。简称为V-Table。在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承、覆的问题,

    保证其容真实反应实际的函数。这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得

    由为重要了,它就像一个地图一样,指明了实际所应该调用的函数。

    看一个例子:

    #pragma once
    
    //C++中的虚函数的作用主要是实现了多态的机制
    
    //通过Base的实例来得到虚函数表
    
    class Base 
    {
    public:
    	virtual void f() { cout << "Base::f" << endl; }
    	virtual void g() { cout << "Base::g" << endl; }
    	virtual void h() { cout << "Base::h" << endl; }
    };
    
    typedef void(*Fun)(void);
    void Test()
    {
    	Base b;
    
    	Fun pFun = NULL;
    
    	cout << "虚函数表地址:" << (int*)(&b) << endl;
    	cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&b) << endl;
    
    	for (int i = 0; i < 3; ++i)   //分别输出三个函数
    	{
    		pFun = (Fun)*((int*)*(int*)(&b)+i);  //函数指针加一表示找下一个函数的地址
    		pFun();
    	}
    }
    

    结果截图:


    关于代码中函数指针,已加说明。通过对对象地址的强转输出得到虚函数表的地址,即所谓的虚表指针内容:


    由于是两次进函数,故地址有变,但是道理没错!虚表指针就是 _vfptr;

    2.无虚函数覆盖的一般继承:(虚表)

    看代码:

    #pragma once
    
    //一般继承(无虚函数覆盖)
    class Base
    {
    public:  //三个虚函数
    	virtual void f() { cout << "Base::f" << endl; }
    	virtual void g() { cout << "Base::g" << endl; }
    	virtual void h() { cout << "Base::h" << endl; }
    };
    
    //无覆盖 公有继承
    class Derive :public Base
    {
    public:
    	virtual void f1() { cout << "Derive::f1" << endl; }
    	virtual void g1() { cout << "Derive::g1" << endl; }
    	virtual void h1() { cout << "Derive::h1" << endl; }
    };
    
    void Test()
    {
    	Derive d;  
    
    }
    
    请看,在此继承中,子类没有重载任何父类函数,则子类的函数表在VS2013下是这样的:


    发现没,d是一个子类对象,子类继承父类,虚函数表中尽然只有父类的虚函数地址,子类自己的虚函数都没有显示,这是不可能的啊

    很显然,这可能就是编译器的BUG,但其实,真实的子类虚函数表是这样的:


    可以看出:

    1》虚函数按照声明顺序存放于虚表中;

    2》子类虚函数在基类虚函数之后;

    3.有虚函数覆盖的一般继承:(虚表)

    看代码:

    #pragma once
    
    //有虚函数覆盖的一般继承
    //有覆盖是必然的,否则,虚函数将失去作用
    
    class Base
    {
    public:  //三个虚函数
    	virtual void f() { cout << "Base::f" << endl; }
    	virtual void g() { cout << "Base::g" << endl; }
    	virtual void h() { cout << "Base::h" << endl; }
    };
    
    //一个覆盖 公有继承
    class Derive :public Base
    {
    public:
    	virtual void f() { cout << "Derive::f" << endl; }  //注意:这里函数覆盖了基类的相同函数;
    	virtual void g1() { cout << "Derive::g1" << endl; }
    	virtual void h1() { cout << "Derive::h1" << endl; }
    };
    
    void Test()
    {
    	Base B;
    	B.f();
    
    	Derive d; 
    	Base *b = new Derive();
    	b->g();
    	b->f();  //调用的是子类中的f(); 实现多态的表现;
    }

    运行结果:


    很明显基类对象调用基类的函数,子类对象,“好像”也调用子类函数,好像的原因看下面:


    分析:

    1》覆盖的虚函数f()存放在虚表中原来父类虚函数的位置。

    2》其他函数依旧

    3》这里还是同样没有显示子类虚函数存放在表中,个人坚持认为,是编译器的bug,真实展示如下:


    可见:覆盖的虚函数f()存放在虚表中原来父类虚函数的位置,这也是基类对象*p访问 f() 时,访问的直接是子类的虚函数,完美的体现了多态;

    有关多态,请在见另一篇博客:http://blog.csdn.net/li_ning_/article/details/51872201

    另外有关多重继承,见下篇;

    赐教!

  • 相关阅读:
    github和bitbucket
    shell 删除文件下的* (copy).jpg备份文件
    linux 的iptables防火墙
    yum使用本地源
    linux的vnc- rdesktop远程登录windows桌面
    httpd/php/mysql的安装-1
    linux下的视频音频播放器终极解决方案
    linux读写ntfs
    示波器和三极管
    电子技术中的dB
  • 原文地址:https://www.cnblogs.com/melons/p/5791810.html
Copyright © 2020-2023  润新知