C++使用移动语义时意外调用默认构造函数的原因分析
二者表现差异的根本原因
差异来自C++类成员的初始化阶段和构造函数体内赋值阶段的执行顺序规则:
所有非静态类成员变量,会在进入构造函数的{}函数体之前完成初始化。如果你没有在构造函数的初始化列表中显式指定成员的初始化方式,编译器会自动调用该成员的默认构造函数完成初始化,后续构造函数体内对该成员的操作都属于赋值操作,不是初始化操作。
版本1(赋值写在构造函数体内)的执行逻辑
- 先执行测试代码里的
MyResource(1),调用带int参数的构造函数,输出Int Ctor - 进入
MyWrapper构造函数体前,初始化列表没有指定m_resource的初始化方式,编译器自动调用MyResource的默认构造函数初始化成员m_resource,输出Default Ctor - 进入
MyWrapper构造函数体后,执行m_resource = std::move(resource),调用移动赋值运算符,输出Move Op - 临时对象
MyResource(1)析构,输出Dtor wrapper对象销毁时内部m_resource析构,输出Dtor
完全匹配版本1的输出结果。
版本2(初始化写在初始化列表)的执行逻辑
- 先执行测试代码里的
MyResource(1),调用带int参数的构造函数,输出Int Ctor - 进入
MyWrapper构造函数体前,直接用初始化列表的m_resource(std::move(resource))调用移动构造函数初始化成员m_resource,输出Move Ctor,不会触发默认构造 - 临时对象
MyResource(1)析构,输出Dtor wrapper对象销毁时内部m_resource析构,输出Dtor
完全匹配版本2的输出结果。
额外说明
如果MyResource没有定义默认构造函数,版本1的代码会直接编译失败。这也是C++开发中优先使用初始化列表初始化成员的核心原因:避免不必要的默认构造+赋值开销,也能适配没有默认构造的成员类型。
内容的提问来源于stack exchange,提问作者Daniel Marques
相关产品推荐
相关产品推荐

