委托构造函数必须在成员初始化列表中调用同一类的其他构造函数,如a() : a(42) {};不可在函数体内调用、不可跨类或基类、不可循环委托、不可与基类初始化共存,且委托后不能再初始化成员。

委托构造函数怎么写才合法
委托构造函数必须在成员初始化列表里调用,不能放在函数体里;否则编译器直接报错 error: constructor delegation outside of member initializer list。
常见错误是把委托写成普通函数调用:
class A {
public:
A(int x) : val(x) {}
A() {
A(42); // ❌ 错误!这只是临时对象,不委托
}
private:
int val;
};
- ✅ 正确写法:
A() : A(42) {}—— 冒号后紧跟被委托的构造函数调用 - ❌ 不能带
this->或作用域符:this->A(42)或A::A(42)都非法 - 委托目标必须是同一类的其他构造函数,不能跨继承链或调用基类构造函数(那是基类初始化的事)
委托链不能形成循环
编译器会静态检查委托关系,一旦发现 A→B→A 这类闭环,立刻拒绝编译,报错类似 error: delegated constructor calls itself。
典型踩坑场景是参数类型隐式转换干扰判断:
立即学习“C++免费学习笔记(深入)”;
class B {
public:
B(int x) : B(static_cast<double>(x)) {} // ✅ int → double,调用 B(double)
B(double x) : B(static_cast<int>(x)) {} // ❌ 编译失败:B(double) 试图调回 B(int),但 int 转 double 后又可能转回来,实际形成隐式循环
};
- 委托链必须是单向、无环、编译期可判定的
- 避免在委托中依赖用户自定义转换运算符或非 explicit 构造函数,容易让编译器“绕晕”
- 如果真需要多路径初始化逻辑,优先提取为 private 初始化函数(
init()),而非强行塞进委托链
委托之后不能再初始化成员或调用基类构造
被委托的构造函数全权负责所有初始化工作;委托者构造函数的成员初始化列表必须为空,且不能同时指定基类初始化。
比如下面这段代码会编译失败:
class C : public Base {
public:
C() : Base(), C(0) {} // ❌ 错误:既调基类又委托,二者互斥
C(int x) : val(x) {}
private:
int val;
};
- ✅ 正确做法:把基类初始化移到被委托的那个构造函数里,例如
C(int x) : Base(), val(x) {} - 委托构造函数体内可以执行运行期逻辑(如日志、校验),但不能再碰成员变量初始化 —— 它们已被上游构造函数处理过了
- 注意:委托不改变对象生命周期起点,
this在整个委托链中始终指向同一个对象内存地址
和默认/拷贝/移动构造函数共存时的隐式行为
一旦你写了任何构造函数(包括委托构造函数),编译器就不再自动生成默认构造函数;但委托本身不会抑制拷贝或移动构造函数的生成,除非你显式删除或定义了它们。
- 如果你只写了
A(int)和A() : A(0) {},那么A a1;合法,但A a2 = a1;是否合法,取决于编译器是否合成拷贝构造函数 —— 它仍会合成,除非你干预 - 委托构造函数不影响
= default的适用性,你可以安全地写A(const A&) = default; - 容易忽略的一点:委托构造函数的 noexcept 属性不自动继承,需显式标注,否则可能影响容器(如
std::vector)的异常安全策略










