构造函数名必须与类名完全一致且无返回类型;初始化列表优于赋值,尤其对const/引用成员;委托构造函数可复用逻辑;单参数构造函数应加explicit防止隐式转换。

构造函数名字必须和类名完全一致
构造函数没有返回类型(连 void 都不能写),名字必须严格等于类名,大小写敏感。写成 MyClass() 是对的,写成 myclass() 或 init() 就不会被识别为构造函数,编译器会报错:「no matching function for call to MyClass::MyClass()」。
常见错误包括:
- 误加
void前缀,如void MyClass() {}→ 编译失败 - 类名含命名空间时,构造函数定义没写全限定名,比如类是
ns::Widget,却在实现里只写Widget::Widget() - 头文件中声明了带参数的构造函数,但源文件里定义时漏写了参数列表,导致链接时报
undefined reference to MyClass::MyClass()
初始化列表比赋值更高效且必要
成员变量在进入构造函数体之前就已完成初始化。用初始化列表(: member1(val1), member2(val2))直接调用其构造函数;若在函数体内用 = 赋值,则先调用默认构造函数,再调用赋值操作符——多一次开销,对 const 成员或引用成员甚至根本不可行。
例如:
立即学习“C++免费学习笔记(深入)”;
class Person {
const std::string name;
int& age_ref;
public:
Person(const std::string& n, int& a) : name(n), age_ref(a) {} // ✅ 必须用初始化列表
// Person(...) { name = n; age_ref = a; } // ❌ 编译错误:const 和引用不能赋值
};容易忽略的点:
- 基类构造必须通过初始化列表调用,不能在函数体里“手动构造”
- 初始化顺序只由成员在类中声明的顺序决定,和初始化列表里的书写顺序无关
- 对于内置类型(如
int x;),初始化列表里不写,它就是未初始化的(值不确定);而写成x(0)才是确定初始化
委托构造函数能减少重复代码
C++11 起支持一个构造函数调用同类的另一个构造函数,称为委托。适用于多个构造函数逻辑高度重合的情况,比如一个带完整参数的构造函数 + 一个只传部分参数的简写版。
语法是把委托调用放在初始化列表首位,用 this(...):
class Buffer {
char* data_;
size_t size_;
public:
Buffer(size_t s) : data_(new char[s]), size_(s) {}
Buffer() : Buffer(1024) {} // 委托给上面那个,等价于默认分配 1KB
};注意限制:
- 委托构造函数不能同时有初始化列表(除了
this(...)) - 不能循环委托(A 委托 B,B 又委托 A)
- 被委托的构造函数仍会执行自己的函数体,所以别在两者里都做资源分配
隐式转换可能引发意外构造,用 explicit 控制
单参数构造函数默认允许隐式转换。比如 String s = "hello"; 会悄悄调用 String(const char*) 构造函数。这在函数重载或容器插入时容易出问题。
加上 explicit 就强制显式调用:
class String {
public:
explicit String(const char* s); // 禁止 String s = "abc";
};
String s1("abc"); // ✅ 显式调用 OK
String s2 = "abc"; // ❌ 编译错误
现代 C++ 实践中,除非你明确需要隐式转换(比如 std::chrono::seconds 接受整数),否则单参数构造函数都建议加 explicit。
最容易被忽略的是:移动构造函数也适用 explicit,尤其当你要避免临时对象被悄悄绑定到非 const 引用时。


















