从汇编看c++中的多态
作者:网络转载 发布时间:[ 2013/5/31 10:37:22 ] 推荐标签:
如果类有继承关系,构造函数又如何初始化vptr指针呢?
下面是c++源码:
class X {
private:
int i;
public:
virtual void f() {}
};
class Y : public X {//Y继承自X
private:
int j;
};
int main() {
Y y;
}
下面是main函数中的汇编码:
_main PROC
; 16 : int main() {
push ebp
mov ebp, esp
sub esp, 12 ; 为对象y预留12 byte的空间 vptr指针4byte 父类成员变量4byte 子类成员变量4byte
; 17 : Y y;
lea ecx, DWORD PTR _y$[ebp];获取对象y的首地址(即this指针),作为隐含参数传递给构造函数
call ??0Y@@QAE@XZ;调用y的构造函数 虽然y没有显示定义构造函数,但由于其含有虚成员函数,编译器提供默认构造函数
; 18 : }
xor eax, eax
mov esp, ebp
pop ebp
ret 0
_main ENDP
下面是子类构造函数汇编码:
??0Y@@QAE@XZ PROC ; Y::Y, COMDAT
; _this$ = ecx
push ebp
mov ebp, esp
push ecx;//压栈ecx的目的是存放this指针
mov DWORD PTR _this$[ebp], ecx;将this指针(即对象首地址)放到刚才预留空间 ecx里面存放对象首地址
mov ecx, DWORD PTR _this$[ebp];将对象首地址给ecx 作为隐含参数传递给父类构造函数
call ??0X@@QAE@XZ;调用父类构造函数
mov eax, DWORD PTR _this$[ebp];将y的首地址给寄存器eax
mov DWORD PTR [eax], OFFSET ??_7Y@@6B@;将y的vtable(??_7Y@@6B@)首地址赋给y对象首地址所指内存 即初始化子类vptr指针
mov eax, DWORD PTR _this$[ebp];将y首地址给eax,作为返回值。构造函数总是返回对象首地址
mov esp, ebp
pop ebp
ret 0
??0Y@@QAE@XZ ENDP
下面是父类构造函数汇编码:
??0X@@QAE@XZ PROC ; X::X, COMDAT
; _this$ = ecx
push ebp
mov ebp, esp
push ecx;压栈的目的是为了存放this指针(父对象对象首地址)预留空间
mov DWORD PTR _this$[ebp], ecx;将父对象对象首地址(ecx中保存)放入刚才预留空间
mov eax, DWORD PTR _this$[ebp];将父对象首地址给寄存器eax
mov DWORD PTR [eax], OFFSET ??_7X@@6B@;将vtable(??_7X@@6B@ 和子类不同)首地址赋给父对象首地址处的内存 即初始化父对象的vptr指针
mov eax, DWORD PTR _this$[ebp];将父对象的首地址传给eax,作为返回值。构造函数总是返回对象首地址
mov esp, ebp
pop ebp
ret 0
??0X@@QAE@XZ ENDP
从上面子类和父类的构造函数汇编码可以看出来,子对象包含父对象,在构造子对象的时候先构造父对象(子对象构造函数先调用父对象构造函数)。而且父对象的首地址和子对象的首地址一样(通过汇编码中ecx传递的值可以看出来),因此父对象和子对象的vptr指针位于同一处。所以,在构造对象的构成中,vptr指针先被初始化指向父对象的vtable首地址(在父对象构造函数中),后又被初始化为指向子对象的vtable首地址(在子对象的构造函数中)。因此,在涉及继承的时候,vptr指针的值由后调用的构造函数决定。
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11