『深度编码』C++中的四种构造函数
在 C++ 中,构造函数用于在创建对象时初始化对象的状态。构造函数有几种不同的类型,包括默认构造函数、一般构造函数(带参数的构造函数)、拷贝构造函数和移动构造函数。
一、默认构造函数:
它可以在没有提供任何初始化参数的情况下被调用,用于对类的数据成员进行默认初始化。
特点:
没有参数,或者有默认参数。
如果用户没有显式定义默认构造函数,编译器会自动生成一个。
通常用来初始化对象的成员变量为默认值。
生成条件:
当类中没有显式定义任何构造函数时,编译器会自动生成默认构造函数。
代码示例:
class MyClass {
public:int x;MyClass() { // 默认构造函数x = 0; // 初始化 x 为 0}
};
二、一般构造函数(带参数的构造函数):
通过接受不同的参数,实现对类的数据成员进行灵活的初始化,以满足各种具体场景的需求。
特点:
有一个或多个参数,用来初始化对象的状态。
对象创建时必须提供相应的参数。
生成条件:
编译器永远不会自动生成带参数的一般构造函数。带参数的构造函数必须由用户显式定义,用于满足自定义初始化需求(如传入参数初始化成员变量)。可以重载多个带参数的构造函数(参数个数或类型不同),编译器不干涉。
代码示例:
class MyClass {
public:int x;// 一般构造函数,带有参数MyClass(int val) {x = val; // 使用参数初始化成员变量}
};int main() {MyClass obj(10); // 调用带参数的构造函数std::cout << obj.x << std::endl; // 输出 10
}
三、拷贝构造函数:
用于创建一个与现有对象完全相同的新对象,例如在对象的复制操作中发挥作用。
特点:
接受一个同类型的常量引用作为参数,通常用于复制一个对象。
生成条件:
当类中没有显式定义拷贝构造函数,且没有显式定义移动构造函数和移动赋值运算符时,编译器会自动生成默认拷贝构造函数。若显式定义了拷贝构造函数,编译器不再生成默认版本。若显式定义了移动构造函数或移动赋值运算符,编译器会抑制默认拷贝构造函数的生成(因为移动语义的显式定义暗示 “不希望默认拷贝”)。默认拷贝构造函数执行浅拷贝(逐成员复制,包括指针地址)。
代码示例:
class MyClass {
public:int x;// 一般构造函数MyClass(int val) {x = val;}// 拷贝构造函数MyClass(const MyClass &other) {x = other.x; // 使用另一个对象的值进行初始化}
};int main() {MyClass obj1(10); // 创建 obj1,调用一般构造函数MyClass obj2 = obj1; // 使用 obj1 初始化 obj2,调用拷贝构造函数std::cout << obj2.x << std::endl; // 输出 10
}
四、移动构造函数(C++11 引入):
用于处理右值引用,提高资源移动的效率,特别是在涉及临时对象等情况时。
特点:
接受一个右值引用作为参数,通常用于将临时对象的资源转移给新对象。
生成条件:
当类中没有显式定义移动构造函数,且没有显式定义拷贝构造函数、拷贝赋值运算符、析构函数时,编译器会自动生成默认移动构造函数。上述四个函数(拷贝构造、拷贝赋值、析构、移动构造)中,若显式定义了任意一个,编译器不再自动生成默认移动构造函数(C++11 规则,称为 “移动操作抑制”)。默认移动构造函数执行浅移动(直接转移成员资源,如指针地址,然后将源对象成员置空),仅对 “可移动” 的成员有效(如指针、std::string 等)。
示例代码:
class MyClass {
public:int* data;// 一般构造函数MyClass(int val) {data = new int(val); // 动态分配内存}// 移动构造函数MyClass(MyClass&& other) noexcept {data = other.data; // 转移资源other.data = nullptr; // 使原对象处于有效但空状态}// 析构函数~MyClass() {delete data; // 释放动态分配的内存}
};int main() {MyClass obj1(10); // 创建 obj1,调用一般构造函数MyClass obj2 = std::move(obj1); // 使用移动构造函数将资源从 obj1 移动到 obj2std::cout << *obj2.data << std::endl; // 输出 10
}