系列文章目录构造函数超详解特性类型初始化方式易错避坑析构函数超详解误区、语法、调用时机、析构顺序拷贝构造函数超详解浅拷贝、深拷贝、拷贝构造 vs 赋值运算符重载、易错点赋值运算符重载 operator超详解深浅拷贝、自赋值判断、连续赋值原理、三法则、完整示例代码【C初阶】const 成员函数 取地址运算符重载超详解六大默认成员函数收官篇文章目录系列文章目录前言一、通俗理解什么是构造函数二、构造函数五大硬性特性必背三、构造函数三大类型1. 无参构造函数2. 带参构造函数3. 全缺省构造函数4. 经典易错语法坑四、编译器默认构造的底层行为五、构造函数两种初始化方式1. 构造函数体内赋值2. 初始化列表推荐3. 必须用初始化列表的 4 种场景否则会报错4. C11 新特性类内成员缺省值六、explicit 关键字禁止隐式转换总结前言学 C 类和对象构造函数绝对是入门第一道核心重点。我们用 C 语言写结构体时每次都要手动写Init初始化函数、每次创建结构体对象都要手动调用初始化非常麻烦还容易忘而 C 直接设计了构造函数对象一创建自动执行初始化不用手动调用完美替代 C 语言的初始化函数。本篇文章就来详细讲解一下构造函数内容如有疏漏之处还望各位不吝指正。一、通俗理解什么是构造函数官方定义 核心作用构造函数是类的特殊成员函数核心任务专门负责对象成员变量的初始化不负责开辟空间对象空间在栈帧 / 堆上提前开好。生活化比喻把类比作【人出生模板】对象就是具体的某一个人构造函数 对象出生时的专属初始化接生员对象被实例化创建编译器自动调用构造函数给成员变量初始化全程不用我们手动调用天生自带初始化能力。二、构造函数五大硬性特性必背函数名必须和类名完全相同不能改名、不能加前缀后缀没有返回值连void都不用写这是C 语法硬性规定的对象创建时系统自动调用禁止手动主动调用构造函数支持函数重载一个类可以有多个构造如果我们一个构造都不写编译器会自动生成默认无参构造只要你显式写了任意一个构造编译器就不再自动生成。三、构造函数三大类型1. 无参构造函数指没有任何形参的构造函数。#includeiostreamusingnamespacestd;classDate{public:// 无参构造函数Date(){_year1;// 初始化年_month1;// 初始化月_day1;// 初始化日}voidprint(){cout_year/_month/_dayendl;}private:int_year;int_month;int_day;};调用方式细节实例化对象时不能传参且不能加括号加括号会被编译器识别为 “函数声明”而非对象创建intmain(){Date d1;// 正确调用无参构造函数// Date d1(); // 错误被识别为“声明一个返回Date类型、无参的函数d1”d1.print();// 输出1/1/1return0;}关键注意事项若手动定义了无参构造编译器不再自动生成默认构造函数无参构造的灵活性极低只能固定初始化值实际中很少单独使用无参构造不能和全缺省构造同时存在下文会讲原因。2. 带参构造函数带有一个或多个形参的构造函数解决 “无参构造只能固定初始化” 的问题支持自定义初始化值。#includeiostreamusingnamespacestd;classDate{public:// 带参构造函数Date(intyear,intmonth,intday){_yearyear;_monthmonth;_dayday;}voidprint(){cout_year/_month/_dayendl;}private:int_year;int_month;int_day;};调用方式细节实例化对象时必须传全参数参数个数 / 类型需匹配intmain(){Dated2(2026,5,20);// 正确调用带参构造自定义初始化d2.print();// 输出2026/5/20// Date d2; //错误类中已手动定义带参构造编译器不再生成无参构造无参实例化会报错return0;}关键注意事项支持**函数重载**一个类可以定义多个带参构造参数个数 / 类型不同例如// 重载1只初始化年Date(intyear){_yearyear;_month1;_day1;}// 重载2初始化年月Date(intyear,intmonth){_yearyear;_monthmonth;_day1;}// 重载3初始化年月日Date(intyear,intmonth,intday){_yearyear;_monthmonth;_dayday;}带参构造的灵活性中等必须传参无法无参实例化适合 “强制用户指定初始化值” 的场景。3. 全缺省构造函数所有参数都提供默认值的构造函数是 “无参构造 带参构造” 的结合体兼顾灵活性和易用性。#includeiostreamusingnamespacestd;classDate{public:// 全缺省构造函数所有参数都有默认值Date(intyear1,intmonth1,intday1){_yearyear;_monthmonth;_dayday;}voidprint(){cout_year/_month/_dayendl;}private:int_year;int_month;int_day;};调用方式细节支持0 个、部分、全部参数传递参数按 “从左到右” 匹配默认值补全未传递的参数intmain(){Date d1;// 传0个参数用所有默认值 → 1/1/1Dated3(2026);// 传1个参数year2026month/day用默认值 → 2026/1/1Dated4(2026,5);// 传2个参数year2026、month5day用默认值 → 2026/5/1Dated5(2026,5,20);// 传3个参数覆盖所有默认值 → 2026/5/20d1.print();// 1/1/1d3.print();// 2026/1/1d4.print();// 2026/5/1d5.print();// 2026/5/20return0;}关键注意事项核心易错点与无参构造互斥若同时定义无参构造和全缺省构造编译器会报 “调用歧义”Date d1; 无法判断调用哪个classDate{public:// 无参构造Date(){_year1;// 初始化年_month1;// 初始化月_day1;// 初始化日}// 全缺省构造Date(intyear1,intmonth1,intday1){_yearyear;_monthmonth;_dayday;}};intmain(){Date d1;// 报错ambiguous call to overloaded functionreturn0;}默认值的规则参数默认值必须从右往左连续赋值不能中间参数无默认值例如// 错误month无默认值且在year有默认和day有默认中间Date(intyear1,intmonth,intday1){_yearyear;_monthmonth;_dayday;}// 正确从右往左连续给默认值Date(intyear,intmonth1,intday1){_yearyear;_monthmonth;_dayday;}属于 “默认构造函数”能 “无参调用” 的构造函数都算默认构造无参构造、全缺省构造若类中定义了全缺省构造编译器不再生成默认构造。4. 经典易错语法坑Date d1;// 正确定义一个无参对象Dated2();// 错误这不是对象是函数声明原因C 语法歧义Date d2(); 编译器会理解成声明一个返回值为 Date、无参的函数 d2而不是创建对象。这种情况下VS编译器 会给出 C4930 警告一定要避开这个写法。四、编译器默认构造的底层行为如果我们不写任何构造编译器自动生成默认构造它的规则内置类型int、char、double、指针编译器不保证初始化成员值是随机垃圾值自定义类型成员类里面嵌套另一个类对象编译器自动调用这个类的默认构造完成初始化。示例#includeiostreamusingnamespacestd;typedefintSTDateType;// 栈类classStack{public:Stack(intn4){_a(STDateType*)malloc(sizeof(STDateType)*n);if(_anullptr){perror(malloc 申请空间失败\n);exit(1);}_top0;_capacityn;coutStack初始化成功endl;// 打印是否初始化成功}private:STDateType*_a;int_top;int_capacity;};// 两个Stack实现队列classMyQueue{public:// 编译器默认生成MyQueue的构造函数调用了Stack的构造完成了两个成员的初始化private:Stack pushst;Stack popst;};intmain(){MyQueue mq;return0;}五、构造函数两种初始化方式1. 构造函数体内赋值只是赋值操作不是真正意义的初始化。Date(intyear1,intmonth1,intday1){_yearyear;// 将形参year的值赋给_year_monthmonth;// 将形参month的值赋给_month_dayday;// 将形参day的值赋给_day}2. 初始化列表推荐语法是以⼀个冒号开始接着是⼀个以逗号分隔的数据成员列表每个成员变量后⾯跟⼀个放在括号中的初始值或表达式Date(inty1,intm1,intd1):_year(y),_month(m),_day(d){}特点初始化列表才是真正初始化比函数体内赋值效率更高无论你写不写初始化列表编译器都会默认走初始化逻辑。3. 必须用初始化列表的 4 种场景否则会报错引用类型成员变量const修饰的成员变量没有默认构造的自定义类成员继承中的基类初始化classDate{public:// 必须初始化列表函数体内赋值编译报错Date(intx):_ref(x),_n(20),_t(10){}private:int_ref;// 引用constint_n;// constTime _t;// 无默认构造的自定义成员};4. C11 新特性类内成员缺省值可以在类声明时直接给成员默认值classDate{private:int_year1;// 类内缺省值int_month1;int_day1;};特性如果构造初始化列表没给该成员赋值自动使用类内默认值。六、explicit 关键字禁止隐式转换普通单参构造会触发隐式类型转换classDate{public:Date(inty){_yeary;}private:int_year;};intmain(){Date d2025;// 隐式转换int - Datereturn0;}加explicit修饰构造禁止隐式转换只能显式创建explicitDate(inty){_yeary;}intmain(){// Date d 2025; // 编译报错Dated(2025);// 只能显式调用}总结构造函数是 C 面向对象的基石后面拷贝构造、析构、运算符重载全都依赖它。把这篇知识点吃透后续学习才能走得更远~