ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

第22节 构造函数与初始化列表:解决const、引用成员初始化坑点

第22节 构造函数与初始化列表:解决const、引用成员初始化坑点 承接上文上一节学习了类的成员变量、成员函数与工程编码规范我们发现对象创建后成员变量默认是随机垃圾值需要手动调用setter赋值。本节学习构造函数对象创建时自动执行的特殊成员函数再深入初始化列表解决普通函数体内无法初始化const成员、引用成员的硬性语法问题掌握对象初始化底层逻辑。学习说明构造函数是面向对象里对象生命周期的起点。很多新手分不清在构造函数函数体内赋值和初始化列表初始化有本质区别。 函数体内是赋值操作初始化列表才是真正的初始化。const成员、引用成员必须在初始化列表完成初始化不能在函数体内赋值这是本节核心难点。本节学习目标掌握构造函数基础语法、调用时机、构造函数特征理解默认构造函数以及自定义构造后默认构造消失的规则学会初始化列表写法分清初始化 vs 赋值的本质差异掌握const成员、引用成员为什么只能用初始化列表初始化掌握初始化列表的执行顺序和书写顺序无关按成员声明顺序初始化一、构造函数基础核心特点函数名和类名完全相同没有返回值连void都不能写对象创建的一瞬间自动调用无法手动调用可以重载多个构造函数参数列表不同如果自己不写任何构造编译器自动生成默认无参构造只要手写任意构造函数编译器不再提供默认构造#include iostream #include string using namespace std; class Person { private: string m_name; int m_age; public: // 无参构造函数 Person() { cout 无参构造函数执行 endl; m_name 未知; m_age 0; } // 有参构造函数构造重载 Person(string name, int age) { cout 有参构造函数执行 endl; m_name name; m_age age; } void showInfo() { cout m_name m_age endl; } }; int main() { Person p1; // 自动调用无参构造 Person p2(李四, 25); // 自动调用有参构造 p1.showInfo(); p2.showInfo(); return 0; }上面写法是构造函数体内赋值不是初始化。普通成员可以这么写但const、引用成员不支持这种写法。补充验证示例手写有参构造后默认构造消失#include iostream #include string using namespace std; class Person { public: Person(string name) { cout 有参构造执行 endl; } }; int main() { Person p1(张三); // 正确 // Person p2; // 编译报错手写构造后编译器不再生成默认无参构造 return 0; }C11 default显式生成默认构造进阶示例#include iostream #include string using namespace std; class Person { private: string m_name; public: Person(string name) { m_name name; } Person() default; // 显式告诉编译器生成默认无参构造 }; int main() { Person p1(张三); Person p2; // 正常编译运行 return 0; }二、初始化列表语法初始化列表写在构造函数参数括号后面、函数体{}之前用冒号开头。#include iostream #include string using namespace std; class Person { private: string m_name; int m_age; public: // 初始化列表写法 Person(string name, int age) : m_name(name), m_age(age) { cout 初始化列表执行完成进入构造函数体 endl; } void showInfo() { cout m_name m_age endl; } }; int main() { Person p(王五, 30); p.showInfo(); return 0; }核心规则初始化列表先执行然后才进入构造函数大括号内部初始化 创建变量同时给初始值函数体内 变量已经创建完成再修改值赋值三、重点const成员、引用成员只能使用初始化列表const变量、引用变量定义的时候就必须初始化一旦创建完成后续不能赋值。 放到构造函数体内时成员已经完成创建此时再赋值直接编译报错。只能在初始化列表阶段完成初始化。#include iostream using namespace std; class Test { private: const int m_a; // const成员必须初始化 int m_ref; // 引用成员必须初始化 public: // 只能在初始化列表初始化 const 和引用成员 Test(int val) : m_a(100), m_ref(val) { // m_a 200; // 错误const不能赋值 // m_ref val; // 错误引用已经绑定不能在这里初始化 } }; int main() { int val 42; Test t(val); // m_a 100, m_ref绑定到变量val return 0; }四、初始化列表容易踩坑初始化顺序⚠️初始化顺序由成员在类中声明的先后顺序决定和初始化列表书写顺序无关这是高频隐蔽bug。#include iostream using namespace std; class Demo { private: int m_x; int m_y; public: Demo(int val) : m_y(val), m_x(m_y) { // 先初始化m_x再初始化m_y // m_x(m_y) 此时m_y还未初始化m_x得到随机垃圾值 cout m_x m_x m_y m_y endl; } }; int main() { Demo d(10); // 输出m_x随机值 m_y10 // 因为先初始化m_x(m_y)此时m_y还未初始化 return 0; }坑点说明列表写的是先m_y再m_x但类内声明顺序是m_x在前程序优先初始化m_x此时m_y还没赋值导致m_x为随机值。五、工程编码推荐✅ 推荐优先使用初始化列表初始化所有成员而不是构造体内赋值语法统一同时支持普通成员、const成员、引用成员对于自定义类型成员减少一次临时对象拷贝性能更好代码可读性强一眼看清对象创建时各个成员初始值❌ 不推荐全部放到构造函数体内赋值仅简单Demo可以使用性能对比示例用于思考题#include iostream #include string using namespace std; class Student { private: string m_name; public: // 写法A函数体内赋值 Student(string name) { m_name name; // 先默认构造m_name再赋值两次操作 } // 写法B初始化列表 Student(string name) : m_name(name) { } // 直接初始化一次操作 };六、高频坑点总结手写任意构造函数后编译器不再提供默认无参构造。再写Person p;会编译报错。区分初始化列表是初始化函数体内代码是赋值二者本质不一样。const成员、引用成员只能在初始化列表初始化构造函数体内赋值直接编译失败。初始化列表执行顺序 ≠ 书写顺序按类内成员声明顺序初始化极易写出隐藏bug。七、本节思考题写了一个有参构造函数再定义Person p;为什么编译报错const成员变量能不能在构造函数{}里面赋值为什么初始化列表里面成员的初始化顺序是按列表书写顺序还是类内声明顺序举个例子。对比上面Student类的写法A和写法B二者有什么区别为什么工程上推荐写法B下期预告下一节析构函数实战内存释放场景与手写规范。
返回列表