C++虚函数表分析

在<<逆向C++>>一文中提到 VC6.0 以上的编译器支持一个d1reportAllClassLayout的开关, 可以输出所有对象的内存布局信息, 我自己常用vs2005来开发,所以这里我就以vs2005为例讲怎么设置d1reportAllClassLayout。

右键项目属性(Properties)—》配制属性(Configuration Properties)—》C/C++--》命令行(Command Line)的框里输入/d1reportAllClassLayout,即可看类的对象布局,如下图:(当然也可以使用命令行:cl –d1reportSingleClassLayout[classname] test.cpp)

测试代码:

class CBase
{
public:
        virtual void fun(void) {}

private:
        int m_valuable;
};

输出结果:

class CBase size(8):
 +---
 0 | {vfptr}
 4 | m_valuable
 +---
CBase::[email protected]:
 | &CBase_meta
 |  0
 0 | &CBase::fun
CBase::fun this adjustor: 0
CDerived:非virtual继承CBase,持有一个virtual重写方法和一个virtual新方法,一个成员变量
class CDerived: public CBase
{
public:
	void fun(void) { }
	virtual void vfun(void) { }

public:
	int m_derived;
};
编译后输出结果:
class CDerived size(12):
 +---
 | +--- (base class CBase)
 0 | | {vfptr}
 4 | | m_valuable
 | +---
 8 | m_derived
 +---
CDerived::[email protected]:
 | &CDerived_meta
 |  0
 0 | &CDerived::fun
 1 | &CDerived::vfun
CDerived::fun this adjustor: 0
CDerived::vfun this adjustor: 0
其中[email protected]的CBase::fun项在这里更新为CDerived::fun,同时增加了一项CDerived::vfun。
CDerived2:virtual继承CBase,持有一个virtual重写方法和一个virtual新方法,一个成员变量,因此结构为[email protected]自身,[email protected]自身,[email protected]自身,以及CBase结构。
总结:
class CDerived2: virtual public CBase
{
public:
	void fun(void) { }
	virtual void vfun(void) { }

public:
	int m_derived;
};
结果输出:
class CDerived2 size(20):
 +---
 0 | {vfptr}
 4 | {vbptr}
 8 | m_derived
 +---
 +--- (virtual base CBase)
12 | {vfptr}
16 | m_valuable
 +---
CDerived2::[email protected]@:
 | &CDerived2_meta
 |  0
 0 | &CDerived2::vfun
CDerived2::[email protected]:
 0 | -4
 1 | 8 (CDerived2d(CDerived2+4)CBase)
CDerived2::[email protected]@:
 | -12
 0 | &CDerived2::fun
CDerived2::fun this adjustor: 12
CDerived2::vfun this adjustor: 0
vbi:    class  offset o.vbptr  o.vbte fVtorDisp
           CBase      12       4       4 0
其中[email protected]自身只有一项:CDerived2::vfun(),[email protected]自身只有一项:它virtual继承的父类CBase,而[email protected]原来的CBase::fun更新为CDerived2::fun。

CDerived3:virtual继承CBase,因此结构为[email protected]自身,[email protected]自身,CBase结构
class CDerived3: virtual public CBase
{
public:
	void fun(void) { }

public:
	int m_derived3;
};
结果输出:
class CDerived3 size(16):
 +---
 0 | {vbptr}
 4 | m_derived3
 +---
 +--- (virtual base CBase)
 8 | {vfptr}
12 | m_valuable
 +---
CDerived3::[email protected]:
 0 | 0
 1 | 8 (CDerived3d(CDerived3+0)CBase)
CDerived3::[email protected]:
 | -8
 0 | &CDerived3::fun
CDerived3::fun this adjustor: 8
vbi:    class  offset o.vbptr  o.vbte fVtorDisp
           CBase       8       0       4 0
CGDerived:继承CDerived2、CDerived3
class CGDerived: public CDerived2, public CDerived3
{
public:
	void vfun() { }
	virtual void vgfun() { }

public:
	int m_gd;
};
输出:
class CGDerived size(32):
 +---
 | +--- (base class CDerived2)
 0 | | {vfptr}
 4 | | {vbptr}
 8 | | m_derived
 | +---
 | +--- (base class CDerived3)
