上一篇文章里点这里说到了一个类的大小,是单一未继承的类,并没有涉及派生类的大小,而如果是一个派生类呢,它的大小会受基类大小影响吗?
#include <stdio.h>
#include <stdlib.h>
#pragma pack(4)
class CBase
{
public:
CBase():bBaseValue(300)
{}
~CBase()
{}
private:
int bBaseValue;
};
class CTest: public CBase
{
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;
}
基类 CBase 只有一个成员数据 bBaseValue,应该是 4 字节。那它的派生类的大小是多少呢?
这个派生类的大小,包含了基类的大小 4,和自身的大小 12 ,一共 16 个字节。这里涉及到了添加继承后,类的对象模型,我们还是用 GDB 看一看:
0x0000012c (十进制 300) 就是基类的成员数据,从实际内存中我们可以看到派生类里,对象起始位置依然保存的是指向虚表的指针,然后是基类数据,最后才是派生类自身的数据,更形象的图形表示类似这样:
这是基类没有虚函数的情况下,而基类有虚函数的情况,那派生类的内存情况是怎样的呢?
可以看到基类的虚函数放在了虚函数表的第一个位置,其后才是派生类的虚函数。而这种情况是派生类没有重写基类的虚函数,如果派生类重写基类的虚函数呢?
#include <stdio.h>
#include <stdlib.h>
#pragma pack(4)
class CBase
{
public:
CBase():bBaseValue(300)
{}
~CBase()
{}
virtual void print()
{
printf("CBase Value = %d\n", bBaseValue);
}
private:
int bBaseValue;
};
class CTest: public CBase
{
public:
CTest(): mValue(200)
{
}
virtual ~CTest()
{
}
virtual void print()
{
printf("in CTest mValue = %d\n", mValue);
}
private:
int mValue;
};
#pragma pack()
int main()
{
CTest test;
printf("sizeof CTest is %llu\n", sizeof(test));
return 0;
}
派生类重写了基类的 print() 函数,那派生类对象里的内存情况变得如何了呢?
虚函数表里只有 3 个函数指针了,原来基类虚函数的指针被派生类的重写函数覆盖了,这个是不是就是 C++ 的多态实现?
以下情况是基类有 2 个虚函数,派生类重写了其中一个虚函数:
#include <stdio.h>
#include <stdlib.h>
#pragma pack(4)
class CBase
{
public:
CBase():bBaseValue(300)
{}
~CBase()
{}
virtual void basePrint()
{
printf("CBase Value = %d\n", bBaseValue);
}
virtual void print()
{
printf("override Value = %d\n", bBaseValue);
}
private:
int bBaseValue;
};
class CTest: public CBase
{
public:
CTest(): mValue(200)
{
}
virtual ~CTest()
{
}
virtual void print()
{
printf("in CTest mValue = %d\n", mValue);
}
private:
int mValue;
};
#pragma pack()
int main()
{
CTest test;
printf("sizeof CTest is %llu\n", sizeof(test));
return 0;
}
可以看到,虚函数表里第一个永远是基类的虚函数指针,如果被派生类重写了,则用派生类的虚函数指针替换掉基类的虚函数指针,不被重写则依然是基类的虚函数指针。还有更复杂的情况是多重继承、虚继承,还得学习。