背景
C++ 中最基本的存储单位是字节,C++ 中所有的数据都是由对象组成的,每一个对象都包含了一个或多个内存位置;普通类的内存大小为其所有非静态成员变量内存大小之和;为提高效率内存中各个数据类型需要按照一定的规则在内存空间上进行排列,而不是挨个的顺序排列。
单一继承不带虚函数
一个派生类对象可以认为是两部分构成:一是继承的基类对应子对象;二是派生类自身定义的成员对象。
class classA
{
public:
char x;
short y;
};
class classB : public classA
{
public:
int c;
};
int main()
{
cout << "classA 大小:" << sizeof(classA) << endl;
cout << "classB 大小:" << sizeof(classB) << endl;
system("pause");
return 0;
}
代码运行结果如下:
继承关系下的 this 指针
派生类继承的基类子对象的指针和派生类自身的指针相同:
class classA
{
public:
char x;
short y;
public:
classA()
{
cout << "classA() this " << this << endl;
}
};
class classB : public classA
{
public:
int c;
classB()
{
cout << "classA() this " << this << endl;
}
};
int main()
{
classB b;
system("pause");
return 0;
}
代码运行结果如下:
通过 VS 查看类的布局如下:
包含虚函数的对象布局
当类中存在虚函数时,编译器在编译时会给该类生成一个虚函数表,虚函数表是一个函数指针数组;并且在该类的每个对象中都会生成虚函数表指针 vptr,用于指向虚函数表。
class classA
{
public:
char x;
short y;
public:
classA() = default;
virtual ~classA() = default;
virtual void Virtualfunc()
{
cout <<" Virtualfunc 执行" << endl;
}
};
通过 VS 查看类的布局如下:
如上图所示,类大小为 8 个字节,比之前增加了 4 个字节;类的前 4 个字节就是虚函数表指针,指向虚函数表的地址。
通过 VS 查看虚函数表的布局如下:
如上图所示,虚函数按照声明的顺序保存在虚函数表中。
单一继承不包含覆盖虚函数
class classA
{
public:
char x;
short y;
public:
classA() = default;
virtual ~classA() = default;
virtual void Virtualfunc()
{
cout <<" Virtualfunc 执行" << endl;
}
};
class classB : public classA
{
public:
int c;
classB() = default;
~classB() = default;
};
通过 VS 查看类的布局如下:
如上图所示,类大小为 8 个字节,比之前增加了 4 个字节;类的前 4 个字节就是虚函数表指针,指向虚函数表的地址。
通过 VS 查看虚函数表的布局如下:
如上图所示,classB 中的 Virtualfunc 函数还是 基类 classA 中的函数。
单一继承包含覆盖虚函数
class classA
{
public:
char x;
short y;
public:
classA() = default;
virtual ~classA() = default;
virtual void Virtualfunc()
{
cout <<" Virtualfunc A执行" << endl;
}
};
class classB : public classA
{
public:
int c;
classB() = default;
~classB() = default;
void Virtualfunc()
{
cout << " Virtualfunc B执行" << endl;
}
};
通过 VS 查看虚函数表的布局如下:
如上图所示,此时 classB 中的 Virtualfunc 函数还是 已经是 classB 中的函数。
通过虚表指针调用虚函数
虚函数表指针位于类最前面的位置,通过虚函数表指针可以遍历表中的虚函数指针,从而调用虚函数。
classB * b = new classB;
int * ptr = reinterpret_cast<int *>(b);//指向内存中前 4 个 字节
int * vptr = reinterpret_cast<int *>(*ptr);// *ptr 即虚函数表的地址,指向整个数组
typedef void(*func)();
func funcA = reinterpret_cast<func>(vptr[1]);
func funcB = reinterpret_cast<func>(vptr[2]);
funcA();
funcB();
代码运行结果如下:
考虑继承多个基类时:
classB * b = new classB;
int ** vtable = reinterpret_cast<int **>(b);//考虑到多继承、多个虚表
typedef void(*func)();
func funcA = reinterpret_cast<func>(vtable[0][1]);
func funcB = reinterpret_cast<func>(vtable[0][2]);
funcA();
funcB();