12 | | {vbptr}
16 | | m_derived3
 | +---
20 | m_gd
 +---
 +--- (virtual base CBase)
24 | {vfptr}
28 | m_valuable
 +---
CGDerived::[email protected]@:
 | &CGDerived_meta
 |  0
 0 | &CGDerived::vfun
 1 | &CGDerived::vgfun
CGDerived::[email protected]@:
 0 | -4
 1 | 20 (CGDerivedd(CDerived2+4)CBase)
CGDerived::[email protected]@:
 0 | 0
 1 | 12 (CGDerivedd(CDerived3+0)CBase)
CGDerived::[email protected]@:
 | -24
 0 | &thunk: this-=12; goto CDerived2::fun
CGDerived::vfun this adjustor: 0
CGDerived::vgfun this adjustor: 0
vbi:    class  offset o.vbptr  o.vbte fVtorDisp
           CBase      24       4       4 0
因此首先是CDerived2的结构和CDerived3的结构,自己的新virtual方法vgfun则添加在最左父类CDerived2的虚函数表中。然后是自己的成员。最后,CDerived2和CDerived3的父类CBase结构也带入其中。它的fun默认指向CGDerived的最左父类CDerived2::fun。

CGG:继承CGDerived
class CGG: public CGDerived
{
public:
	int m_kc;
};
输出:
class CGG size(36):
 +---
 | +--- (base class CGDerived)
 | | +--- (base class CDerived2)
 0 | | | {vfptr}
 4 | | | {vbptr}
 8 | | | m_derived
 | | +---
 | | +--- (base class CDerived3)
12 | | | {vbptr}
16 | | | m_derived3
 | | +---
20 | | m_gd
 | +---
24 | m_kc
 +---
 +--- (virtual base CBase)
28 | {vfptr}
32 | m_valuable
 +---
CGG::[email protected]@:
 | &CGG_meta
 |  0
 0 | &CGDerived::vfun
 1 | &CGDerived::vgfun
CGG::[email protected]@:
 0 | -4
 1 | 24 (CGGd(CDerived2+4)CBase)
CGG::[email protected]@:
 0 | 0
 1 | 16 (CGGd(CDerived3+0)CBase)
CGG::[email protected]@:
 | -28
 0 | &thunk: this-=16; goto CDerived2::fun
vbi:    class  offset o.vbptr  o.vbte fVtorDisp
           CBase      28       4       4 0
增加一个变量,基本只是把CGDerived的结构再套一层,最后加上自己的成员变量。vbtable所指向的父类结构依然在最后。

空类
class CBase2
{

};
输出:
class CBase2 size(1):
 +---
 +---
CD2:virtual继承则一定会创建vbtable,用vbptr指针指向,因此size为4
class CD2: virtual public CBase2
{

};
输出:
class CD2 size(4):
 +---
 0 | {vbptr}
 +---
 +--- (virtual base CBase2)
 +---
CD2::[email protected]:
 0 | 0
 1 | 4 (CD2d(CD2+0)CBase2)
vbi:    class  offset o.vbptr  o.vbte fVtorDisp
          CBase2       4       0       4 0

CE:非virtual继承CD2和CDerived2,于是按照类的声明顺序,先带入CDerived2的结构,再带入CD2的结构(而不是按继承顺序)
class CE: public CD2, public CDerived2
{

};
输出:
class CE size(24):
 +---
 | +--- (base class CDerived2)
 0 | | {vfptr}
 4 | | {vbptr}
 8 | | m_derived
 | +---
 | +--- (base class CD2)
12 | | {vbptr}
 | +---
 +---
 +--- (virtual base CBase2)
 +---
 +--- (virtual base CBase)
16 | {vfptr}
20 | m_valuable
 +---
CE::[email protected]@:
 | &CE_meta
 |  0
 0 | &CDerived2::vfun
CE::[email protected]@:
 0 | 0
 1 | 4 (CEd(CD2+0)CBase2)
 2 | 4 (CEd(CE+12)CBase)
