C++ 6种构造函数简化记忆
对C++构造函数的记忆,不管对于在 C++老兵还是新手,都是一个很头痛的问题。新手困惑于构造函数种类繁多,应用场景各异。老手则困惑于记过则忘,难以长久。今天花点时间,简单做一个汇总,方便新手理解,同时也方便老手记忆。
构造函数(Constructor)是类的特殊成员函数,主要用于控制对象的初始化过程。其名称与类名相同,无返回类型(包括 void
)。根据功能和设计场景的不同,C++ 支持多达6种类型的构造函数。
以下是对这6种构造函数类型的压缩简化版本:
1. 默认构造函数(Default Constructor)
定义:无参构造函数,或所有参数均有默认值的构造函数。
作用:当创建对象时未显式提供初始化参数时,编译器自动调用默认构造函数完成对象的初始化。
特点:
若类中未显式定义任何构造函数,编译器会自动生成一个隐式默认构造函数(行为为空,仅对内置类型(如
int
、指针)不做初始化,对类类型成员调用其默认构造函数)。若类中显式定义了其他构造函数(如参数化构造函数),编译器不再自动生成隐式默认构造函数(需手动定义或使用
= default
显式保留)。
示例:
class Sensor {
public:// 隐式默认构造函数(编译器自动生成)// Sensor() = default; // 显式保留默认构造函数(可选)// 显式定义的默认构造函数(无参)Sensor() : id(0), is_active(false) {}private:int id;bool is_active;
};// 使用场景:创建对象时不传递参数
Sensor s1; // 调用默认构造函数
2. 参数化构造函数(Parameterized Constructor)
定义:带有至少一个参数的构造函数。
作用:允许通过传递不同参数初始化对象,满足多样化的初始化需求。
特点:
是最常用的构造函数类型,用于为对象成员赋予初始值。
可重载多个参数化构造函数(参数类型或数量不同)。
示例:
class Sensor {
public:// 参数化构造函数(带两个参数)Sensor(int id, bool active) : id(id), is_active(active) {}private:int id;bool is_active;
};// 使用场景:创建对象时传递参数初始化
Sensor s2(101, true); // id=101, is_active=true
3. 拷贝构造函数(Copy Constructor)
定义:参数为同类型对象的常量引用(const T&
)的构造函数。
作用:用一个已存在的对象(源对象)初始化一个新对象(目标对象),实现对象的复制。
关键细节:
若未显式定义,编译器会生成隐式拷贝构造函数,其行为是浅拷贝(逐成员复制,对指针成员仅复制地址,不复制指向的内存)。
若类包含动态分配的资源(如堆内存、文件句柄),需显式定义深拷贝的拷贝构造函数,避免多个对象共享同一资源导致重复释放等问题。
示例(浅拷贝 vs 深拷贝):
// 浅拷贝问题示例
class Buffer {
public:Buffer(size_t size) : size(size), data(new uint8_t[size]) {} // 参数化构造函数~Buffer() { delete[] data; } // 析构函数释放内存// 隐式拷贝构造函数(浅拷贝):仅复制 data 指针地址// Buffer(const Buffer&) = default; // 显式深拷贝构造函数Buffer(const Buffer& other) : size(other.size), data(new uint8_t[other.size]) {memcpy(data, other.data, size); // 复制数据内容}private:size_t size;uint8_t* data;
};// 使用场景:对象复制(如传值、返回对象、初始化列表)
Buffer buf1(1024); // 参数化构造函数
Buffer buf2 = buf1; // 调用拷贝构造函数(若未定义深拷贝会导致析构时重复释放)
4. 移动构造函数(Move Constructor,C++11 引入)
定义:参数为同类型对象的右值引用(T&&
)的构造函数。
作用:将源对象(通常是临时对象或即将销毁的对象)的资源“转移”到新对象,避免不必要的复制,提升性能。
关键细节:
若未显式定义,编译器仅在类未显式定义拷贝构造、拷贝赋值、析构函数时,自动生成隐式移动构造函数(行为是将源对象的成员逐个“移动”,对指针成员直接复制地址,并将源对象指针置空)。
移动构造后,源对象进入“有效但未定义”状态(通常可安全析构或重新赋值)。
示例:
class DynamicArray {
public:DynamicArray(size_t size) : size(size), data(new int[size]) {} // 参数化构造函数~DynamicArray() { delete[] data; }// 隐式移动构造函数(编译器自动生成,若未定义其他特殊成员)// DynamicArray(DynamicArray&& other) noexcept // : size(other.size), data(other.data) {// other.size = 0;// other.data = nullptr; // 关键:置空源对象指针,避免重复释放// }// 显式移动构造函数(优化资源转移)DynamicArray(DynamicArray&& other) noexcept : size(std::exchange(other.size, 0)), data(std::exchange(other.data, nullptr)) {}private:size_t size;int* data;
};// 使用场景:利用临时对象初始化新对象(如 std::move 或函数返回临时对象)
DynamicArray arr1(100);
DynamicArray arr2 = std::move(arr1); // 调用移动构造函数,arr1 的资源转移给 arr2
5. 委托构造函数(Delegating Constructor,C++11 引入)
定义:一个构造函数通过其初始化列表调用同类的另一个构造函数。
作用:减少重复初始化代码,集中维护初始化逻辑。
关键细节:
委托构造函数的初始化列表只能调用一个同类的其他构造函数,不能同时初始化成员变量。
避免循环委托(如构造函数 A 调用 B,B 又调用 A),否则会导致编译错误。
示例:
class Sensor {
public:// 基础构造函数(初始化核心参数)Sensor(int id) : id(id), is_active(true), type("Unknown") {}// 委托构造函数:调用基础构造函数,并初始化额外成员Sensor(int id, const std::string& type) : Sensor(id) { // 委托给上一个构造函数this->type = type; // 初始化额外成员}private:int id;bool is_active;std::string type;
};// 使用场景:多个构造函数共享部分初始化逻辑
Sensor s3(202); // 调用 Sensor(int id)
Sensor s4(203, "Camera"); // 调用委托构造函数,最终调用 Sensor(int id)
6. 转换构造函数(Converting Constructor)
定义:单参数的构造函数(或多参数但除第一个外均有默认值),允许将参数类型隐式转换为类类型。
作用:简化类型转换,使代码更简洁。
注意事项:
若不需要隐式转换,建议用
explicit
关键字修饰,强制要求显式调用构造函数,避免意外的类型转换。
示例:
class Temperature {
public:// 转换构造函数(单参数,允许 int 隐式转换为 Temperature)explicit Temperature(int celsius) : celsius(celsius) {} // explicit 禁止隐式转换// 若去掉 explicit:// Temperature(int celsius) : celsius(celsius) {}// 则 int 类型可隐式转换为 Temperature 对象,例如:Temperature t = 25;(可能不符合预期)private:int celsius;
};// 使用场景(显式调用):
Temperature t1(25); // 正确:显式调用构造函数
// Temperature t2 = 30; // 错误:因 explicit 禁止隐式转换,需显式调用 Temperature t2(30);
总结表格
构造函数类型 | 核心作用 | 关键特性 | 典型场景 |
---|---|---|---|
默认构造函数 | 无参初始化对象 | 编译器自动生成(若未定义其他构造) | 对象无参创建 |
参数化构造函数 | 多参数初始化对象 | 可重载,满足多样化初始化需求 | 对象带参创建 |
拷贝构造函数 | 用已有对象复制初始化新对象 | 需处理深拷贝(避免资源冲突) | 对象复制(传值、返回等) |
移动构造函数(C++11) | 转移资源而非复制,提升性能 | 右值引用参数,源对象资源被“窃取” | 临时对象初始化、资源转移 |
委托构造函数(C++11) | 减少重复初始化代码 | 调用同类其他构造函数 | 多构造函数共享逻辑 |
转换构造函数 | 允许参数类型隐式转换为类类型 | 单参数(或部分默认参数),可用 | 类型简化转换 |
注意:在实际开发(如嵌入式 Linux 或 Qt 应用)中,需根据类的资源管理需求(如是否持有堆内存、文件句柄)选择是否自定义拷贝/移动构造函数,避免资源泄漏或重复释放。对于仅需唯一实例的类(如硬件驱动句柄),可禁用拷贝构造函数(= delete
)。
惠州西湖