今天拜读了陈皓的C++ 虚函数表解析的文章,感觉对C++的继承和多态又有了点认识,这里写下自己的理解。如果哪里不对的,欢迎指正。如果对于C++虚函数表还没了解的话,请先拜读下陈皓的C++ 虚函数表解析的文章,不然我写的可能你看不懂。

以前一直对于c++多态感觉很神奇,从书上看,多态就是在构造子类对象的时候,通过虚函数,利用父类指针,来调用子类真正的函数。这个解释是正确的,但是它是怎么实现的呢,一直再猜想。以前也知道有虚函数表这件事,也没有仔细理解是什么东东。今天仔细读了陈皓的文章,才明白C++多态的原理。这里说说我的理解:

 先附上我写的一段简单代码:

   #include <iostream>
using namespace std; class Base{
public:
virtual void f() { cout << "Base::f()" << endl;}
virtual void g() { cout << "Base::g()" << endl;}
};
class Derive : public Base {
public:
virtual void f() { cout << "Devive::f()" << endl;}
virtual void f1() { cout << "Devive::f1()" << endl;}
virtual void g1() { cout << "Devive::g1()" << endl;}
};
typedef void (*Fun)(void); int main()
{
Fun pFun = NULL; Base b;
cout << "virtual table address: " << (int*)(&b) << endl;
cout << "first virtual function address: " << (int*)*(int*)(&b) << endl;
pFun = (Fun)(*(int*)*(int*)(&b));
pFun();
pFun = (Fun)*((int*)*(int*)(&b)+);
pFun(); cout << "*********" << endl; Derive d;
cout << "virtual table address: " << (int*)(&d) << endl;
cout << "first virtual function address: " << (int*)*(int*)(&d) << endl;
pFun = (Fun)(*(int*)*(int*)(&d));
pFun();
pFun = (Fun)*((int*)*(int*)(&d)+);
pFun();
pFun = (Fun)*((int*)*(int*)(&d)+);
pFun();
pFun = (Fun)*((int*)*(int*)(&d)+);
pFun();

输出结果:

virtual table address: 0xbfd268f8
first virtual function address: 0x8048b90
Base::f()
Base::g()
*********
virtual table address: 0xbfd268f4
first virtual function address: 0x8048b78
Devive::f()
Base::g()
Devive::f1()
Devive::g1()

理解1:首先你想要实现多态就必须通过虚函数,如果第6行我们改为 void f() { cout << "f" << endl;}, 那么顾名思义这个f()函 数就不会进入虚函数表中,也就没有多态之说了

理解2:在32位系统和64位系统下,取得虚函数表里的函数的方法还有所不同,再32位系统下,如程序那样取就行,而在64位系统下,取得第二个函数(Fun)*((int*)*(int*)(&d+2),第三个函数(Fun)*((int*)*(int*)(&d+4)....

理解3:对于程序清单里,我们分别输出类 Base和类Derive的v-table的地址和第一个虚函数的地址,我们发现,他们地址根本不一样,所以说,C++会为每一个类都分配一个virtual table

理解4:如果我们分别打印 *(int*)*(int*)(&b),*(int*)*(int*)(&d),我们发现他们的值都是一样的,这就验证我的猜想,虚函数表存放都是虚函数的指针(这话说的有点蛋疼,但这不是关键),故子类调用父类的时候,调用函数,是去查的虚函数表,然后查到函数的真正的位置,然后调用

理解5:在理解了虚函数表的结构后,我觉得多态的函数调用,是跟子类的虚函数表有关的,可以说是跟子类没有直接关系的(我以前觉得多态函数的调用是先去父类的函数中找这个函数,然后在这个子类中找同样的函数调用它,现在想来真是2到渣的节奏)

附几张图,来说明这个调用关系

说明一下,父类a的函数分别是virtual void f(),virtual void g(),virtual void h()

     子类b的函数分别是virtual void f1(), virtual void g1(), virtual void h1()

     子类c的函数分别是virtual void f(), virtual void g1(), virtual void h1()

类a.只有父类时

 

类b.有子类继承,但是没有函数的重载

 

类c.有子类继承,有函数重载

 

如:我们调用

Derive c;

Base *p_base = &c;

p_base->f();

这里的调用过程是,在类C虚函数表中查找到Base类的f()的地址【虚函数表内函数的存放顺序是,先放父类的虚函数,然后再存放子类的虚函数】,然后在该地址处取得存放的真正的函的地址,即是子类的函数,故调用的是子类的函数

版权所有,如要转载请说明出处及作者

我理解的C++虚函数表的更多相关文章

  1. 深入理解C++虚函数表

    虚函数表是C++类中存放虚函数的一张表,理解虚函数表对于理解多态很重要. 本次使用的编译器是VS2013,为了简化操作,不用去操作函数指针,我使用到了VS的CL编译选项来查看类的内存布局. CL使用方 ...

  2. 从逆向的角度去理解C++虚函数表

    很久没有写过文章了,自己一直是做C/C++开发的,我一直认为,作为一个C/C++程序员,如果能够好好学一下汇编和逆向分析,那么对于我们去理解C/C++将会有很大的帮助,因为程序中所有的奥秘都藏在汇编中 ...

  3. 对C++虚函数、虚函数表的简单理解

    一.虚函数的作用 以一个通用的图形类来了解虚函数的定义,代码如下: #include "stdafx.h" #include <iostream> using name ...

  4. 深入理解类成员函数的调用规则(理解成员函数的内存为什么不会反映在sizeof运算符上、类的静态绑定与动态绑定、虚函数表)

    本文转载自:http://blog.51cto.com/9291927/2148695 总结: 一.成员函数的内存为什么不会反映在sizeof运算符上?             成员函数可以被看作是类 ...

  5. C++虚函数表理解

    一,思维模式图 二,代码验证 class A { public: A(int x) { fProtected = x; } float GetFProtected() { return fProtec ...

  6. C++ 虚函数表与内存模型

    1.虚函数 虚函数是c++实现多态的有力武器,声明虚函数只需在函数前加上virtual关键字,虚函数的定义不用加virtual关键字. 2.虚函数要点 (1) 静态成员函数不能声明为虚函数 可以这么理 ...

  7. C++虚函数及虚函数表解析

    一.背景知识(一些基本概念) 虚函数(Virtual Function):在基类中声明为 virtual 并在一个或多个派生类中被重新定义的成员函数.纯虚函数(Pure Virtual Functio ...

  8. 关于C++中虚函数表存放位置的思考

    其实这是我前一段时间思考过的一个问题,是在看<深入探索C++对象模型>这本书的时候我产生的一个疑问,最近在网上又看到类似的帖子,贴出来看看: 我看到了很多有意思的答案,都回答的比较好,下面 ...

  9. c++ 继承类强制转换时的虚函数表工作原理

    本文通过简单例子说明子类之间发生强制转换时虚函数如何调用,旨在对c++继承中的虚函数表的作用机制有更深入的理解. #include<iostream> using namespace st ...

随机推荐

  1. Qt动画效果的实现,QPropertyAnimation

    Qt动画架构中的主要类如下图所示: 动画框架由基类QAbstractAnimation和它的两个儿子QVariantAnimation和QAnimationGroup组成.QAbstractAnima ...

  2. CSRF 攻击的应对之道--转

    http://www.ibm.com/developerworks/cn/web/1102_niugang_csrf/ 简介: CSRF(Cross Site Request Forgery, 跨站域 ...

  3. CentOS安装某个命令的办法如locate

    在Linux使用命令时,有时候因为系统精简的原因,某些命令可能没有安装locate包,例如你要输入的命令是locate 发现没有该命令,修复办法如下: 一.使用如下命令查询缺失命令的包名 #yum l ...

  4. javascript冷知识

    本人很少写博客,所以文笔很不好,如果解释的不够清楚的,欢迎点评 1.+号(一元加操作符): 如果放在数值前的话,对数值不会产生任何影响,不过放在其他的数据类型前面的话,就等于调用number()将他转 ...

  5. oracle数据库常用SQL语句

    1)删除表的一列 ALTER TABLE 表名 DROP COLUMN 列名; 2)增加表的一列 且默认值为0 alter table 表名 add 字段名 类型 default '0'; 3)修改表 ...

  6. ansible api

    ##一个简单的python脚本,通过ansible的api调用get_url模块实现远程下载功能 #!/usr/bin/env python import json import ansible.ru ...

  7. JavaScript实现搜索联想功能

    -.虽然Jquery已经有了一个完整的包 实现前端搜索联想功能,但是出于学习还是想了解一下实现此功能的原理性 回想起来 实现此功能很简单,1.前端输入字符串 文本改变 异步请求服务器 将返回的资料显示 ...

  8. [JavaEE] Eclipse中web-inf和meta-inf文件夹的信息

    WEB-INF    /WEB-INF/web.xml        你的Web应用程序配置文件,这是一个XML文件,其中描述了 servlet 和其他的应用组件配置及命名规则:    /WEB- I ...

  9. 【阿里云产品公测】PTS测试 SLB+ECS+RDS组合的DZ论坛负载极限压力,100并发2000页

    作者:阿里云用户woaj01 ​​环境介绍: 1.ECS:1核 1G 5M 杭州 2.RDS:240M  5G  杭州内网 3.SLB:私网实例​ 配置测试环境: 测试脚本: 1.生成参数文件,我的方 ...

  10. #技塑人生# windows2008无法远程— 注册表缺失键值导致高级防火墙服务异常

    windows2008无法远程— 注册表缺失键值导致高级防火墙服务异常 阿里云技术支持中心:章阿贵 一.远程无法访问(windows server 2008) 症状:无法远程但是系统内网络正常,防火墙 ...