引言
拷贝构造函数是C++中一个重要的特性,它允许一个对象通过另一个已创建好的同类型对象来初始化。
了解拷贝构造函数的概念、作用、特点、规则、默认行为以及如何自定义实现,对于编写健壮和高效的C++程序至关重要。
C++类和对象系列文章,可点击下方链接阅读:
【C++指南】类和对象(一):类和对象的定义和使用 基础讲解_c++ 类对象的使用-CSDN博客
【C++指南】类和对象(二):类的默认成员函数——全面剖析 :构造函数-CSDN博客
【C++指南】类和对象(三):类的默认成员函数——全面剖析: 析构函数-CSDN博客
【C++指南】类和对象(四):类的默认成员函数——全面剖析 拷贝构造函数-CSDN博客
【C++指南】类和对象(五):类的默认成员函数——全面剖析 赋值运算符重载函数-CSDN博客
目录
引言
🍃概念
🍃作用
🍃特点
触发拷贝函数自动调用的情况:
🍃规则
🍃默认拷贝构造函数的行为
🍃自定义实现拷贝构造函数
🍃总结
🍃概念
如果⼀个构造函数的第⼀个参数是自身类类型的引用,且任何额外的参数都有默认值,则此构造函数 也叫做拷贝构造函数。
拷贝构造函数是一种特殊的构造函数,它接受一个同类型的对象的引用作为参数,用于初始化新创建的对象。
拷贝构造函数的声明通常如下:
ClassName(const ClassName& other);
其中,ClassName是类的名字,other 是传入的对象。
🍃作用
拷贝构造函数的主要作用是实现对象的深拷贝或浅拷贝。
- 深拷贝:在内存中为对象分配新的空间,并复制源对象的所有成员(包括动态分配的内存)。
- 浅拷贝:仅复制对象的成员值,如果成员包含指针,则两个对象将共享相同的内存地址。
关于浅拷贝与深拷贝的详细内容可以参考文章:
【C++指南】C++中的浅拷贝与深拷贝:深入剖析-CSDN博客
🍃特点
- 自动调用:在对象通过另一个对象初始化时,拷贝构造函数会被自动调用。
- 参数传递:拷贝构造函数的参数是常量引用(
const ClassName&
),避免不必要的拷贝,同时防止对象在拷贝过程中被修改。
拷贝构造函数的参数必须是类类型对象的引用,而不是传值方式。主要是因为:
避免无限递归
如果拷贝构造函数的参数是传值方式,那么在调用拷贝构造函数时,编译器会尝试创建一个临时对象来传递给该函数。这个临时对象的创建又会调用拷贝构造函数,从而导致无限递归。最终,这会导致栈溢出和编译错误。
触发拷贝函数自动调用的情况:
1.对象初始化:
- 使用另一个同类型的对象来初始化一个新对象时。
MyClass obj1;
MyClass obj2 = obj1; // 调用拷贝构造函数
2.函数参数传递:
- 当一个对象作为值参数传递给函数时。
void func(MyClass obj) {
// 在这里,obj是通过拷贝构造函数创建的
}
MyClass obj1;
func(obj1); // 调用拷贝构造函数
3.函数返回值:
- 当一个函数返回一个对象(作为值)时,会调用拷贝构造函数来构造返回的对象。
MyClass func() {
MyClass obj;
return obj; // 调用拷贝构造函数
}
4.编译器生成的临时对象:
- 编译器在某些情况下会生成临时对象,例如,在表达式中计算中间结果时。
MyClass obj = MyClass(); // 这里的`MyClass()`创建了一个临时对象,然后调用拷贝构造函数赋值给obj
🍃规则
- 如果类中没有显式定义拷贝构造函数,编译器会提供一个默认的拷贝构造函数。
- 拷贝构造函数不能被声明为
const
,因为它需要修改(初始化)目标对象。 - 拷贝构造函数必须是可访问的,以便在需要时能够被调用。
🍃默认拷贝构造函数的行为
默认拷贝构造函数的行为是逐成员复制,对于基本类型成员,直接复制值,也就是浅拷贝;对于对象成员,调用其拷贝构造函数。
这种默认行为通常适用于不包含动态分配内存或资源管理(如文件句柄、网络连接等)的简单类。
🍃自定义实现拷贝构造函数
当类包含动态分配的内存、指针或需要管理的资源时,必须自定义拷贝构造函数来实现深拷贝,以避免浅拷贝带来的问题(如重复释放内存、数据不一致等)。
Tips:
如果⼀个类显式实现了析构并释放资源,那么他就 需要显式写拷贝构造,否则就不需要
以下是一个包含动态分配内存的类的示例,展示如何自定义拷贝构造函数:
#include <iostream>
#include <cstring>
class MyClass {
private:
char* data;
public:
// 默认构造函数
MyClass(const char* str = "") {
data = new char[strlen(str) + 1];
strcpy(data, str);
}
// 拷贝构造函数
MyClass(const MyClass& other) {
data = new char[strlen(other.data) + 1];
strcpy(data, other.data);
}
// 析构函数
~MyClass() {
delete[] data;
}
// 打印数据
void print() const {
std::cout << data << std::endl;
}
};
int main() {
MyClass obj1("Hello");
MyClass obj2 = obj1; // 调用拷贝构造函数
obj1.print(); // 输出: Hello
obj2.print(); // 输出: Hello
return 0;
}
在这个例子中,MyClass 包含一个指向字符数组的指针。自定义拷贝构造函数通过分配新的内存并复制字符串内容,实现了深拷贝。析构函数负责释放动态分配的内存,防止内存泄漏。
🍃总结
- 拷贝构造函数是C++中用于通过另一个对象初始化新对象的特殊构造函数。
- 它接受一个同类型的常量引用作为参数。
- 如果没有显式定义,编译器会提供一个默认的拷贝构造函数,逐成员复制对象。
- 自定义拷贝构造函数通常用于实现深拷贝,以避免浅拷贝带来的问题。
- 编写拷贝构造函数时,需要特别注意动态分配的内存和需要管理的资源,确保正确复制和释放。
通过理解和应用拷贝构造函数,可以编写出更加健壮和高效的C++程序。