C++ 多态与虚函数表(五)拓展—— 对象大小

本文详细解析了C++中不同类结构(包括单继承、多继承、重复继承和虚继承)的对象大小计算原理及虚函数表布局。通过具体实例代码,展示了各类在64位环境下G++4.6.4版本编译器中的实际大小,并深入分析了内存布局和虚函数表的构成。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、前言

前面文章中分析了对象内存布局,对象大小已经不难理解了,这里集中总结一下。

个人笔记,不保证严谨和正确☺,如有错误还望指出!

《C++ 多态与虚函数表(一)—— 单继承》

《C++ 多态与虚函数表(二)—— 多继承》

《C++ 多态与虚函数表(三)—— 重复继承》

《C++ 多态与虚函数表(四)—— 虚继承》

《C++ 多态与虚函数表(五)拓展—— 对象大小》

 

二、实例代码

2.1 代码

运行环境 64位,G++ 4.6.4版本

#include<iostream>
#include<string>
using namespace std;

class A {};		// 1
class B 		// 16
{
public:
	int bv;
	virtual void f() { cout << "B::f()" << endl; }
	B(): bv(1) {}
};

class C		// 16
{
public:
	int cv;
	virtual void h() { cout << "C::h()" << endl; }
	C(): cv(2) {}
};

class D : public B //16
{
public:
	int dv;
	virtual void f() { cout << "D::f()" << endl; }
	D(): dv(3) {}
};

class E : public B //16
{
public:
	int ev;
	virtual void f() { cout << "E::f()" << endl; }
	E(): ev(4) {}
};

class F : public B, public C  // 32
{
public:
	int fv;
	virtual void g() { cout << "F::g()" << endl; }
	F(): fv(5) {}
};

class G : public D, public E  // 40
{
public:
	int gv;
	virtual void g() { cout << "G::g()" << endl; }
	G(): gv(6) {}
};

class H : virtual public B  // 32
{
public:
	int hv;
	virtual void g() { cout << "H::g()" << endl; }
	H(): hv(7) {}
};

class I : virtual public B  // 32
{
public:
	int iv;
	virtual void g() { cout << "I::g()" << endl; }
	I(): iv(8) {}
};

class J : public H, public I  // 48
{
public:
	int jv;
	virtual void jf() { cout << "J::jf()" << endl; }
	J(): jv(9) {}
};

class K : virtual public B, virtual public C  // 48
{
public:
	int kv;
	virtual void g() { cout << "K::g()" << endl; }
	K(): kv(10) {}
};

// 测试
void test()
{
    cout << "szieof(A): " << sizeof(A) << endl;
    cout << "szieof(B): " << sizeof(B) << endl;
    cout << "szieof(C): " << sizeof(C) << endl;
    cout << "szieof(D): " << sizeof(D) << endl;
    cout << "szieof(E): " << sizeof(E) << endl;
    cout << "szieof(F): " << sizeof(F) << endl;
    cout << "szieof(G): " << sizeof(G) << endl;
    cout << "szieof(H): " << sizeof(H) << endl;
    cout << "szieof(I): " << sizeof(I) << endl;
    cout << "szieof(J): " << sizeof(J) << endl;
    cout << "szieof(K): " << sizeof(K) << endl;
}

// 测试 J
void testJ()
{
    J b;
    cout << ">>>>>>sizeof(J): " << sizeof(J) << endl;

    typedef void(*ptrType)(void);
    ptrType pFun = NULL;
    ptrType** pVtab = (ptrType**)&b;

    ptrType* pHead = (ptrType*)(&b);        // pHead: 指向的是对象空间首地址(即,虚函数表地址)
    ptrType* pVptr = (ptrType*)(*pHead);    // pVptr: 虚函数表第一个函数地址

    int index = 0;  // 对象内存访问下标
    auto fun = [&](string strName, string strFunName, int maxj) 
    {
    	ptrType* pVptr = (ptrType*)(*pHead);
        cout << "[" << index << "] " << strName <<"::vptr->" << endl;
        for (int j = 0; j < maxj; ++j)
        {
            pFun = (ptrType)*(pVptr+j);
            cout << "   [" << j <<"] ";
            pFun(); 
        }
        pHead = pHead + 1;
        ++index;

        cout << "[" << index << "] " << strFunName << " = " << (int)*((int *)pHead) << endl; 
    };

    fun("H", "H::hv", 2);
    pHead = pHead + 1; ++index; 

    fun("I", "I::iv", 1);
    pHead = (ptrType*)((int *)pHead + 1); 

	cout << "[" << index << "] I::hv = " << (int)*((int *)pHead) << endl; 
    pHead = (ptrType*)((int *)pHead + 1); ++index; 

    fun("B", "B::bv", 1);
    pHead = pHead + 1; ++index; 
}

