Visual C++对C++对象模型的实现细节

下面这些是用Visual Studio 2019 16.4pre4反汇编和二进制分析得到的结论,有可能有不准确和错误的,也不一定全面,建议参考更权威的资料或者自己使用Visual Studio反汇编和二进制分析验证。

没有基类、没有虚函数的类的结构:按顺序放置用户定义的字段,在字段之间、结构体末尾可能存在用于内存对齐的填充字节。

没有基类、存在虚函数的类的结构:第一个字段是指向虚函数表的指针void** vtable,其余是用户定义的字段。有虚函数的类也称为多态类。

存在基类的类(派生类)的结构:先按顺序放置指向虚函数表的指针void** vtable(如果需要的话)、指向虚基类偏移表的指针ptrdiff_t* vbase(如果需要的话),再首先放置常规基类(有虚函数的类优先,这样如果可能的话可以共用虚函数表指针vtable),然后放置用户定义的字段,最后放置虚基类和继承的虚基类。放置的任何基类尾部的虚基类都被裁剪掉并集中放置在最后的虚基类区域。

虚函数表的结构:在vtable[-1]处放置运行时类型信息(RTTI)指针(如果开启了RTTI的话),然后从vtable[0]开始依次放置虚函数指针。这样的结构主要是为了兼容COM接口的二进制规范。

虚基类偏移表的结构:第一个字段vbase[0]是类基地址相对于虚基类偏移表指针ptrdiff_t* vbase字段的偏移(一般是0或负数,比如如果有虚函数表的话一般是负数),从第二个字段vbase[1]开始是虚基类相对类基地址的偏移。

指针转换:

1、派生类->常规基类:直接加上基类在派生类中的偏移。

2、常规基类->派生类:直接减去基类在派生类中的偏移。

3、派生类->虚基类:从虚基类偏移表中读取虚基类的偏移加上。

4、虚基类->派生类:需要使用dynamic_cast,从RTTI信息中读取相对偏移,如果虚基类没有虚表则无法实现这样的转换。

虚函数this指针协变:

1、派生类实现常规基类的虚函数,调用者永远传入基类的this指针,实现者减去偏移修正为派生类的this指针。

2、派生类初次实现虚基类的虚函数,调用者永远传入基类的this指针,实现者假设类没有被再次继承,直接当作常规基类处理,减去偏移修正为派生类的this指针。

3、派生类继承2中的类,并重新实现了对应函数,在函数中调用基类函数时,实现者需要对传入的基类this指针进行相对修正,再调用原有函数,以使得原有函数对“基类的this指针”修正以后恰好是派生类指针。

4、派生类继承2中的类,但没有重新实现对应函数,实现者需要在基类虚函数表中重新注册一个修正函数,对传入的基类this指针进行相对修正,再调用原有函数,以使得原有函数对“基类的this指针”修正以后恰好是派生类指针。

虚函数返回值指针协变:

1、如果派生类和基类没有偏移,协变函数和原函数就是同一个函数,只在基类虚函数表中存在。

2、如果派生类和基类存在偏移,协变函数存在于派生类虚函数表中,基类虚函数表中对应项是一个修正函数,它先调用协变函数,再将返回值修正为基类指针。

成员函数指针:

1、对于非虚函数,成员函数指针就是指向函数本身。

2、对于虚函数,成员函数指针指向进行虚函数调用的自动生成的代码。

发布了29 篇原创文章 · 获赞 1 · 访问量 3409

猜你喜欢

转载自blog.csdn.net/defrag257/article/details/103205759
今日推荐