CE::[email protected]@:
 0 | -4
 1 | 12 (CEd(CDerived2+4)CBase)
CE::[email protected]@:
 | -16
 0 | &thunk: this-=4; goto CDerived2::fun
vbi:    class  offset o.vbptr  o.vbte fVtorDisp
          CBase2      16      12       4 0
           CBase      16      12       8 0
CF:virtual继承CBase和CBase2,vbtable此时的项便有两个,此时按照继承的顺序,而不是按照类的声明顺序
class CF: virtual public CBase2, virtual public CBase
{

};
输出:
class CF size(12):
 +---
 0 | {vbptr}
 +---
 +--- (virtual base CBase2)
 +---
 +--- (virtual base CBase)
 4 | {vfptr}
 8 | m_valuable
 +---
CF::[email protected]:
 0 | 0
 1 | 4 (CFd(CF+0)CBase2)
 2 | 4 (CFd(CF+0)CBase)
CF::[email protected]:
 | -4
 0 | &CBase::fun
vbi:    class  offset o.vbptr  o.vbte fVtorDisp
          CBase2       4       0       4 0
           CBase       4       0       8 0

总结:

继承方式:非virtual继承:导入各个父类的结构(按照父类声明的顺序,从上到下),自身member在最后

重写virtual方法:更新该方法最早定义的类的vftable

新的virtual方法:在最左父类的vftable增加

继承方式:有virtual继承:在自身member后增加virtual父类的结构(按照子类继承的顺序从左到右),同时在最前面增加vbtable(如果没有的话),增加一项指向父类结构

重写virtual方法:更新该方法的最早定义的类的vftable

新的virtual方法:在自身最前面增加vftable(如果没有的话),在自己的vftable增加

附 所有源码:

#ifndef TEST_CBASE_H
#define TEST_CBASE_H

class CBase
{
public:
	CBase();
	explicit CBase(int valuabel);
	~CBase();

	virtual void fun(void) {}

private:
	int m_valuable;
};

class CDerived: public CBase
{
public:
	void fun(void) { }
	virtual void vfun(void) { }

public:
	int m_derived;
};

class CDerived2: virtual public CBase
{
public:
	void fun(void) { }
	virtual void vfun(void) { }

public:
	int m_derived;
};

class CDerived3: virtual public CBase
{
public:
	void fun(void) { }

public:
	int m_derived3;
};

class CGDerived: public CDerived2, public CDerived3
{
public:
	void vfun() { }
	virtual void vgfun() { }

public:
	int m_gd;
};

class CGG: public CGDerived
{
public:
	int m_kc;
};

class CBase2
{

};

class CD2: virtual public CBase2
{

};

class CE: public CD2, public CDerived2
{

};

class CF: virtual public CBase2, virtual public CBase
{

};

#endif//TEST_CBASE_H
时间: 2024-10-03 23:04:14

C++虚函数表分析的相关文章

单继承与多继承中的虚函数表和虚函数指针

首先,我们了解一下何为单继承,何为多继承?? 单继承:一个子类只有一个直接父类. 多继承:一个子类有两个或多个直接父类. 单继承中的虚函数表分析: 示例程序: #include <iostream> using namespace std; typedef void(*FUNC)(); class Base { public: virtual void func1() { cout << "Base::func1()" << endl; } virt

类虚函数表原理实现分析(当我们将虚表地址[n]中的函数替换,那么虚函数的实现就由我们来控制了)

原理分析 当调用一个虚函数时, 编译器生成的代码会调用 虚表地址[0](param1, param2)这样的函数. 已经不是在调用函数名了. 当我们将虚表地址[n]中的函数实现改为另外的函数, 虚函数的实现就由我们来控制了. 实验 根据虚表原理, 实验一下修改自己程序的虚函数表项地址. 使编译器生成的代码执行一个虚函数A时, 执行的是我们自己定义的非虚函数B. 知识点 * 使用union赋值, 绕过编译器函数与变量强转赋值的限制 * 类成员函数指针的执行 * 修改和恢复自己的代码段属性 * 虚函

C++ Daily 《5》----虚函数表的共享问题

