C++11 引入的委托构造函数(delegating constructor)
一、概念与语法
-
委托构造指一个构造函数在其初始化列表中,直接调用同类的另一个构造函数来完成部分(或全部)初始化。
-
语法形式:
class A { public: A(int x, int y) : _x(x), _y(y) { /* 主构造逻辑 */ } // 委托构造:将初始化委托给 A(int,int) A() : A(0, 0) // 委托到上面带参构造 { /* 可留空或做额外逻辑 */ } A(int x) : A(x, 0) // 委托到 A(int,int) { } private: int _x, _y; };
-
只有一个委托目标可写在初始化列表的最开头,而且初始化列表中不能再出现成员或基类初始化,否则编译失败。
二、使用场景
- 消除重复代码
多个构造函数共享“公共初始化”逻辑时,将初始化集中在一个“主构造函数”中,其它构造函数只负责传参及少量收尾工作。 - 统一资源获取/设置
比如打开文件、分配内存、注册回调等步骤应只在一处编写,避免修改遗漏。 - 与默认参数不可兼得时的替代
对于需要在不同分支做不同预处理,再调用主构造,委托构造比重载+默认参数更灵活。
三、执行顺序
假设有三种构造函数链:
A() : A(1) { std::cout<<"A() body\n"; }
A(int x) : A(x, 2) { std::cout<<"A(int)\n"; }
A(int x, int y) { std::cout<<"A(int,int)\n"; }
调 A()
时,调用流程为:
- 调用
A()
的初始化列表,委托到A(int)
; A(int)
的初始化列表,委托到A(int,int)
;- 进入
A(int,int)
——执行成员和基类的初始化,再执行它的函数体; - 返回
A(int)
,执行其函数体; - 返回
A()
,执行其函数体。
输出示例:
A(int,int)
A(int)
A()
四、限制与注意事项
-
只能委托同一类内部的另一个构造
不能跨类委托,也不能在同一初始化列表里同时委托和初始化成员/基类。 -
禁止循环委托
A 委托到 B,B 又委托到 A,会导致无限递归。编译器会报错。 -
初始化列表顺序不变
即使在委托链中先后出现,基类子对象仍旧最先初始化,接着按声明顺序初始化成员。 -
与默认成员初始化配合
- 如果成员在类内已给出默认初始化,主构造函数或其委托目标可覆盖它;
- 非委托目标构造函数不要在初始化列表写成员初始化,以免与委托冲突。
-
异常安全
所有初始化都在主构造函数里发生,如果主构造抛异常,已初始化的成员会被正确析构。 -
可与
explicit
/ 隐式转换结合
可以给委托构造函数加explicit
,防止意外隐式构造。
五、综合示例
#include <iostream>
#include <string>
class Logger {
public:
Logger(const std::string& name, int level)
: _name(name), _level(level)
{
std::cout << "Logger("<<_name<<","<<_level<<")\n";
}
// 委托到 Logger(name, 0)
Logger(const std::string& name)
: Logger(name, 0)
{
std::cout << "Logger(name)\n";
}
// 委托到 Logger("default", 0)
Logger()
: Logger("default")
{
std::cout << "Logger()\n";
}
private:
std::string _name;
int _level;
};
int main() {
Logger L1; // default 构造
Logger L2("network"); // name 构造
Logger L3("db", 3); // 主构造
return 0;
}
输出:
Logger(default,0)
Logger(name)
Logger()
Logger(network,0)
Logger(name)
Logger(db,3)
六、最佳实践
- 明确主构造函数
将最完整、最“重”初始化的构造函数作为主构造,保持其逻辑最全。 - 文档注释
在头文件注明哪个构造是“委托”,以及委托的入口/出口含义。 - 避免成员初始化冲突
除主构造外,其它构造不在初始化列表中出现成员/基类初始化,以免编译器报“不能与委托共存”。 - 链式委托深度适中
链太深可能影响可读性和调试,建议不超过 2~3 级。 - 与默认参数配合
对于可省略的参数,也可用默认参数+委托构造混合方式,让 API 既简洁又灵活。