C++对象内存模型
一、概述
在编译时,编译器会对成员函数进行重构,让成员函数非成员化,编译器安排this指针作为成员函数的第一个参数,通过this指针可以找到对象的数据成员。
二、有虚函数单继承实验
1. 测试文件
#include <iostream> using namespace std; /* 类大小16B,普通成员函数不占类的内存空间 */ class Base_1 { public: int a; int b; int c; int d; int func_add(int x, int y) { return x + y; } }; /* 类大小24B,有虚函数就存在虚函数表指针 */ class Base_2 { public: int a; int b; int c; int d; virtual int func_add(int x, int y) { return x + y; } }; /* 类大小16B,类的静态成员变量不占用类的空间 */ class Base_3 { public: int a; int b; static int c; static int d; virtual int func_add(int x, int y) { return x + y; } }; int Base_3::c = 0x5566; int Base_3::d = 0x7788; typedef int (*func_t)(void *p, int x, int y); class Son_2 : public Base_2 { public: int e; int f; virtual int fun_sub(int x, int y) { return y - x; } }; int main() { Base_1 b_1; Base_2 b_2; Base_3 b_3; Son_2 s_2; /* 看每个类和对象的大小 */ cout << "sizeof(Base_1)=" << sizeof(Base_1) << " sizeof(b_1)=" << sizeof(b_1) << endl; //sizeof(Base_1)=16 sizeof(b_1)=16 cout << "sizeof(Base_2)=" << sizeof(Base_2) << " sizeof(b_2)=" << sizeof(b_2) << endl; //sizeof(Base_2)=24 sizeof(b_2)=24 cout << "sizeof(Base_3)=" << sizeof(Base_3) << " sizeof(b_3)=" << sizeof(b_3) << endl; //sizeof(Base_1)=16 sizeof(b_1)=16 cout << "sizeof(Son_2) =" << sizeof(Son_2) << " sizeof(s_2)=" << sizeof(s_2) << endl; //sizeof(Son_2) =32 sizeof(s_2)=32 /* 从基类虚函数表中调函数 */ unsigned long **t_ptr_1 = (unsigned long **)(*(unsigned long *)&b_2); int r_b_2_1 = ((func_t)t_ptr_1[0])(&b_2, 0x11, 0x33); cout << hex << "r_b_2_1=0x" << r_b_2_1 << endl; //r_b_2_1=0x44 /* 看对象成员内存分布 */ s_2.a = 0x1122; s_2.b = 0x3344; s_2.c = 0x5566; s_2.d = 0x7788; s_2.e = 0x99aa; s_2.f = 0xbbcc; int *p = reinterpret_cast<int *>(&s_2); for (int i = 0; i < sizeof(s_2)/sizeof(int); i++) { //前8字节是虚函数表指针,之后按定义次序的父类成员变量,子类成员变量 ######## cout << hex << "0x" << p[i] << endl; } /* 看地址 */ cout << hex << "&s_2 =" << &s_2 << endl; cout << hex << "&s_2.a=" << &s_2.a << endl; cout << hex << "&s_2.b=" << &s_2.b << endl; cout << hex << "&s_2.c=" << &s_2.c << endl; cout << hex << "&s_2.d=" << &s_2.d << endl; cout << hex << "&s_2.e=" << &s_2.e << endl; cout << hex << "&s_2.f=" << &s_2.f << endl; /* 从子类虚函数表中调函数 */ unsigned long **t_ptr_2 = (unsigned long **)(*(unsigned long *)&s_2); int r_s_2_1 = ((func_t)t_ptr_2[0])(&s_2, 0x11, 0x33); int r_s_2_2 = ((func_t)t_ptr_2[1])(&s_2, 0x22, 0x44); cout << "r_s_2_1=" << r_s_2_1 << " r_s_2_2=" << r_s_2_2 << endl; //r_s_2_1=44 r_s_2_2=22 先是父类的虚函数,然后才是子类自己的虚函数 ######## return 0; }
2. 执行结果:
sunfl@XmartOS01:~/tmp/5.cpp_test/6.mem_layout$ ./pp sizeof(Base_1)=16 sizeof(b_1)=16 sizeof(Base_2)=24 sizeof(b_2)=24 sizeof(Base_3)=16 sizeof(b_3)=16 sizeof(Son_2) =32 sizeof(s_2)=32 r_b_2_1=0x44 0x7f500cf0 0x5621 0x1122 0x3344 0x5566 0x7788 0x99aa 0xbbcc &s_2 =0x7ffd2687de50 &s_2.a=0x7ffd2687de58 &s_2.b=0x7ffd2687de5c &s_2.c=0x7ffd2687de60 &s_2.d=0x7ffd2687de64 &s_2.e=0x7ffd2687de68 &s_2.f=0x7ffd2687de6c r_s_2_1=44 r_s_2_2=22
三、有虚函数多继承实验
1. 测试文件
#include <iostream> using namespace std; class Base_1 { public: int a; int b; virtual int fun_1(int x, int y) { return x + y; } }; class Base_2 { public: int c; int d; virtual int fun_2(int x, int y) { return 2 * (x + y); } }; class Son : public Base_1, public Base_2 { public: int e; int f; virtual int fun_sub(int x, int y) { return y - x; } }; typedef int (*func_t)(void *t, int x, int y); int main() { Son s_1; s_1.a = 0x1122; s_1.b = 0x3344; s_1.c = 0x5566; s_1.d = 0x7788; s_1.e = 0x99aa; s_1.f = 0xbbcc; /* 类大小 */ cout << "sizeof(Base_1)=" << sizeof(Base_1) << " sizeof(Son)=" << sizeof(Son) << " sizeof(s_1)=" << sizeof(s_1) << endl; /* 类成员分布 */ int *p = reinterpret_cast<int *>(&s_1); for (int i = 0; i < sizeof(s_1)/sizeof(int); i++) { cout << hex << "0x" << p[i] << endl; } /* 类成员地址 */ cout << hex << "&s_1 =" << &s_1 << endl; cout << hex << "&s_1.a=" << &s_1.a << endl; cout << hex << "&s_1.b=" << &s_1.b << endl; cout << hex << "&s_1.c=" << &s_1.c << endl; cout << hex << "&s_1.d=" << &s_1.d << endl; cout << hex << "&s_1.e=" << &s_1.e << endl; cout << hex << "&s_1.f=" << &s_1.f << endl; /* 第一个虚函数表中的函数 */ unsigned long **ptr = (unsigned long **)(*(unsigned long *)&s_1); int r_1 = ((func_t)ptr[0])(&s_1, 0x11, 0x66); int r_2 = ((func_t)ptr[1])(&s_1, 0x33, 0x44); cout << "r1=" << r_1 << " r_2=" << r_2 << endl; #if 0 /* 不成功,从地址上看,第二个vptr占12B而不是8B */ unsigned long **ptr_1 = (unsigned long **)(*(unsigned long *)((char*)&s_1 + sizeof(Base_1))); int r2_1 = ((func_t)ptr_1[0])(&s_1, 0x11, 0x66); int r2_2 = ((func_t)ptr_1[1])(&s_1, 0x33, 0x44); cout << "r2_1=" << r2_1 << " r2_2=" << r2_2 << endl; #endif }
2. 执行结果:
sizeof(Base_1)=16 sizeof(Son)=40 sizeof(s_1)=40 0xa93cdcb8 //第一个虚函数表指针 0x55df 0x1122 0x3344 0xa93cdcd8 //第二个虚函数表指针 0x55df 0x5566 0x7788 0x99aa 0xbbcc &s_1 =0x7ffed25273e0 &s_1.a=0x7ffed25273e8 &s_1.b=0x7ffed25273ec &s_1.c=0x7ffed25273f8 //和上面相差12字节而不是8字节? &s_1.d=0x7ffed25273fc &s_1.e=0x7ffed2527400 &s_1.f=0x7ffed2527404 r1=77 r_2=11
三、总结
1. 在没有虚函数的情况下,相较于C,C++没有消耗额外的内存,在有虚函数的情况下,类中会多出一个虚函数表指针。
2. 类的成员函数所有对象共享,在内存中只有一份。
3. 类的static成员变量存储上不属于类也不属于类对象,在类和对象的大小中看不到它。
4. 对于继承,按定义次序,先是父类成员变量,之后才是子类成员变量。
5. 对于继承,虚函数表中先是父类的虚函数,然后才是子类的虚函数。
6. 对于多继承,按继承的从左到右的次序依次排布继承得来的成员。
7. 对于多继承,父类都有虚函数的,子类中有多个虚函数表指针。
参考:
C++对象内存模型: https://tangocc.github.io/2018/03/20/cpp-class-memory-struct/
posted on 2024-01-29 17:13 Hello-World3 阅读(42) 评论(0) 编辑 收藏 举报