构造函数名必须与类名完全一致且无返回类型,声明在public区,可重载但需避免二义性;编译器仅在未定义任何构造函数时生成隐式默认构造函数,否则需显式提供;推荐用= default声明默认构造函数,初始化列表优于函数体内赋值。

构造函数声明必须和类名完全一致,且不能写返回类型
声明构造函数时,名字必须与类名一字不差,大小写敏感,且后面不能加 void 或任何类型——连 void 都不允许。写成 void Student() 或 int Student() 是语法错误,编译直接失败。
常见错误现象:在类内声明了 Student(void) 或 Student() const,结果报错 constructor cannot be declared as 'const' 或 return type specified for constructor。
- 声明位置通常放在
public:区域(除非你刻意限制对象创建方式) - 可以带参数,也可以不带;可以有默认参数,但不能有 C 风格的可变参数(如
...) - 如果类中没写任何构造函数,编译器会自动生成一个隐式默认构造函数(函数体为空)
- 一旦你写了任意一个构造函数(哪怕只有
Student()),编译器就不再生成隐式默认构造函数
定义时用作用域解析符 ::,参数列表必须严格匹配声明
定义构造函数不能在类体内直接写实现(除非是极简的内联写法),常规做法是在类外用 ClassName::ClassName(...) 形式定义。这里的参数顺序、类型、const/volatile 修饰都必须和声明完全一致,否则链接时报 undefined reference to 'Student::Student(char*, int, float)'。
例如声明是 Student(const char* name, int age = 0),定义时就不能写成 Student::Student(char* name, int age)(去掉了 const)或漏掉默认值逻辑。
- 初始化列表(
: m_name(name), m_age(age))比在函数体内赋值更安全高效,尤其对 const 成员、引用成员、没有默认构造函数的类类型成员是必须的 - 不要在构造函数体内用
new分配资源后又抛异常——没写异常规范或没配 RAII 容易导致内存泄漏 - 避免在构造函数里调用虚函数,此时动态绑定尚未完成,实际调用的是当前类版本,不是派生类重写的版本
默认构造函数被“覆盖”后,Student s; 会编译失败
只要你在类里写了带参数的构造函数(哪怕所有参数都有默认值),编译器就不会再给你生成无参的默认构造函数。这时候写 Student s; 就会报错 no matching function for call to 'Student::Student()'。
解决办法只有两个:显式补一个无参构造函数,或者确保所有构造函数都允许零实参调用(即全参数带默认值)。
- 推荐写法:
Student() = default;(C++11 起),语义清晰且不触发用户自定义逻辑 - 不推荐只写
Student() {},它虽然能编译通过,但会抑制编译器生成移动构造函数等特殊成员函数 - 如果类含 const 成员或引用成员,
= default可能失效,必须手写初始化列表
构造函数重载时,参数类型差异要足够明显
多个构造函数共存没问题,但编译器必须能根据实参唯一推导出调用哪一个。比如同时声明 Student(int) 和 Student(long),传入字面量 42 就可能产生二义性(int 和 long 都可隐式转换)。
典型踩坑场景:用 std::string 和 const char* 同时做参数类型,又没禁用隐式转换,容易意外触发非预期的重载版本。
- 用
explicit修饰单参数构造函数,能防止隐式转换(如禁止Student s = 18;这种写法) - 避免布尔类型和整型混用参数(
Student(bool)vsStudent(int)),true可转int,极易冲突 - 参数类型尽量选不可隐式转换的类型,比如用
std::string_view替代const char*+std::string二重构造
构造函数看似简单,但涉及初始化顺序、资源生命周期、重载解析规则等多个层面。最容易被忽略的是:默认构造函数的生成规则不是“有没有写”,而是“有没有写任何构造函数”——哪怕只写了一个带默认参数的,也会让 Student s; 失效。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











