C++ 对象模型

nxdong July 23, 2022 [c++] #c++

c++对象模型.

C++ 对象模型

通过两个步骤实现C++对象模型:

  1. A table of pointers to virtual functions is generated for each class (this is called the virtual table). 为每一个类生成一个由指向虚函数的指针们组成的虚函数表.
  2. A single pointer to the associated virtual table is inserted within each class object (traditionally, this has been called the vptr). The setting, resetting, and not setting of the vptr is handled automatically through code generated within each class constructor, destructor, and copy assignment operator. The type_info object associated with each class in support of runtime type identification (RTTI) is also addressed within the virtual table, usually within the table’s first slot. 一个指向虚函数表的指针会被插入到每一个类对象(一般管这个叫vptr). vptr的setting,resetting或者不设置由每个类的构造函数,析构函数,和拷贝赋值操作生成的代码自动处理. 每个类的type_info 对象(为了支持RTTI)也放在虚函数表里,一般放在第一个槽里.

img

RTTI

它允许检索对象类型信息(使用typeid运算符)以及在运行时检查继承层次结构(使用dynamic_cast)。RTTI仅在存在多态性行为时可用,即类至少有一个虚拟函数。

https://alex-robenko.gitbook.io/bare_metal_cpp/compiler_output/rtti

https://www.geeksforgeeks.org/rtti-run-time-type-information-in-cpp/

clang 对象结构打印

基本类继承

base.cpp 内容如下:

class Base {
protected:
    int a;
public:
    int add(int b) {
        return a + b;
    }
};

struct Point {
    double cx, cy;
};

class Derived : public Base {
public:
    int method(int cc) {
        return aa + bb + cc;
    }
protected:
    int aa, bb;
    Point a_point;
    char c;
};

int main(int argc, char **argv) {
    return sizeof(Derived);
}

命令行输出对象结构:

clang -cc1 -fdump-record-layouts base.cpp
*** Dumping AST Record Layout
         0 | class Base
         0 |   int a
           | [sizeof=4, dsize=4, align=4,
           |  nvsize=4, nvalign=4]

*** Dumping AST Record Layout
         0 | struct Point
         0 |   double cx
         8 |   double cy
           | [sizeof=16, dsize=16, align=8,
           |  nvsize=16, nvalign=8]

*** Dumping AST Record Layout
         0 | class Derived
         0 |   class Base (base)
         0 |     int a
         4 |   int aa
         8 |   int bb
        16 |   struct Point a_point
        16 |     double cx
        24 |     double cy
        32 |   char c
           | [sizeof=40, dsize=33, align=8,
           |  nvsize=33, nvalign=8]

带虚函数的继承

vtable.cpp 文件内容如下:

class Base {
protected:
    int a;
public:
    int add(int b) {
        return a + b;
    }
    virtual ~Base() {}
};

struct Point {
    double cx, cy;
};

class Derived : public Base {
public:
    int method(int cc) {
        return aa + bb + cc;
    }
    ~Derived(){}
protected:
    int aa, bb;
    Point a_point;
    char c;
};

int main(int argc, char **argv) {
    Base* b = new Derived;
    delete b;
    b = nullptr;
    return sizeof(Derived);
}

命令行执行(注意命令行参数的变化):

clang -cc1 -emit-llvm -stdlib=libc++ -fdump-record-layouts -fdump-vtable-layouts  vtable.cpp

*** Dumping AST Record Layout
         0 | class Base
         0 |   (Base vtable pointer)
         8 |   int a
           | [sizeof=16, dsize=12, align=8,
           |  nvsize=12, nvalign=8]

*** Dumping AST Record Layout
         0 | struct Point
         0 |   double cx
         8 |   double cy
           | [sizeof=16, dsize=16, align=8,
           |  nvsize=16, nvalign=8]

*** Dumping AST Record Layout
         0 | class Derived
         0 |   class Base (primary base)
         0 |     (Base vtable pointer)
         8 |     int a
        12 |   int aa
        16 |   int bb
        24 |   struct Point a_point
        24 |     double cx
        32 |     double cy
        40 |   char c
           | [sizeof=48, dsize=41, align=8,
           |  nvsize=41, nvalign=8]

*** Dumping IRgen Record Layout
Record: CXXRecordDecl 0x7fe399868550 <vtable.cpp:1:1, line:12:1> line:1:7 referenced class Base definition
...
这中间有一坨其他的信息. 这里做省略.
...
Layout: <CGRecordLayout
  LLVMType:%class.Derived = type <{ %class.Base.base, i32, i32, [4 x i8], %struct.Point, i8, [7 x i8] }>
  NonVirtualBaseLLVMType:%class.Derived.base = type <{ %class.Base.base, i32, i32, [4 x i8], %struct.Point, i8 }>
  IsZeroInitializable:1
  BitFields:[
]>
Vtable for 'Base' (5 entries).
   0 | offset_to_top (0)
   1 | Base RTTI
       -- (Base, 0) vtable address --
   2 | Base::~Base() [complete]
   3 | Base::~Base() [deleting]
   4 | int Base::subxxx(int)

VTable indices for 'Base' (3 entries).
   0 | Base::~Base() [complete]
   1 | Base::~Base() [deleting]
   2 | int Base::subxxx(int)

Vtable for 'Derived' (5 entries).
   0 | offset_to_top (0)
   1 | Derived RTTI
       -- (Base, 0) vtable address --
       -- (Derived, 0) vtable address --
   2 | Derived::~Derived() [complete]
   3 | Derived::~Derived() [deleting]
   4 | int Derived::subxxx(int)

VTable indices for 'Derived' (3 entries).
   0 | Derived::~Derived() [complete]
   1 | Derived::~Derived() [deleting]
   2 | int Derived::subxxx(int)

备注

gcc打印虚表:

g++ -fdump-class-hierarchy vtable.cpp

参考资料

https://alex-robenko.gitbook.io/bare_metal_cpp/compiler_output/rtti

https://www.geeksforgeeks.org/rtti-run-time-type-information-in-cpp/

https://eli.thegreenplace.net/2012/12/17/dumping-a-c-objects-memory-layout-with-clang/

https://www.cnblogs.com/xhb19960928/p/11720314.html