
C++11 中引入的委托构造函数是一种特殊的构造函数,它允许一个构造函数调用同一个类中的另一个构造函数来初始化对象。这种构造函数在声明时使用符号后跟其他构造函数的初始化列表,而不是在函数体内调用。这种特性可以减少代码冗余,提高代码的可维护性,同时也使得代码结构更加清晰。本文将详细介绍委托构造函数的定义、使用方法、使用场景以及注意事项。
委托构造函数的定义与语法
委托构造函数的语法格式如下:
class MyClass {
public:
MyClass(int param1, int param2) {
// 构造函数的具体实现
}
MyClass(int param) : MyClass(param, 0) {
// 委托给另一个构造函数完成初始化
}
};在上述代码中,第一个构造函数接受两个参数,而第二个构造函数只接受一个参数。第二个构造函数使用了初始化列表的方式,通过委托给第一个构造函数来完成对象的初始化。
语法细节
- 初始化列表:委托构造函数必须出现在成员初始化列表中,并且不能同时存在成员初始化列表和函数体。例如,以下代码是错误的:
class MyClass {
public:
MyClass(int param1, int param2) : param1_(param1), param2_(param2) {
// 正确的初始化
}
MyClass(int param) : MyClass(param, 0), param_(param) { // 错误:不能同时使用委托构造函数和成员初始化列表
// 委托给另一个构造函数完成初始化
}
private:
int param1_;
int param2_;
int param_;
};- 委托链:委托构造函数可以形成一个链,但不能形成闭环。例如,以下代码是正确的:
class MyClass {
public:
MyClass(int param1, int param2, int param3) : param1_(param1), param2_(param2), param3_(param3) {
// 正确的初始化
}
MyClass(int param1, int param2) : MyClass(param1, param2, 0) {
// 委托给另一个构造函数完成初始化
}
MyClass(int param1) : MyClass(param1, 0) {
// 委托给另一个构造函数完成初始化
}
private:
int param1_;
int param2_;
int param3_;
};但以下代码是错误的,因为它形成了一个闭环:
class MyClass {
public:
MyClass(int param1) : MyClass(param1, 0) {
// 委托给另一个构造函数完成初始化
}
MyClass(int param1, int param2) : MyClass(param1) {
// 错误:形成了闭环
}
};委托构造函数的使用场景
C++11 引入委托构造函数的原因主要包括以下几点:
- 减少代码重复:委托构造函数允许一个构造函数调用同一个类中的另一个构造函数,这样可以避免在多个构造函数之间重复相同的初始化代码。
- 提高代码清晰度:通过委托构造函数,可以将类的构造逻辑分解成更小的、更易于管理的部分,使得代码结构更加清晰。
- 增强类的可维护性:当需要修改或扩展类的初始化逻辑时,委托构造函数使得只需在一个地方进行更改,而不是在多个构造函数中重复修改。
- 支持类的灵活设计:委托构造函数提供了一种机制,使得派生类可以重用基类的构造函数,或者在派生类中重用其他构造函数的初始化逻辑。
- 简化构造函数的重载:当类有多个构造函数时,委托构造函数可以简化这些构造函数的实现,特别是当某些构造函数共享相同的初始化步骤时。
委托构造函数的使用示例
下面我们通过两个示例来详细解析委托构造函数的使用。
示例 1:简化初始化逻辑
class MyClass {
public:
MyClass(int a, double b) : a_(a), b_(b) {
// 构造函数的具体实现
std::cout << "MyClass(int, double) called" << std::endl;
}
MyClass(int a) : MyClass(a, 0.0) {
// 委托给另一个构造函数完成初始化
std::cout << "MyClass(int) called" << std::endl;
}
MyClass(int a, double b, std::string c) : a_(a), b_(b), c_(std::move(c)) {
// 构造函数的具体实现
std::cout << "MyClass(int, double, std::string) called" << std::endl;
}
private:
int a_;
double b_;
std::string c_;
};在这个例子中,MyClass(int a) 构造函数委托给 MyClass(int a, double b) 构造函数,将 b 参数设置为 0.0。这样可以避免在多个构造函数中重复初始化 a_ 和 b_。
示例 2:避免代码冗余
class Person {
public:
Person(const std::string& name, int age) : name_(name), age_(age) {
std::cout << "Person(const std::string&, int) called" << std::endl;
}
Person(const std::string& name) : Person(name, 0) {
std::cout << "Person(const std::string&) called" << std::endl;
}
Person() : Person("", 0) {
std::cout << "Person() called" << std::endl;
}
void printInfo() const {
std::cout << "Name: " << name_ << ", Age: " << age_ << std::endl;
}
private:
std::string name_;
int age_;
};在这个例子中,Person(const std::string& name) 构造函数委托给 Person(const std::string& name, int age) 构造函数,将 age 参数设置为 0。Person() 构造函数则委托给 Person(const std::string& name, int age) 构造函数,将 name 参数设置为 “”,age 参数设置为 0。
示例 3:复杂类的初始化
假设有一个类 ComplexClass,它有多个成员变量和多个构造函数,我们可以使用委托构造函数来简化初始化逻辑:
class ComplexClass {
public:
ComplexClass(int a, double b, std::string c, bool d) : a_(a), b_(b), c_(std::move(c)), d_(d) {
std::cout << "ComplexClass(int, double, std::string, bool) called" << std::endl;
}
ComplexClass(int a, double b, std::string c) : ComplexClass(a, b, c, false) {
std::cout << "ComplexClass(int, double, std::string) called" << std::endl;
}
ComplexClass(int a, double b) : ComplexClass(a, b, "", false) {
std::cout << "ComplexClass(int, double) called" << std::endl;
}
ComplexClass(int a) : ComplexClass(a, 0.0, "", false) {
std::cout << "ComplexClass(int) called" << std::endl;
}
private:
int a_;
double b_;
std::string c_;
bool d_;
};在这个例子中,多个构造函数通过委托构造函数的方式,最终调用最完整的构造函数 ComplexClass(int a, double b, std::string c, bool d),这样可以避免在每个构造函数中重复初始化成员变量。
使用委托构造函数的注意事项
在使用委托构造函数时,需要注意以下几点:
- 成员初始化列表:委托构造函数必须出现在成员初始化列表中,并且不能同时存在成员初始化列表和函数体。
- 避免闭环:这种链式的构造函数调用不能形成一个闭环(死循环),否则会在运行期抛异常。
- 默认构造函数:委托构造函数不能用于默认构造函数之间。例如,以下代码是错误的:
class MyClass {
public:
MyClass() : MyClass() { // 错误:形成了闭环
}
};- 委托构造函数的顺序:委托构造函数的调用顺序必须与成员变量的声明顺序一致。例如,以下代码是正确的:
class MyClass {
public:
MyClass(int a, double b) : a_(a), b_(b) {
}
MyClass(double b, int a) : MyClass(a, b) {
}
private:
int a_;
double b_;
};但以下代码是错误的,因为它违反了成员变量的声明顺序:
class MyClass {
public:
MyClass(int a, double b) : a_(a), b_(b) {
}
MyClass(double b, int a) : MyClass(b, a) { // 错误:违反了成员变量的声明顺序
}
private:
int a_;
double b_;
};- 委托构造函数的参数传递:委托构造函数可以传递任何类型的参数,包括临时对象。例如,以下代码是正确的:
class MyClass {
public:
MyClass(int a, double b) : a_(a), b_(b) {
}
MyClass(std::string c) : MyClass(c.length(), 0.0) {
}
private:
int a_;
double b_;
};在这个例子中,MyClass(std::string c) 构造函数将 std::string 对象的长度传递给 MyClass(int a, double b) 构造函数。
总结
委托构造函数是 C++11 的一项重要特性,它通过允许一个构造函数调用同一个类中的另一个构造函数来初始化对象,从而减少了代码冗余,提高了代码的可维护性和清晰度。合理使用委托构造函数可以简化类的构造逻辑,使代码更加简洁和易于管理。同时,理解和掌握委托构造函数的使用,也是提升 C++ 编程技能的重要一步。
