// 测试 K
void testK()
{
    K b;
    cout << ">>>>>>sizeof(K): " << sizeof(K) << endl;

    typedef void(*ptrType)(void);
    ptrType pFun = NULL;
    ptrType** pVtab = (ptrType**)&b;

    ptrType* pHead = (ptrType*)(&b);        // pHead: 指向的是对象空间首地址(即,虚函数表地址)
    ptrType* pVptr = (ptrType*)(*pHead);    // pVptr: 虚函数表第一个函数地址

    int index = 0;  // 对象内存访问下标
    auto fun = [&](string strName, string strFunName, int maxj) 
    {
    	pVptr = (ptrType*)(*pHead); 		// pVptr: 虚函数表第一个函数地址
        cout << "[" << index << "] " << strName <<"::vptr->" << endl;
        for (int j = 0; j < maxj; ++j)
        {
            pFun = (ptrType)*(pVptr+j);
            cout << "   [" << j <<"] ";
            pFun(); 
        }
        pHead = pHead + 1;
        ++index;

        cout << "[" << index << "] " << strFunName << " = " << (int)*((int *)pHead) << endl; 
        pHead = pHead + 1; ++index; 
    };

    fun("K", "K::kv", 1);
    fun("B", "B::bv", 1);
    fun("C", "C::cv", 1);
}

int main()
{
    test();
    testJ();
    testK();
    return 0;
}

2.2 执行结果

2.3 分析

A:空类需要有一个地址标记。大小为 1。

B:一个虚表指针(8),和一个 int(B::bv) 需要内存对齐(8)。大小为 16 = 8 + 8。

C:同B。大小为 16。

D:一个虚函数表指针(8),虚函数表记录的是(D::f()),B类中一个int(B::bv)对象,D类中一个int(D::dv)对象。大小为 16 = 8 + 4 + 4。

E:同D。大小为16。

F:一个虚函数指针(8),虚函数表记录的是(B::f(), F::g()),B类中一个 int(B::bv) 需要内存对齐(8), 一个虚函数指针(8),虚函数表记录的是(C::h()),C类中一个 int(C::cv)(4),F类中一个 int(F::fv)(4)。大小为 32 = 8 + 8 + 8 + 4 + 4。

G:一个虚函数指针(8),虚函数表记录的是(D::f(), G::g()),B类中一个 int(B::bv)(4),D类中一个 int(D::dv)(4),一个虚函数指针(8),虚函数表记录的是(E::f()),B类中一个 int(B::bv)(4),类中一个 int(E::dv)(4),G类中一个 int(G::gv) 需要内存对齐(8)。大小为 40 = 8 + 4 + 4 + 8 + 4 + 4 + 8。

H:一个虚函数指针(8),虚函数表记录的是(H::g()),H类中一个 int(H::hv) 需要内存对齐(8),一个虚函数指针(8),虚函数表记录的是(B::f()),B类中一个 int(B::bv) 需要内存对齐(8)。大小为 32 = 8 + 8 + 8 + 8。

I:同H。大小为 32。

J:一个虚函数指针(8),虚函数表记录的是(H::g(), J::jf()),H类中一个 int(H::hv) 需要内存对齐(8),一个虚函数指针(8),虚函数表记录的是(I::g()),I类中一个 int(I::iv)(4),J类中一个 int(J::jv)(4), 一个虚函数指针(8),虚函数表记录的是(B::f()),B类中一个 int(B::bv) 需要内存对齐(8)。大小为 48 = 8 + 8 + 8 + 4 + 4 + 8 + 8。

K:一个虚函数指针(8),虚函数表记录的是(K::g()),K类中一个 int(K::kv) 需要内存对齐(8),一个虚函数指针(8),虚函数表记录的是(B::f()),B类中一个 int(B::bv) 需要内存对齐(8),一个虚函数指针(8),虚函数表记录的是(C::h()),C类中一个 int(C::cv) 需要内存对齐(8)。大小为 48 = 8 + 8 + 8 + 8 + 8 + 8。

J 内存布局和虚函数表

 K 内存布局和虚函数表

 

 

 

 

 

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值