ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

成员初始化列表和构造函数赋值有什么区别

成员初始化列表和构造函数赋值有什么区别 1. 引言在 C 中初始化类成员有两种常见方式成员初始化列表和构造函数体内的赋值。虽然两者在简单场景下看起来效果相同但在底层机制、性能、以及某些特殊成员的处理上存在显著差异。本文将从多个维度对比这两种方式的区别。2. 基本语法对比先看两种写法的基本形式。class Example { public: Example(int a, int b) : m_a(a), m_b(b) {} // 成员初始化列表 private: int m_a; int m_b; }; class Example2 { public: Example2(int a, int b) { // 构造函数体内赋值 m_a a; m_b b; } private: int m_a; int m_b; };对于内置类型如 int、double两种方式在最终结果上没有区别但底层过程不同。3. 核心区别3.1 初始化时机不同成员初始化列表在成员变量被创建时直接初始化构造函数体内的赋值则是在成员变量已经默认构造完成后再执行赋值操作。也就是说使用初始化列表时成员只经历一次构造而使用赋值时成员经历了「默认构造 赋值」两步。3.2 对 const 成员和引用成员的影响const 成员和引用成员必须在定义时初始化不能先默认构造再赋值。因此这类成员只能通过成员初始化列表来初始化。class Demo { public: Demo(int x, int ref) : m_const(x), m_ref(ref) {} // 正确 private: const int m_const; int m_ref; };如果改用构造函数体内赋值编译器会直接报错因为 const 成员和引用成员没有默认构造函数可供调用。3.3 对没有默认构造函数的类类型成员的影响如果某个成员是自定义类类型且该类没有提供默认构造函数那么该成员只能在初始化列表中显式初始化。class Member { public: Member(int v) : value(v) {} private: int value; }; class Wrapper { public: Wrapper(int v) : m_member(v) {} // 必须用初始化列表 private: Member m_member; };若在构造函数体内写 m_member v则要求 Member 先有默认构造函数否则编译失败。3.4 性能差异对于类类型成员使用初始化列表直接构造省去了一次默认构造和一次赋值操作性能更优。对于内置类型现代编译器通常能优化掉差异但语义上初始化列表仍然更直接。4. 初始化顺序无论初始化列表中的书写顺序如何成员变量的初始化顺序始终按照它们在类中声明的顺序进行而不是按照初始化列表中的书写顺序。这一点容易引发隐患。class Order { public: Order(int x) : m_b(x), m_a(m_b) {} // 注意m_a 先于 m_b 初始化 private: int m_a; int m_b; };由于 m_a 在类中先声明它会先被初始化此时 m_b 尚未初始化m_a 拿到的是未定义值。因此初始化列表的书写顺序应与成员声明顺序保持一致。5. 总结对比维度成员初始化列表构造函数体内赋值初始化时机成员创建时直接初始化默认构造后再赋值const 成员支持不支持引用成员支持不支持无默认构造函数的类成员支持不支持性能更优少一次默认构造略差初始化顺序按成员声明顺序按构造函数体执行顺序建议凡是能用初始化列表的场景优先使用初始化列表尤其是 const 成员、引用成员和类类型成员。这样既能保证正确性也能获得更好的性能。
返回列表