C++对象模型

发布于:2025-07-27 ⋅ 阅读:(12) ⋅ 点赞:(0)

C++对象模型是C++语言中关于对象如何在内存中布局和如何工作的底层机制。理解C++对象模型对于编写高效、正确的C++代码非常重要。

基本概念

  1. 对象内存布局:C++对象在内存中的组织方式

  2. 虚函数机制:实现运行时多态的基础

  3. 继承模型:单继承、多继承和虚继承的实现

  4. 成员访问:成员变量和成员函数的访问机制

不含有有虚函数的对象模型

对于不包含虚函数的简单类:

内存布局:

  • 成员变量按声明顺序排列

  • 成员函数不占用对象空间(存储在代码区)

 空类的大小:

  • 如果一个类没有任何数据成员(包括静态成员),它的大小通常是 1字节(具体取决于编译器和内存对齐规则)。

带有虚函数的对象模型

当类包含虚函数时,编译器会添加一个虚函数表指针(vptr):

内存布局:

  1. 虚表指针(vptr)指向虚函数表(vtable)

  2. 然后是成员变量

  3. 虚函数表中存储着虚函数地址

 

继承模型

单继承

 1、对一般继承而言,若子类重写(overwrite)了父类的虚函数,则子类虚函数将覆盖虚表中对应的父类虚函数(注意子类与父类拥有各自的一个虚函数表);

2、若子类并无overwrite父类虚函数,而是声明了自己新的虚函数,则该虚函数地址将扩充到虚函数表最后。

3、而对于虚继承,若子类overwrite父类虚函数,同样地将覆盖父类子物体中的虚函数表对应位置,而若子类声明了自己新的虚函数,则编译器将为子类增加一个新的虚表指针vptr,这与一般继承不同。

多继承 

1、一般多继承 (非菱形继承)

        如果继承于多个基类,且这个多个基类中有虚函数,那么 这个类 会有多个虚函数表。

这个类的对象会有多个虚函数表指针。

         子类的虚函数被放在声明的第一个基类的虚函数表中。

        overwrite时,所有基类的同名虚函数都被子类的同名虚函数覆盖。这样做就是为了解决不同的基类类型的指针指向同一个子类实例,而能够调用到实际的函数。

        内存布局中,父类按照其声明顺序排列。

2、菱形继承
class A { int data; };
class B : public A { int b_data; };
class C :public A { int c_data; };
class D :public B, public c { int d_data; };

 

 问题:D的对象会包含两份 A 的成员。通过D 访问A 的成员 data 会编译错误,需要通过 B::data 或 C::data 明确路径。

内存布局

 

         相较于一般多继承,公共继承类在两个虚表中都有。

3.虚继承
class A { int data; };
class B : virtual public A { int b_data; };
class C : virtual  public A { int c_data; };
class D : public B, public c { int d_data; };

 最终派生类负责构造虚基类
注意:在虚继承的情况下,虚基类的构造由最底层的派生类直接负责,而不是由中间的基类来
构造过程:
1、先造虚基类A
由 D的构造函数直接调用 A的构造函数(B和C的造函数不再调用 A)
2、按照声明顺序构造:先 B,后 C
3、D的自身构造

         解决菱形继承的问题。

this指针调整

在多继承中,当派生类指针转换为基类指针时,可能需要调整this指针的值:

Derived* d = new Derived;
Base2* b2 = d;  // 可能需要调整指针值

对象构造与析构过程

  1. 分配内存

  2. 构造基类子对象

  3. 构造成员对象

  4. 执行构造函数体

  5. 析构顺序相反

运行时类型识别(RTTI)

通过type_info对象实现,通常与虚函数表存储在一起。

性能考虑

  1. 虚函数调用比普通函数调用多一次间接寻址

  2. 多重继承可能增加空间开销

  3. 虚继承会增加访问虚基类成员的开销

理解C++对象模型有助于编写更高效的代码,并更好地理解C++的底层机制。


网站公告

今日签到

点亮在社区的每一天
去签到