沿途总是会出现关于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

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

赐教!

详谈C++虚函数表那回事(一般继承关系)的更多相关文章

  1. 详谈C++虚函数表那回事(多重继承关系)

    上一篇说了一般继承,也就是单继承的虚函数表,接下来说说多重继承的虚函数表: 1.无虚函数覆盖的多重继承: 代码: #pragma once //无覆盖,多重继承 class Base1 { publi ...

  2. C++ 虚函数表 多重继承

    上次研究的是单继承的情况,这次研究多重继承下的虚函数表的排列情况. 这次A,A1,A2,B这几个类的继承关系如下图: 测试代码如下: #include<iostream> using na ...

  3. C++单继承、多继承情况下的虚函数表分析

    C++的三大特性之一的多态是基于虚函数实现的,而大部分编译器是采用虚函数表来实现虚函数,虚函数表(VTAB)存在于可执行文件的只读数据段中,指向VTAB的虚表指针(VPTR)是包含在类的每一个实例当中 ...

  4. C++ 虚函数表与多态 —— 继承的虚函数表 & 内存布局

    1. 使用继承的虚函数表: 如果不涉及多重继承,每个类只有1个虚函数表,当子类继承父类后,子类可以自己改写和新增虚函数,如下图所示: 子类重写 func_1 后,子函数的 func_1 将会有新的逻辑 ...

  5. (C/C++学习)4.C++类中的虚函数表Virtual Table

    说明:C++的多态是通过一张虚函数表(Virtual Table)来实现的,简称为V-Table.在这个表中,主要为一个类的虚函数的地址表,这张表解决了继承.覆写的问题,保证其真实反应实际的虚函数调用 ...

  6. C++ 虚函数表解析

    转载:陈皓 http://blog.csdn.net/haoel 前言 C++中 的虚函数的作用主要是实现了多态的机制.关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实 ...

  7. C++ 多态、虚函数机制以及虚函数表

    1.非virtual函数,调用规则取决于对象的显式类型.例如 A* a  = new B(); a->display(); 调用的就是A类中定义的display().和对象本体是B无关系. 2. ...

  8. C++迟后联编和虚函数表

    先看一个题目: class Base { public: virtual void Show(int x) { cout << "In Base class, int x = & ...

  9. C++ 知道虚函数表的存在

    今天翻看陈皓大大的博客,直接找关于C++的东东,看到了虚函数表的内容,找一些能看得懂的地方记下笔记. 0 引子 类中存在虚函数,就会存在虚函数表,在vs2015的实现中,它存在于类的头部. 假设有如下 ...

随机推荐

  1. 配置maven环境出现ARP tomcat native library 版本安装跟需求版本不一致时的解决方法An incompatible version xxxx of the APR based Apache Tomcat Native library is installed, while Tomcat requires version xxxx

    此地址下载你所需要的library版本http://archive.apache.org/dist/tomcat/tomcat-connectors/native/ 点击binaries 点win32 ...

  2. beautifulsoup库使用

    介绍与安装 Beautiful Soup 是一个HTML/XML的解析器,主要的功能也是如何解析和提取 HTML/XML 数据.BeautifulSoup 用来解析 HTML 比较简单, API非常人 ...

  3. Maven 搭建 SSM 项目 (oracle)

    简单谈一下maven搭建 ssm 项目 (使用数据库oracle,比 mysql 难,所以这里谈一下) 在创建maven 的web项目时,常常会缺了main/java , main/test 两个文件 ...

  4. linux系统文件系统重要知识介绍

    [root@Asterplus:~]$ls -lhitotal 48K3684713 -rw------- 1 root root 5.9K Jul 1 00:23 anaconda-ks.cfg36 ...

  5. 【Spring源码分析】AOP源码解析(上篇)

    前言 前面写了六篇文章详细地分析了Spring Bean加载流程,这部分完了之后就要进入一个比较困难的部分了,就是AOP的实现原理分析.为了探究AOP实现原理,首先定义几个类,一个Dao接口: pub ...

  6. log4j日志输出性能优化-缓存、异步

     转载 1.log4j已成为大型系统必不可少的一部分,log4j可以很方便的帮助我们在程序的任何位置输出所要打印的信息,便于我们对系统在调试阶段和正式运行阶段对问题分析和定位.由于日志级别的不同,对系 ...

  7. Error:Error converting bytecode to dex: Cause: com.android.dex.DexException: Multiple dex files define Lcom/google/android/gms/internal/adp;

    Q:版本号不对,广告插件的版本号和项目中用的版本号不一致 A:adsplugins的build gradle里面用的版本号是10.0.1,修改app的build gradle 的google类都改成1 ...

  8. 28.Django cookie

    概述 1.获取cookie request.COOKIES['key'] request.COOKIES.get('key') request.get_signed_cookie(key, defau ...

  9. 洛谷 P1017 进制转换

    推荐洛谷 题目描述 我们可以用这样的方式来表示一个十进制数: 将每个阿拉伯数字乘以一个以该数字所处位置的(值减1)为指数,以10为底数的幂之和的形式.例如:123可表示为 1*10^2+2*10^1+ ...

  10. git上传项目到github简易步骤

    第一步: 在github创建仓库,创建完成进入该仓库,仓库地址如:https://github.com/winerss/wcMall格式 第二步:使用git,cd到本地项目目录下,执行git明令: g ...