深入理解构造函数中的执行顺序

文章详细探讨了C++中构造函数内的执行顺序,包括父类构造、成员对象初始化、初始化列表以及构造函数内语句的执行次序。强调了初始化列表的顺序与成员变量定义顺序相关,而非初始化列表书写顺序。同时,文章还分析了虚继承情况下构造函数的执行流程,涉及vptr和vbptr的初始化。
摘要由CSDN通过智能技术生成

深入理解构造函数中的执行顺序

本文将探讨vbptr、vptr、父类构造、成员对象、初始化列表和构造函数内语句的执行顺序

初始化列表与成员类定义顺序的关系

代码

#include <iostream>

class test1
{
public:
	test1() 
	{
		std::cout << "test2" << std::endl;
	};
};

class test2
{
public:
	test2() 
	{
		std::cout << "test2" << std::endl;
	};
};

class Base
{
public:
	Base(){}
};

class Drive:public Base
{
public:
	Drive():t2(),t1(){}
public:
	test1 t1;
	test2 t2;

};
int main()
{
	Base *drive = new Drive();
	return 0;
}

汇编

00843490  push        ebp  
00843491  mov         ebp,esp  
00843493  sub         esp,0CCh  
00843499  push        ebx  
0084349A  push        esi  
0084349B  push        edi  
0084349C  push        ecx  
0084349D  lea         edi,[ebp-0CCh]  
008434A3  mov         ecx,33h  
008434A8  mov         eax,0CCCCCCCCh  
008434AD  rep stos    dword ptr es:[edi]  
008434AF  pop         ecx  
008434B0  mov         dword ptr [this],ecx  
008434B3  mov         ecx,offset _51CD6868_深入理解构造函数中的执行顺序@cpp (08502BBh)  
008434B8  call        @__CheckForDebuggerJustMyCode@4 (08412DFh)  
008434BD  mov         ecx,dword ptr [this]  
008434C0  call        Base::Base (08412F8h)  
008434C5  mov         ecx,dword ptr [this]  
008434C8  call        test1::test1 (0841032h)  
008434CD  mov         ecx,dword ptr [this]  
008434D0  add         ecx,1  
008434D3  call        test2::test2 (084111Dh)  
008434D8  mov         eax,dword ptr [this]  
008434DB  pop         edi  
008434DC  pop         esi  
008434DD  pop         ebx  

分析

  1. 首先执行了父类构造是没用任何质疑的
  2. 代码中初始化列表t2是在t1前面的,但是汇编代码执行t1是在t2前面的,这也就是说成员对象的初始化与你初始化列表写的先后顺序无关,与你定义的顺序是由关系的

总结

先执行父类构造,在执行初始化列表,初始化列表在执行顺序与成员变量的定义顺序有关系,与初始化列表中的书写顺序无关

继承关系与初始化列表与构造函数代码块的执行顺序

#include <iostream>

class test1
{
public:
	test1() 
	{
		std::cout << "test2" << std::endl;
	};
};

class test2
{
public:
	test2() 
	{
		std::cout << "test2" << std::endl;
	};
};

class Base
{
public:
	Base(){}
};

class Drive:public Base
{
public:
	Drive():t2()
	{
		std::cout << "Drive" << std::endl;
	}
public:
	test1 t1;
	test2 t2;

};
int main()
{
	Base *drive = new Drive();
	return 0;
}

汇编代码:

01003250  push        ebp  
01003251  mov         ebp,esp  
01003253  sub         esp,0CCh  
01003259  push        ebx  
0100325A  push        esi  
0100325B  push        edi  
0100325C  push        ecx  
0100325D  lea         edi,[ebp-0CCh]  
01003263  mov         ecx,33h  
01003268  mov         eax,0CCCCCCCCh  
0100326D  rep stos    dword ptr es:[edi]  
0100326F  pop         ecx  
01003270  mov         dword ptr [this],ecx  
01003273  mov         ecx,offset _51CD6868_深入理解构造函数中的执行顺序@cpp (010102BBh)  
01003278  call        @__CheckForDebuggerJustMyCode@4 (010012DFh)  
0100327D  mov         ecx,dword ptr [this]  
01003280  call        Base::Base (010012F8h)  
01003285  mov         ecx,dword ptr [this]  
01003288  call        test1::test1 (01001032h)  
    25: };
    26: 
    27: class Drive:public Base
    28: {
    29: public:
    30: 	Drive():t2()
0100328D  mov         ecx,dword ptr [this]  
01003290  add         ecx,1  
01003293  call        test2::test2 (0100111Dh)  
    32: 		std::cout << "Drive" << std::endl;
01003298  mov         esi,esp  
0100329A  push        offset std::endl<char,std::char_traits<char> > (01001316h)  
0100329F  push        offset string "Drive" (0100ACD4h)  
010032A4  mov         eax,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (0100E0D4h)]  
    32: 		std::cout << "Drive" << std::endl;