问题: 包含一个以上虚函数的 class B, 它所定义的 对象是否共用一个虚函数表? 分析: 由于含有虚函数,因此对象内存包含了一个指向虚函数表的指针,但是这个指针指向的是同一个虚函数表吗? 实验如下: 结论: 结果表面,同一个类的所有对象,都共享同一个虚函数表. 派生问题: 派生类 和 基类是否共享同一个虚函数表呢? C++ Daily <5>----虚函数表的共享问题,布布扣,bubuko.com

C++对象内存模型2 (虚函数,虚指针,虚函数表)

从例子入手,考察如下带有虚函数的类的对象内存模型: 1 class A { 2 public: 3 virtual void vfunc1(); 4 virtual void vfunc2(); 5 void func1(); 6 void func2(); 7 virtual ~A(); 8 private: 9 int m_data1, m_data2; 10 }; 11 12 class B : A { 13 public: 14 virtual void vfunc1();; 15 vo

深入理解C++虚函数表

虚函数表是C++类中存放虚函数的一张表,理解虚函数表对于理解多态很重要. 本次使用的编译器是VS2013,为了简化操作,不用去操作函数指针,我使用到了VS的CL编译选项来查看类的内存布局. CL使用方法: (1)在开始菜单中的vs目录下打开"Visual Studio Tools"目录,找到"VS2013 开发人员命令提示",打开它: (2)将你要编译的文件放到该命令行对应的文件夹中. (3)输入cl "文件名" /d1reportSingleC

查看虚函数表

如果你看到这篇文章时,急着去吃饭或泡MM,请跳转到蓝色字段开始阅读. C++中的虚函数的作用主要是实现了多态的机制.关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数.这种技术可以让父类的指针有“多种形态”,这是一种泛型技术.所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法.比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议. 关于虚函数的使用方法,我在这里不做过多的阐述.大家可以看看相关的C++的

解析虚函数表和虚继承

之前大二在学C++的时候一直对虚函数和虚继承有些晕(其实好像就是对virtual这个关键字不太熟悉)现在又学习到了一些,对虚函数表和虚继承的机制有了一点更深入的了解. 关于虚函数以及虚继承的基础知识,我自己也总结了一下,点击浅谈C++多态和C++继承可查看,在继承的总结的时候,我没有总结关于虚继承的知识,而且在多态总结也没有设计到太多的虚函数的知识,我是想把这两块集中在一起讲下,也算是自己对virtual关键字有个比较深入的了解吧.(本文所有代码均在VS2013编译器win32下测试) 另外对于

C++多态篇3——虚函数表详解之多继承、虚函数表的打印

在上上一篇C++多态篇1一静态联编,动态联编.虚函数与虚函数表vtable中,我最后简单了剖析了一下虚函数表以及vptr. 而在上一篇文章C++多态篇2--虚函数表详解之从内存布局看函数重载,函数覆盖,函数隐藏中我详细介绍了虚函数的函数重载,函数覆盖以及函数隐藏的问题,其实在那一篇文章中,对单继承的虚函数已经做了十分详细的解答了,如果对前面有兴趣的人可以先看一下那篇文章. 在这一篇中,我会具体的分析一下在不同继承中(单继承,多继承)关于虚函数表在内存中的布局以及如何打印虚函数表.但是有关在虚继承

C++多态篇2——虚函数表详解之从内存布局看函数重载,函数覆盖,函数隐藏

上一篇C++多态篇1一静态联编,动态联编.虚函数与虚函数表vtable中,我在最后分析了虚函数与虚函数表的内存布局,在下一篇详细剖析虚函数及虚函数表的过程中,我发现有关函数重载,函数覆盖,函数重写和函数协变的知识也要理解清楚才能对虚函数表在内存中的布局,对派生类的对象模型以及对多态的实现有更深的理解. 所以这一篇我作为一篇过渡篇,也同时对我以前写过的一篇博文进行一个收尾.在C++继承详解之二--派生类成员函数详解(函数隐藏.构造函数与兼容覆盖规则)文章中,我对函数覆盖,重载,重写提了一下,但是没