反汇编看内存布局来研究vc2005中多重继承及虚函数

来源:互联网 发布:南京大学软件学院 校区 编辑:程序博客网 时间:2024/05/17 01:25

很早就知道虚函数采用一种叫虚函数表的机制,在类的内存空间中添加一个"隐藏"的成员变量的方法保存了虚函数表的指针。自己便对单继承的虚函数的执行原理有了些了解,但一直不知多继承怎么实现,今天就研究了一下。

 

class Base;

class BaseA; 

class BaseB:public virtual  Base

class BaseC:public virtual  Base

class Derived:public BaseA,public BaseB,public BaseC

 

反汇编发现Derived的内存分配如下:

*原来没有vTable的父类就算在继承列表里先声明也不一定放在最前,可能主要考虑这样vTable统一放最前操作比较方便.

 

当以Base*调用时:

  访问vFunBase,直接通过前四个字节的vTable(Base)

  访问m_base直接根据偏移量访问

当以BaseA*调用时:

  直接访问

当以BaseB*调用时:

  访问Base*的vFunBase和m_base通过(this+4)来得到描述Base类位置偏移的说明,从说明中的到与偏移量有关的值(0x04处)

  访问vFunB1,直接通过前四个字节的vTable(Base)

当以BaseC*调用时:

  与BaseB相似(此时指针只指向BaseC的变量,在Derived的指针继承上偏移0x0c)

当以Derived*调用时:

  访问vFunBase与BaseB相同(因为指向相同)

  访问vFunB1与BaseB相同(因为指向相同)

  访问vFunC1和vFunC2时,this+0x0c得到BaseC的vTable,然后存取里面的函数。

 

原来没想到会用多个vTable,今天了解了,呵呵

其实细想一个vtable和几个没有什么关系,编译器就知道每一个类型的内存布局,而调用函数的机器指令是编译时确定的,这时编译器已经知道指针类型(不一定是对象真实类型),那就可以知道需要的函数在这个类型的哪个vtable的第几个位置,生成固定代码即可。

只要你类型转换时指针指向正确(参见第二幅图,转化为不同父类型后指针是不一样的),那么编译器就会根据指针指向的类型的内存布局找到你需要的函数.

*好象调用父类的非虚函数时,传给函数的this指针也如上图所示(想想也应该这样,因为父类的函数只会按父类的内存布局操作,有空会验证一下)

 

自己发现了vTable-4处有猫腻,但就是搞不明白是什么,通过http://bbs.pediy.com/showthread.php?t=61873了解到一些东西(见最后所附)

由于多态和RTTI的目的,对象内部隐藏的那些指针指向的值应该都是和当前对象的真实类型相关,因此相同类的不同对象中隐藏指针的值应该相同.(不要被我图中括号括住的"BaseB"等迷惑,那只是为了说明他们和哪个父类相关)

事实三处RTTITypeDescriptor指针是一样的,在dynamic_cast < type-id > ( expression ) 里会直接把RTTITypeDescriptor的地址作为参数传入.但三处RTTICompleteObjectLocator指针却不一样,现在也没查到RTTICompleteObjectLocator的前12个字节是什么.

  

 

附:

RTTI(RuniTime TypeInfo)是c++的一种运行时类型识别机制
当一个类的引用或者指针的值去进行“类型识别",  叫做动态识别
当一个类的实例去进行"类型识别", 叫做静态识别
动态识别需要编译打开了/GR开关才可有效, 否则会引起一个运行时错误, 而静态的不需要
对于大型工程而言, 静态识别并没有多少意义
实现原理:
这里假设你对类的虚表非常熟悉, 如果一个类存在虚函数, 或者他的基类存在虚函数, 
或者virtual继承了其他类,或者开启了RTTI, 那么编译器会为该类生成一个虚表,
 该表是一个该类需要虚实现的函数的指针表,一般类的头4个字节为该表的指针
(当基类是多重继承的时候, 非第一个继承的基类的虚表不是在头4字节)
当有RTTI在的时候,  虚表-4的位置是一个叫做RTTI Complete Object Locator的描述体
该描述体+c的位置是一个叫做RTTI Type Descriptor的描述体, 该描述体+8的位置是一段
原始类名的buf, 我们只要得到了该buf, 就能得到类的名字.

RTTI Type Descriptor这个描述体在c++中叫做type_info, 他的定义如下
class type_info {
public:
    _CRTIMP virtual ~type_info();
    _CRTIMP int operator==(const type_info& rhs) const;
    _CRTIMP int operator!=(const type_info& rhs) const;
    _CRTIMP int before(const type_info& rhs) const;
    _CRTIMP const char* name() const;
    _CRTIMP const char* raw_name() const;
private:
    void *_m_data;
    char _m_d_name[1];
    type_info(const type_info& rhs);
    type_info& operator=(const type_info& rhs);
};
typeid作为一个c++关键字, 给它传递一个任意类型的实例, 他能够返回一个type_info的引用。

 

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 内衣肩带不带调整的怎么办 不正确佩戴胸罩导致下垂怎么办 增肌时期晚上饿怎么办 啤酒和可乐喝了怎么办 脸部毛孔堵塞不出汗怎么办 减肥运动完饿了怎么办 想减肥晚上饿了怎么办 孕妇喝不进去水怎么办 跑步喝多了水怎么办 衣柜门轮子坏了怎么办 穿瑜伽裤下边有缝怎么办 3岁宝宝比较内向怎么办 我想和我同学搞基怎么办 自己做的葡萄酒太甜怎么办 吉米学校想退款怎么办 汽车租赁公司不退押金怎么办 身材不好怎么办健身教练形象照 反祈祷式做不到怎么办 窦性心跳过缓怎么办 四维彩超查出胎儿心脏有问题怎么办 减脂肚子饿了怎么办 做瑜伽腰扭到了怎么办 出了汗怎么办活动反思 农村都是老人和孩子怎么办 瑜伽垫容易出现痕迹怎么办 37岁失业了该怎么办 45岁找什么工作怎么办 华为手机4g网速慢怎么办 解析软件包时出现问题怎么办 一字马不能下去髋摆不正怎么办 练轮瑜伽骆驼式腰疼怎么办 感昌咳嗽老不好怎么办 我感昌一直不好怎么办 腰间盘突出晚上睡觉痛怎么办 天天吃撑了怎么办啊 一吸气就想咳嗽怎么办 鼻子堵住了怎么办没法吸气时 一只鼻子不通气怎么办 练瑜伽后特别饿怎么办 站一天小腿肿了怎么办 练腹肌腰粗了怎么办