010032A9  push        eax  
010032AA  call        std::operator<<<std::char_traits<char> > (01001262h)  
010032AF  add         esp,8  
010032B2  mov         ecx,eax  
010032B4  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0100E0A0h)]  
010032BA  cmp         esi,esp  
010032BC  call        __RTC_CheckEsp (010012E9h)  
    33: 	}

分析

  1. 在初始化列表并没有把成员对象写全的情况下,会在构造函数执行之前插入一个代码块,这个代码块执行了父类的构造函数和我们没用定义的初始化列表(需要按定义顺序执行),最后在执行构造函数内部的代码块

总结

执行顺序是 父类构造 初始化成员列表 构造函数中的代码块

vptr vbptr 继承 初始化成员列表 和 构造函数代码块

#include <iostream>

class test1
{
public:
	test1()
	{
		std::cout << "test2" << std::endl;
	};
};

class test2
{
public:
	test2()
	{
		std::cout << "test2" << std::endl;
	};
};

class Base
{
public:
	Base() {}
};

class Base2
{
public:
	Base2(){}
};

class Drive :public Base, virtual public Base2
{
public:
	Drive() :t2(), t1() 
	{
		std::cout << "Drive" << std::endl;
	}
	virtual void temp(){}
public:
	test1 t1;
	test2 t2;

};
int main()
{
	Base *drive = new Drive();
	return 0;
}


汇编代码:

    37: 	{
00DD3510  push        ebp  
00DD3511  mov         ebp,esp  
00DD3513  sub         esp,0CCh  
00DD3519  push        ebx  
00DD351A  push        esi  
00DD351B  push        edi  
00DD351C  push        ecx  
00DD351D  lea         edi,[ebp-0CCh]  
00DD3523  mov         ecx,33h  
00DD3528  mov         eax,0CCCCCCCCh  
00DD352D  rep stos    dword ptr es:[edi]  
00DD352F  pop         ecx  
00DD3530  mov         dword ptr [this],ecx  
00DD3533  mov         ecx,offset _51CD6868_深入理解构造函数中的执行顺序@cpp (0DE02BBh)  
00DD3538  call        @__CheckForDebuggerJustMyCode@4 (0DD12DFh)  
00DD353D  cmp         dword ptr [ebp+8],0  
00DD3541  je          Drive::Drive+48h (0DD3558h)  
00DD3543  mov         eax,dword ptr [this]  
00DD3546  mov         dword ptr [eax+4],offset Drive::`vbtable' (0DDACDCh)  
00DD354D  mov         ecx,dword ptr [this]  
00DD3550  add         ecx,0Ch  
00DD3553  call        Base2::Base2 (0DD14C4h)  
00DD3558  mov         ecx,dword ptr [this]  
00DD355B  add         ecx,8  
00DD355E  call        Base::Base (0DD12F8h)  
00DD3563  mov         eax,dword ptr [this]  
00DD3566  mov         dword ptr [eax],offset Drive::`vftable' (0DDACD8h)  
    31: };
    32: 
    33: class Drive :public Base, virtual public Base2
    34: {
    35: public:
    36: 	Drive() :t2(), t1() 
00DD356C  mov         ecx,dword ptr [this]  
00DD356F  add         ecx,8  
00DD3572  call        test1::test1 (0DD1032h)  
00DD3577  mov         ecx,dword ptr [this]  
00DD357A  add         ecx,9  
00DD357D  call        test2::test2 (0DD111Dh)  
    38: 		std::cout << "Drive" << std::endl;
00DD3582  mov         esi,esp  
00DD3584  push        offset std::endl<char,std::char_traits<char> > (0DD1316h)  
00DD3589  push        offset string "Drive" (0DDACE4h)  
00DD358E  mov         eax,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (0DDE0D4h)]  
00DD3593  push        eax  
00DD3594  call        std::operator<<<std::char_traits<char> > (0DD1262h)  
00DD3599  add         esp,8  
00DD359C  mov         ecx,eax  
00DD359E  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0DDE0A0h)]  
00DD35A4  cmp         esi,esp  
00DD35A6  call        __RTC_CheckEsp (0DD12E9h)  
    39: 	}
00DD35AB  mov         eax,dword ptr [this]  
00DD35AE  pop         edi  
00DD35AF  pop         esi  
00DD35B0  pop         ebx  
00DD35B1  add         esp,0CCh  
00DD35B7  cmp         ebp,esp  
00DD35B9  call        __RTC_CheckEsp (0DD12E9h)  
00DD35BE  mov         esp,ebp  
00DD35C0  pop         ebp  
00DD35C1  ret         4 

分析

  1. 首先执行了父类构造
  2. 执行了虚继承的虚基类表
  3. 执行了虚继承的构造函数(注意:这里的虚继承是写在第二个位置)
  4. 执行第一个父类构造
  5. 执行虚函表
  6. 执行初始化列表
  7. 执行构造函数代码块
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值