diff --git a/images/20240409_c++_object_model_inherit.png b/images/20240409_c++_object_model_inherit.png new file mode 100644 index 0000000000..eb560c88d4 Binary files /dev/null and b/images/20240409_c++_object_model_inherit.png differ diff --git a/source/_drafts/c-object-model.md b/source/_drafts/c-object-model.md index f7e26e8c7e..ce6bcfe571 100644 --- a/source/_drafts/c-object-model.md +++ b/source/_drafts/c-object-model.md @@ -251,6 +251,7 @@ int main() { ## 类继承的内存布局 当然,既然是在聊面向对象的类,那就少不了继承了。我们还是从具体例子来看看,在继承情况下,类的内存布局情况。 + ### 不带虚函数的继承 先来看看不带虚函数的继承,示例代码如下: @@ -286,14 +287,17 @@ int main() { } ``` -编译运行后,用 GDB 打印成员变量的内存分布,发现 `Derived` 类的对象在内存中的布局首先包含其基类`Basic`的所有成员变量,紧接着是 Derived 类自己的成员变量。 +编译运行后,用 GDB 打印成员变量的内存分布,发现 `Derived` 类的对象在内存中的布局首先包含其基类`Basic`的所有成员变量,紧接着是 Derived 类自己的成员变量。整体布局如下图: + +![继承类的内存布局](https://slefboot-1251736664.file.myqcloud.com/20240409_c++_object_model_inherit.png) +其实 C++ 标准并没有规定在继承中,基类和派生类的成员变量之间的排列顺序,编译器可以自由发挥的。但是大部分编译器在实现中,都是基类的成员变量在派生类的成员变量之前,为什么这么做呢?因为这样实现,**使对象模型变得更简单和直观。不论是基类还是派生类,对象的内存布局都是连续的,简化了对象创建、复制和销毁等操作的实现。**我们通过派生类对象访问基类成员与直接使用基类对象访问时完全一致,一个派生类对象的前半部分就是一个完整的基类对象。 对于成员函数(包括普通函数和静态函数),它们不占用对象实例的内存空间。不论是基类的成员函数还是派生类的成员函数,它们都存储在程序的代码段中(.text段)。 ### 带有虚函数的继承 -带有虚函数的继承,稍微有点复杂了。 +带有虚函数的继承,稍微有点复杂了。在前面继承例子基础上,增加一个虚函数,然后在 main 中用多态的方式调用。 ```c++ #include @@ -339,6 +343,8 @@ int main() { } ``` +这种情况下,对象的内存布局是什么样?虚函数的多态调用又是怎么实现的呢? + ## 地址空间布局随机化 @@ -360,3 +366,4 @@ int main() { ## 总结 +