C++对象模型(一)
c++ 对象模型:
1,non-static 成员变量存储于每一个类对象中。它是属于对象的
2,static 成员变量存储于所有类对象之外。它不属于对象,而是属于类的,即没有创建类对象,这个成员也是存在的。
3,static 和 non-static 成员函数存储于所有类对象之外,它们是属于类的,即没有创建类对象,这两类成员也是存在的。
可以看出一个类对象的大小,取决于这个类的 non-static 成员变量,类对象大小肯定是来自于类,所以类的大小取决于类的 non-static 成员变量。
虚函数:
1,每一个类产生出一堆指向虚函数的指针(如果有虚函数),放在表格之中,这个表格被称为 virtual table(vtbl)
2,每一个类对象被添加一个指针,指向相关的虚表。这个指针被称为 vptr
所以一个类对象大概的内存模型是这样的:
这是书上的一个例子。这样可以直观的看出,这个类有一个non-static 成员 float _x,一个 static 成员 int _point_count, 一个static 函数 int PointCount(),两个non-static 函数,其中一个是构造函数,一个是普通函数 float x() ,而虚表指针是隐形的,指向了一个虚函数表,表里都是虚函数的地址,即表里都是指针(就是函数指针,即函数入口)。那实际的 C++ 对象模型是怎样的呢?下面看一个简单的例子:
#include <stdio.h>
#include <stdlib.h>#pragma pack(4)
class CTest
{
public:CTest(): mValue(200){}virtual ~CTest(){}private:int mValue;
};
#pragma pack()int main()
{CTest test;printf("sizeof CTest is %llu\n", sizeof(test));return 0;
}
使用GDB查看一个这个类对象内存模型里到底有哪些东西:
打印出对象的内存地址,然后我们就可以看看内存里保存了哪些信息:
为什么打印内存地址后 12 个字节呢?因为这个类的大小是 12 字节,上面用了 #pragma pack(4)设置了 4 字节对齐,如果不设置的话,可能默认就是 8 字节对齐了(因为机器是64位的,指针长度就是 8 字节)。这样看来,前 8 个字节是指向虚表的指针,后 4 个字节就是成员变量 mValue = c8(十进制为 200)。虚表指针指向的是虚表的首地址,所以我们也可以看看虚表里保存了哪些函数指针:
其实从源码能看到只有一个虚函数,所以虚表应该只有一个函数指针,正好前 8 个字节。
但实际看来有两个函数指针,但都是指向 CTest::~CTest() 函数,这里不是很理解,有同学知道的话告知一下。
#include <stdio.h>
#include <stdlib.h>#pragma pack(4)
class CTest
{
public:CTest(): mValue(200){}virtual ~CTest(){}virtual void print(){printf("mValue = %d\n", mValue);}
private:int mValue;
};
#pragma pack()int main()
{CTest test;printf("sizeof CTest is %llu\n", sizeof(test));return 0;
}
例子中多加了一个虚函数,我们再来看一下内存情况:
0x004007e0 就是 CTest::print() 函数入口,既然是函数,我们就可以直接调用了,如:
到这里唯一不理解的是有 2 个函数指针指向了 CTest::~CTest(),有同学知道告知一下。