小言_互联网的博客

Visual Studio查看虚函数表&C++内存模型

450人阅读  评论(0)



在其他选项这里写上/d1 reportAllClassLayout,它可以看到所有相关类的内存布局,如果写上/d1 reportSingleClassLayoutXXX(XXX为类名),则只会打出指定类XXX的内存布局。近期的VS版本都支持这样配置。

运行程序的话就会自动生成一张虚函数表了:

#include <iostream>
using namespace std;

class Base {
   
public:
	int a = 0;
	int b = 0;
	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 {
   
	void g() {
    cout << "Derive::g" << endl; }
	virtual void g1() {
    cout << "Derive::g" << endl; }
};

int main()
{
   
	
	system("pause");
	return 0;
}

 

这个内存结构图分成了两个部分,上面是内存分布,下面是虚表。

单一继承(含成员变量+虚函数+虚函数覆盖)


通过代码查看的虚函数表是这样的:

多继承(含成员函数+虚函数+虚函数覆盖)


2个int型,一个short型(2字节padding后占4个字节),2个虚函数表,所以长度为5*4 = 20;虚函数表是这样的:

内存布局是这样:

深度为2的继承(成员变量+虚函数+虚函数覆盖)


3个int型,1个short型(2字节padding后占4个字节),2个虚函数表;代码显示的类的布局是这样:

内存布局:

class A {
   
public:
	virtual void f1() {
    cout << "A:f1" << endl; };
	virtual void f2() {
    cout << "A:f2" << endl; };
	virtual void f3() {
    cout << "A:f3" << endl; };
};

class B {
   
public:
	virtual void g1() {
    cout << "B:g1" << endl; };
	virtual void g2() {
    cout << "B:g2" << endl; };
	virtual void f2() {
    cout << "B:f2" << endl; };
};

class C :public A, public B {
   
	virtual void f1() {
    cout << "C:f1" << endl; };
	virtual void g1() {
    cout << "C:g1" << endl; };
};

class D :public C {
   
	virtual void f1() {
    cout << "D:f1" << endl; };
	virtual void g2() {
    cout << "D:g2" << endl; };
};

 

显示的内存分布是这样的:

重复继承(含成员变量+虚函数+虚函数覆盖)



由于基类中的m_nAge在内存分布中出现了两次,所以最后的结果是5个int类型和2个虚函数表,共计28字节。

单一虚继承(含成员变量+虚函数+虚函数覆盖)


所谓的虚继承就是把继承语法前加上virtual关键字,例如class B:virtual public A{…};
虚拟继承的出现就是为了解决重复继承中多个间接父类(可以解决菱形问题)的问题的 。内存分布是这样的:

这里需要解释下,因为出现了vfptr与vbptr,vfptr常见,但是vbptr却是第一次见,它指向CChildren的vbtable,另一个CChildren的vfptr位于0地址偏移处,它指向CChildren的vftable。从截图中也可以看出有2个vftable与1个vbtable(一共3个表)。vbtable中的8表示vbptr与基类的vfptr(这里即_vfptr::CParent)之间的偏移量(记录了基类vfptr的地址,即二重指针)。

另外提及一下,如果CChildren里全部是重载基类中的虚函数的话,或者说没有新的虚函数的话,_vfptr::CChildren指向的虚函数表就是空的,所以计算大小的时候可以不用算进去,因为实际上并没有创建相应的表格。
举个例子:

class A {
   
public:
	virtual void f1() {
    cout << "A:f1" << endl; };
	virtual void f2() {
    cout << "A:f2" << endl; };
	virtual void f3() {
    cout << "A:f3" << endl; };
};

class B:virtual A {
   
public:
	//virtual void g1() { cout << "B:g1" << endl; }; //如果打开注释,那么B就会有自己的vfptr
	void f2() {
    cout << "B:f2" << endl; };
	void f3() {
    cout << "B:f3" << endl; };
};

内存分布为:

即此时B类没有自己的vfptr。

多虚继承(含成员变量+虚函数+虚函数覆盖)

(1)CParent2是虚继承,CParent1是一般继承



(2)CParent1是虚继承,CParent2是一般继承


(3)CParent1和CParent2都是虚继承



从这里可以看出vbtable确实是存储了指向相应的基类的vfptr的地址(二重指针)。

菱形的虚拟多重继承(含成员变量+虚函数+虚函数覆盖)




转载:https://blog.csdn.net/ShenHang_/article/details/128531696
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场