简单对象
- 非静态成员变量和虚函数是决定类大小的唯一两个因素
- 非静态成员变量在类的内存里会有字节对齐
- 如果对象中包含虚函数,会增加4个字节的空间,不论有多少个虚函数。
- 静态成员变量,静态成员函数和非静态成员函数不会影响对象内存的大小。静态成员变量存储在内存的静态数据区。另外:非静态成员函数和程序普通函数的函数区别是,类的非静态成员函数函数有this指针,所以可以访问类的成员。静态成员函数不可以调用类的非静态成员,因为静态成员函数不含this指针。
class simpleClass
{
public:
char c, d;
double nValue;
int i;
static int nCount;
simpleClass() {};
virtual ~simpleClass() {};
int getValue(void);
virtual void foo(void) {};
static void addCount() {};
};
int main()
{
std::cout << "sizeof(simpleClass):" << sizeof(simpleClass) << std::endl;
std::cout << "offsetof(simpleClass, c):" << offsetof(simpleClass, c) << std::endl;
std::cout << "offsetof(simpleClass, d):" << offsetof(simpleClass, d) << std::endl;
std::cout << "offsetof(simpleClass, nValue):" << offsetof(simpleClass, nValue) << std::endl;
std::cout << "offsetof(simpleClass, i):" << offsetof(simpleClass, i) << std::endl;
std::cin.get();
}
输出:
sizeof(simpleClass):32
offsetof(simpleClass, c):8
offsetof(simpleClass, d):9
offsetof(simpleClass, nValue):16
offsetof(simpleClass, i):24
因为double是8个字节,所以按照8字节对齐,虚函数表占8字节,两个char一起占8字节,int占8字节,总大小是32。
单继承
class simpleClass
{
public:
static int nCount;
int nValue;
char c;
simpleClass(){};
virtual ~simpleClass(){};
int getValue(void);
virtual void foo(void){};
static void addCount(){};
};
class derivedClass:public simpleClass
{
public:
int nSubValue;
derivedClass(){};
~derivedClass(){};
virtual void foo(void){};
};
在构造一个派生类的实例时首先够着一个基类的实例,而这个基类的实例在派生类的实例销毁之后销毁。derivedClass的大小是16字节,基类simpleClass的大小是12字节。派生类增加了一个整型变量nSubValue,在内存布局中放在基类的后面。而且派生类在构造时不会再创建一个新的虚函数表,而是在基类的虚函数表中增加或者修改。
多继承
class simpleClass1
{
public:
int nValue1;
char c;
simpleClass1(){};
virtual ~simpleClass1(){};
int getValue(void);
virtual void foo1(void){};
};
class simpleClass2
{
public:
int nValue2;
simpleClass2(){};
virtual ~simpleClass2(){};
int getValue(void);
virtual void foo2(void){};
};
class derivedClass:public simpleClass1,public simpleClass2
{
public:
int nSubValue;
derivedClass(){};
~derivedClass(){};
virtual void foo2(void){};
};
- 与单继承相同,创建派生类的对象时,要首先创建基类的对象。由于多继承中一个派生类有多个基类,因此创建基类的对象要遵循派生类声明的顺序。
- simpleClass1对象的大小是12字节,simpleClass2对象的大小是8字节。而派生类增加了4个字节的整型成员数据,大小是24字节。
- 在多继承中药注意避免二义性。如上面的例子两个基类都定义了getValue()函数。如果一个派生类的实例调用getValue函数会报二义性错误。
- 若simpleClass1和simpleClass2的内存对齐不一样,那么派生类继承他们以后,会重新统一内存对齐。* 比如若simpleClass1是按4字节对齐,simpleClass2是按8字节对齐。当derivedClass继承他们以后,simpleClass1也会按照8字节对齐。
菱形继承
考虑如下的菱形继承
如果不适用虚拟继承,内存布局将会如上面右图所示:会有两个baseClass实例。
如果使用虚拟继承,则内存布局如下
使用虚拟继承以后, baseClass只创建了一个实例,并且放在derivedClass实例的内存空间中的最后,每个使用虚拟继承的类都会增加一个虚基类表的指针(virtual base table)。