C++对象内存模型布局详解

目录

本文主要内容如下:

最后还有一些问题:

一、理解虚函数表

二、对象模型概述

三、继承下的C++对象模型

单继承:

多继承:

一般的多继承(非菱形继承):

菱形继承:

五、虚继承

5.1虚基类表解析:

5.2简单虚继承

5.3虚拟多继承

5.4虚拟菱形继承

六、C++封装带来的布局成本是多大?

七、下面这个空类构成的继承层次中,每个类的大小是多少?


由于本人在查找答案时,发现绝大多数博文都是相同的,并且不全,所以我将各站中写的比较好的博文综合起来,补全了答案,并对一些不必要的内容进行了简化,以下是我参考的一些博文链接:

图说C++对象模型:对象内存布局详解 - melonstreet - 博客园 (cnblogs.com)

图解C++对象模型,看这一篇就够了 - 知乎 (zhihu.com)

C++中类所占内存,父类与子类所占内存大小的关系(详细记忆)_c++中虚函数子类和父类的大小为什么会一样-CSDN博客

C++类的大小计算汇总 - 冯耀耀 - 博客园 (cnblogs.com)

C++中涉及到虚函数成员、静态成员、虚继承、多继承、空类等。

类作为一种类型定义,是没有大小可言的。

类的大小指的是类实例化出的对象的大小。因此,用sizeof对一个类型名操作,得到的是具有该类型实体的大小。

规律综合:

  • 类大小的计算,遵循结构体的对齐规则。
  • 类的大小与数据成员有关,与成员函数和静态成员无关。即普通成员函数,静态成员函数,静态数据成员,静态常量数据成员,均对类的大小没有关系。
  • 虚函数对类的大小有影响,是因为虚函数表指针带来的影响。
  • 虚继承对类的大小有影响,是因为虚基表指针带来的影响。
  • 静态数据成员之所以不计算在类的对象大小内,是因为类的静态数据成员被该类的对象所共享,并不属于具体的哪一个对象,静态数据成员定义在内存的全局区。
  • 空类的大小为1,含有虚函数,虚继承,多继承是特殊情况。

本文主要内容如下:

  1. 虚函数表解析。含有虚函数或其父类含有虚函数的类,编译器都会为其添加一个虚函数表、vptr,先了解虚函数表的构成,有助于对C++对象模型的理解。
  2. 虚基类表解析。虚继承产生虚基类表(vbptr),虚基类表的内容与虚函数表完全不同。
  3. 对象模型的概述:介绍简单对象模型,表格驱动对象模型,以及非继承情况下的C++对象模型。
  4. 继承下的C++对象模型。分析C++类对象在以下情况的内存布局:
  • 单继承:子类单一继承自父类,分析了子类重写父类虚函数、子类定义了新的虚函数情况下子类对象内存布局
  • 多继承:子类继承于多个父类,分析了子类重写父类虚函数、子类定义了新的虚函数情况下子类对象内存布局,同时分析了非虚继承下的菱形继承。
  • 虚继承:分析了单一继承下的虚继承、多重基层下的虚继承、重复继承下的虚继承。

最后还有一些问题:

C++封装带来的布局成本多大?

由空类组成的继承层次中,每个类对象的大小是多大?

一、理解虚函数表

C++中虚函数的作用主要是为了实现多态机制。多态,简单来说是指在继承层次中,父类的指针可以具有多种形态,当它指向某个子类对象时,通过它能够调用到子类的函数,而非父类的函数。这是一种运行期多态,即父类指针唯有在程序运行时才能知道所指的真正类型是什么。而这种决议是通过虚函数表来实现的。

当一个类本身定义了虚函数,或其父类有虚函数时,为了支持多态机制,编译器将为该类添加一个虚函数指针(vptr)。虚函数指针一般都放在内存布局的第一个位置上,这是为了保证在多层继承或多重继承的情况下能以最高效率取到虚函数表。

虚函数表指针指向虚函数表,虚函数表中存储的是一系列虚函数的地址,虚函数地址出现的顺序与类中虚函数声明的顺序一致。取到的虚函数表的地址也即是虚函数表第一个虚函数的地址。

二、对象模型概述

在C++中有两种数据成员(class data members):static 和 nonstatic,以及三种类成员函数(class member function):static、nonstatic 和 virtual。现在我们有一个类Base,包含了上面五种类型的数据或函数:

class Base
{
public:
 
    Base(int i) :baseI(i){};
  
    int getI(){ return baseI; }
 
    static void countI(){};
 
    virtual ~Base(){}

    virtual void print(void){ cout << "Base::print()"; }

    
 
private:
 
    int baseI;
 
    static int baseS;
};

类图如下:

在非继承下的C++对象模型下:<