今天拜读了陈皓的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. php 运行客户提交代码(攻击)和运行图片中的代码

    1.$a=@strrev(ecalper_gerp);$b=@strrev(edoced_46esab);@$a($b(L3h4L2Ug),$_POST[POST],bxxb); 2.<?php ...

  2. 21 Free SEO Tools For Bloggers--reference

    http://dizyne.net/21-free-seo-tools-for-bloggers/ What do you think is important in a website? Yes, ...

  3. ubuntu11.04编译TQ2440 Qt2.2.0 成功

    转:http://blog.csdn.net/xuehui869/article/details/8958311 运行主机:ubuntu 32位 11.04QT版本:2.2.0开发板:TQ2440源码 ...

  4. 内存映射mmap

    Table of Contents 1. 什么是mmap 2. 使用方法 2.1. mmap构造器的格式 2.2. 例子1 2.3. 例子2 3. 其它 4. 参考资料 什么是mmap 通常在Unix ...

  5. LeetCode11 Container With Most Water

    题意: Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i, a ...

  6. js实现网站导航的二级下拉菜单

    http://www.codesky.net/article/201109/1200js/%E5%AE%9E%E7%94%A8%E5%AF%BC%E8%88%AA%E8%8F%9C%E5%8D%95. ...

  7. Unity3D 4.61 实现淡入淡出的场景过渡方法。

    还在学习过程中,如果有大大看到请指点. orz原来官方就有了更好的处理方法的教程,具体查看下面视屏. [Unity官方实例教程 秘密行动] Unity官方教程<秘密行动>(五) 屏幕渐变效 ...

  8. [C.Sharp] TimeSpan的用法,获取测试程序运行时间

    TimeSpan的用法 TimeSpan是用来表示一个时间段的实例,两个时间的差可以构成一个TimeSpan实例,现在就来简单介绍一下几点重要的用法: a 先来介绍几个方法 TimeSpan.Minu ...

  9. Debian 中添加ppa

    在Debian8中默认没有"apt-add-repository"命令,所有也就没法安装ppa. 怎么破? sudo apt-get install software-proper ...

  10. 【Linux】Shell脚本编程(一)

    Linux shell脚本编程: 守护进程,服务进程:启动?开机时自动启动: 交互式进程:shell应用程序 广义:GUI,CLI GUI: CLI: 词法分析:命令,选项,参数 内建命令: 外部命令 ...