c++类工具的内存模子
副标题#e#
C++类工具内存布局
首先先容一下C++中有担任干系的类工具内存的机关:在C++中,假如类中有虚函数,那么它就会有一个虚函数表的指针__vfptr,在类工具最开始的内存数据中。之后是类中的成员变量的内存数据。
对付子类,最开始的内存数据记录着父类工具的拷贝(包罗父类虚函数表指针和成员变量)。之后是子类本身的成员变量数据。
对付子类的子类,也是同样的道理。可是无论担任了几多个子类,工具中始终只有一个虚函数表指针。
为了探讨C++类工具的内存机关,先来写几个类和函数
首先写一个基类:
class Base
{
public:
virtual void f() { cout << "Base::f" << endl; }
virtual void g() { cout << "Base::g" << endl; }
virtual void h() { cout << "Base::h" << endl; }
int base;
protected:
private:
};
然后,我们多种差异的担任环境来研究子类的内存工具布局。
1. 无虚函数集担任
//子类1,无虚函数重载
class Child1 : public Base
{
public:
virtual void f1() { cout << "Child1::f1" << endl; }
virtual void g1() { cout << "Child1::g1" << endl; }
virtual void h1() { cout << "Child1::h1" << endl; }
int child1;
protected:
private:
};
这个子类Child1没有担任任何一个基类的虚函数,因此它的虚函数表如下图:
我们可以看出,子类的虚函数表中,先存放基类的虚函数,在存放子类本身的虚函数。
#p#副标题#e#
2. 有一个虚函数担任
//子类2,有1个虚函数重载
class Child2 : public Base
{
public:
virtual void f() { cout << "Child2::f" << endl; }
virtual void g2() { cout << "Child2::g2" << endl; }
virtual void h2() { cout << "Child2::h2" << endl; }
int child2;
protected:
private:
};
当子类重载了父类的虚函数,则编译器会将子类虚函数表中对应的父类的虚函数替换成子类的函数。
3. 全部虚函数都担任
//子类3,全部虚函数重载
class Child3 : public Base
{
public:
virtual void f() { cout << "Child3::f" << endl; }
virtual void g() { cout << "Child3::g" << endl; }
virtual void h() { cout << "Child3::h" << endl; }
protected:
int x;
private:
};
4. 多重担任
多重担任,即类有多个父类,这种环境下的子类的内存布局和单一担任有所差异。
我们可以看到,当子类担任了多个父类,那么子类的内存布局是这样的:子类的内存中,顺序
5. 菱形担任
6. 单一虚拟担任
虚拟担任的子类的内存布局,和普通担任完全差异。虚拟担任的子类,有单独的虚函数表, 别的也单独生存一份父类的虚函数表,两部门之间用一个四个字节的0x00000000来作为分界。子类的内存中,首先是本身的虚函数表,然后是子类的数据成员,然后是0x0,之后就是父类的虚函数表,之后是父类的数据成员。
假如子类没有本身的虚函数,那么子类就不会有虚函数表,可是子类数据和父类数据之间,照旧需要0x0来隔断。
因此,在虚拟担任中,子类和父类的数据,是完全隔断的,先存放子类本身的虚函数表和数据,中间以0x分界,最后生存父类的虚函数和数据。假如子类重载了父类的虚函数,那么则将子类内存中父类虚函数表的相应函数替换。
7. 菱形虚拟担任
结论:
(1)对付基类,假如有虚函数,那么先存放虚函数表指针,然后存放本身的数据成员;假如没有虚函数,那么直接存放数据成员。
(2)对付单一担任的类工具,先存放父类的数据拷贝(包罗虚函数表指针),然后是本类的数据。
(3)虚函数表中,先存放父类的虚函数,再存放子类的虚函数
(4)假如重载了父类的某些虚函数,那么新的虚函数将虚函数表中父类的这些虚函数包围。
#p#分页标题#e#
(5)对付多重担任,先存放第一个父类的数据拷贝,在存放第二个父类的数据拷贝,一次类推,最后存放本身的数据成员。个中每一个父类拷贝都包括一个虚函数表指针。假如子类重载了某个父类的某个虚函数,那么该将该父类虚函数表的函数包围。别的,子类本身的虚函数,存储于第一个父类的虚函数表后边部门。
(6)当工具的虚函数被挪用是,编译器去查询工具的虚函数表,找到该函数,然后挪用。