c++ 2-对象模型

本文最后更新于 2024年9月24日 下午

[toc]

复合 继承 复合+继承的构造和析构顺序

复合 (has a)

64a4f8a8e2b93d267d72414baa3855c6.png

继承 (is a)

ed1293ad1e1d545a09731f0ba488e02c.png

复合+继承

d9df00d6a7fa11d8fe733430ca08a7c2.png

该类型的构造顺序不受初始化时指定的初始化顺序影响。

1
2
3
4
5
6
7
8
9
10
11
12
13
class B : public A
{
public:
B() : b1(new B1), A(4) //指定先初始化复合,实际先初始化基类
{
std::cout << "B ctor" << std::endl;
}
B(B&) = delete;
~B() { std::cout << "B dtor" << std::endl; delete b1;}

private:
B1* b1;
};

更复杂的类关系可以分解成上述几个简单类型进行分析。

虚指针、虚表、虚机制、多态

f384fe7ebf687bd4750f1e5bb524d8f3.png

  • 只要有 虚函数 就有 虚指针 以及 虚表
  • 那么在64位系统上我们通过(void*)*(unsigned long *)p即可获得vptr指向的vtable的首地址。而vtable其实就是一个指针数组,按照虚函数在类中被声明的顺序排列
  • g++ -fdump-lang-class xxx.cpp会生成一个文件,该文件记录类的内存模型和虚函数表
  • 上面这个图的虚表是简略版,其中还包括一些其他信息,如下所示:
    6d31f8101c456274588fd318a8c40f27.png
    其中,
    1.橙色线框中的内容仅限于虚拟继承的情形(若无虚拟继承,则无此内容)
    2.“offset to top”是指到对象起始地址的偏移值,只有多重继承的情形才有可能不为0,单继承或无继承的情形都为0。
    3.“RTTI information”是一个对象指针,它用于唯一地标识该类型。
    4.“virtual function pointers”也就是我们之前理解的虚函数表,其中存放着虚函数指针列表。

this指针进一步理解

通过一个对象调用一个函数,这个对象的地址就叫做this。

编译器看到符合三个条件就会进行动态绑定,三个条件分别是指针、向上转型、虚函数。下图的向上转型发生在this指针,OnFileOpen函数的this指针类型是CDocument。
2022-03-13\_15-25-17.jpg

dynamic binding

动态绑定就是走虚指针 虚表那条路线,运行时决定,调用什么就看指针指向的是哪个对象。静态绑定编译时就决定了调用哪个函数,地址都写死了。

无虚函数内存布局

一个类没有虚函数的时候,其实就是结构体,它的内存布局就是按照成员变量的顺序来的。
计算占用内存大小,需要注意内存对齐。

有虚函数内存布局

参照上面虚函数的那张图

多重继承 虚继承内存布局

不错的文章

多重继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A {
int a;
};
class B : public A {
int b;
};
class B2 : public A {
int b2;
};
class C : public B, public B2 {
int c;
};
cout << sizeof(A) << endl; //4
cout << sizeof(B) << endl; //8
cout << sizeof(B2) << endl; //8
cout << sizeof(C) << endl; //20

内存布局:
选区\_878.png

进一步参考

深度探索c++对象模型
侯捷c++课程


c++ 2-对象模型
https://leelewin.github.io/p/75768c137f7441b0ae570f54167d5536/
作者
liminglei
发布于
2022年3月12日
许可协议
BY_NC_SA