c++对象模型

最近看了本 《c++ 对象模型》的书,收益良多。讲了c++对象中成员的分布,虚函数表等。

先看图

类的层次结构

img

dev类等成员分布

img

下面是代码,获取成员数据都是通过内存偏移,所以即使是父类的私有方法获成员,都可以访问到,只能说指针太强大了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
typedef void(*Fun)(void);   //void类型的函数指针 //适用于无实例对象,例如全局函数等

// ------------------------------------------------ test1 -------------------------
class Tmp
{
public:
short a;
int b;
double c;
};

class Base
{
public:
Base():base1Num(123) { cout << "Base::Base" << endl; }
virtual ~Base() { cout << "Base::~Base" << endl; }
virtual void f() { cout << "Base::f" << endl; }
virtual void g() { cout << "Base::g" << endl; }
virtual void h() { cout << "Base::h" << endl; }
void ooo(){ cout << "Base::ooo" << endl; }
void ppp(){ cout << "Base::ppp" << endl; }

private:
virtual void j() { cout << "Base::j" << endl; }
int base1Num;
};

class Base2
{
public:
Base2():base2Num(456) { cout << "Base2::Base2" << endl; }
virtual ~Base2() { cout << "Base2::~Base2" << endl; }
virtual void x() { cout << "Base2::x" << endl; }
virtual void y() { cout << "Base2::y" << endl; }
void rrr() { cout << "Base2::rrr" << endl; }
void sss() { cout << "Base2::sss" << endl; }

private:
virtual void z() { cout << "Base2::z" << endl; }
int base2Num;
};

class dev : public Base , public Base2
{
public:
virtual void f() { cout << "dev::f" << endl; }
virtual void k() { cout << "dev::k" << endl; }
virtual void z() { cout << "dev::z" << endl; }

public:
int num;
char* str;
dev* child;
Tmp tmp;
//double price; //加了个8个字节的double,字节对齐时会占用更多字节,对象大小增大
};

void testVirtualTable()
{
//Base b1;
//b1.j(); //compile error

dev d;
d.num = 100;
d.str = "hello world";
//d.child = new dev;
//d.child->num = 500;

d.tmp.a = 31;
d.tmp.b = 777;
d.tmp.c = 888;
//d.f(); //compile error

cout << "虚函数表地址:" << (int*)(&d) << endl;
cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&d) << endl;
printf("\n");

//通过函数指针访问到私有的j(), j()对于对象来讲本来是不可见的,指针太强大
Fun pFun2 = nullptr;
//第一个虚函数表指针指向
//pFun2 = (Fun)*((int*)*(int*)(&d) + 0); //Base::~Base //析构不能调
//pFun2();
pFun2 = (Fun)*((int*)*(int*)(&d) + 1); //dev::f //dev重写Base的f
pFun2();
pFun2 = (Fun)*((int*)*(int*)(&d) + 2); //Base::g
pFun2();
pFun2 = (Fun)*((int*)*(int*)(&d) + 3); //Base::h
pFun2();
pFun2 = (Fun)*((int*)*(int*)(&d) + 4); //Base::j
pFun2();
pFun2 = (Fun)*((int*)*(int*)(&d) + 5); //dev::k
pFun2();
//Base base1Num的存储偏移在虚函数表指针的下4个字节
int base1Num = (int)*((int*)(&d) + 1);
printf("--- base1Num:%d\n", base1Num); //123
printf("\n");

//第二个虚函数表指针指向
//pFun2 = (Fun)*((int*)*((int*)(&d) + 1) + 0); //Base2::~Base2 //析构不能调
//pFun2();
pFun2 = (Fun)*((int*)*((int*)(&d) + 2) + 1);//Base2::y
pFun2();
pFun2 = (Fun)*((int*)*((int*)(&d) + 2) + 2); //Base2::y
pFun2();
pFun2 = (Fun)*((int*)*((int*)(&d) + 2) + 3); //dev::z //dev重写Base2的z
pFun2();
//Base2 base2Num的存储偏移在虚函数表指针的下4个字节
int base2Num = (int)*((int*)(&d) + 3);
printf("--- base2Num:%d\n", base2Num); //456
printf("\n");

//通过地址获取成员变量
int num = (int)*((int*)(&d) + 4);
char* str = (char*)*((int*)(&d) + 5);
printf("--- dev.num:%d\n", num);
printf("--- dev.str:%s\n", str);
printf("\n");

//(base vtp + base1num + base2 vtp + base2num + dev::num + dev::str + dev::dev*) * 4 = 28
//printf("--- dev size : %d\n", sizeof(dev)); //28
//如果 dev 加多个 double 型成员,因为字节对齐是更具最大元素来对界,会得到sizeof为40,参考ByteAlign.cpp;

printf("--- Tmp size : %d\n", sizeof(Tmp));
printf("--- dev size : %d\n", sizeof(dev));

printf("--- str:0x%x\n", str);
Tmp* tmp = (Tmp*)&(*((int*)(&d) + 7));
printf("--- tmp:0x%x\n", tmp);
printf("--- tmp.b:%d\n", tmp->b);

short tmpA = (short)*((int*)(&d) + 7);
printf("--- tmpA:%d\n", (int)tmpA);
int tmpB = (int)*((int*)(&d) + 8);
printf("--- tmpB:%d\n", tmpB);
int tmpC = (int)*((int*)(&d) + 9);
printf("--- tmpC:%d\n", tmp->c);

//dev* child = (dev*)*((int*)(&d) + 4);
//printf("--- child num:%d\n", child->num);

//Base* b2 = new dev();
////b2->k(); //compile error,父类指针无法call子类特有的虚函数

////通过函数指针访问到子类特有的虚函数k(), 指针太强大
//Fun pFun3 = (Fun)*((int*)*(int*)b2 + 4);
//pFun3();
}

运行结果

img