个人总结:用类的一个对象去初始化另一个对象时会调用拷贝构造函数,浅拷贝直接复制值,深拷贝需要单独分配一块内存然后再将内容复制过去。自定义结构体或类当中的默认拷贝构造函数和赋值运算符为浅拷贝,如果存在指针变量则只会复制指针的值,而不会复制指针所指向的内存,这会导致两个问题:
1、修改一个指针变量指向的内存,会导致另一个指针变量指向这块内存也发生修改,开发过程中可能会出现错误。
2、释放一个指针变量指向的内存后,另一个指针变量指向的内存为空,重复释放会导致未定义的错误。
解决方案:
1、手动编写深拷贝构造函数、赋值运算符以及析构函数。
2、使用std::unique_ptr可以独占内存,禁止浅拷贝。
3、使用std::shared_ptr可以避免重复释放指针内存,避免出现错误。
class String {
private:
char* data;
int size;
public:
// 深拷贝构造函数
String(const String& other) {
size = other.size;
data = new char[size + 1];
memcpy(data, other.data, size + 1);
}
// 深拷贝赋值运算符
String& operator=(const String& other) {
if (this != &other) { // 避免自赋值
delete[] data;
size = other.size;
data = new char[size + 1];
memcpy(data, other.data, size + 1);
}
return *this;
}
// 析构函数
~String() {
delete[] data;
}
}
在 C++ 中,拷贝行为(浅拷贝或深拷贝)主要取决于类型的实现方式,特别是该类型是否拥有需要管理的资源(例如,动态分配的内存)。
默认浅拷贝的类型:
- 基本数据类型(Primitive Data Types):
int
,float
,double
,char
,bool
,enum
等。 这些类型直接存储值,拷贝时直接复制值即可。 - 结构体 (structs) 和类 (classes) (默认情况): 如果结构体或类没有自定义拷贝构造函数、拷贝赋值运算符、移动构造函数、移动赋值运算符和析构函数,那么默认的拷贝行为是浅拷贝。这意味着成员变量的值会被逐个复制,如果成员变量是指针,则指针的值(地址)会被复制,而不是指针指向的内容。
- 数组 (Arrays): C++ 中的数组在拷贝时通常是浅拷贝,直接复制数组元素的值。 但是,如果数组的元素是指针,那么仍然只是复制指针的值。
默认深拷贝的类型:
std::string
: 如前所述,std::string
提供了深拷贝语义。std::vector
:std::vector
也提供了深拷贝语义。 拷贝一个std::vector
会创建一个新的 vector,并将原始 vector 中的元素复制到新的 vector 中。- 其他标准库容器 (Standard Library Containers):
std::list
,std::deque
,std::map
,std::set
等标准库容器通常都提供深拷贝语义。 拷贝这些容器会创建新的容器,并将原始容器中的元素复制到新的容器中。 - 智能指针 (
std::unique_ptr
,std::shared_ptr
): 虽然智能指针本身是指针,但它们提供了更安全的资源管理方式。std::unique_ptr
: 拷贝是被禁止的 (通过= delete
)。它强制执行独占所有权。 移动操作(使用std::move
)会将所有权从一个unique_ptr
转移到另一个unique_ptr
。std::shared_ptr
: 拷贝会增加引用计数。 多个shared_ptr
可以指向同一块内存,当最后一个shared_ptr
销毁时,才会释放内存。 这不是严格意义上的深拷贝,而是共享所有权。
需要特别注意的情况:
- 包含指针成员的自定义类/结构体: 如果你的类或结构体包含指针成员,并且这些指针指向动态分配的内存,那么默认的浅拷贝行为可能会导致问题。 你需要自定义拷贝构造函数、拷贝赋值运算符、移动构造函数、移动赋值运算符和析构函数来实现深拷贝或采取其他适当的资源管理策略。
总结:
类型 | 拷贝行为 | 说明 |
---|---|---|
基本数据类型 | 浅拷贝 | 直接复制值 |
默认的 struct/class | 浅拷贝 | 逐个复制成员变量的值。 如果成员变量是指针,则复制指针的值。 |
数组 | 浅拷贝 | 复制数组元素的值。如果数组元素是指针,则复制指针的值。 |
std::string | 深拷贝 | 创建新的字符串缓冲区,并将原始字符串的内容复制到新的缓冲区中。 |
std::vector | 深拷贝 | 创建新的 vector,并将原始 vector 中的元素复制到新的 vector 中。 |
其他标准库容器 | 深拷贝 | 创建新的容器,并将原始容器中的元素复制到新的容器中。 |
std::unique_ptr | 禁止拷贝 | 强制独占所有权。 移动操作转移所有权。 |
std::shared_ptr | 共享所有权 | 拷贝会增加引用计数。 多个 shared_ptr 可以指向同一块内存,当最后一个 shared_ptr 销毁时,才会释放内存。 |
包含指针成员的自定义类/结构体 | 默认浅拷贝,需要自定义拷贝构造函数等来实现深拷贝或采取其他资源管理策略 |
关键点:
- 了解你的类/结构体是否拥有需要管理的资源。
- 如果拥有资源,则需要考虑拷贝行为,并根据实际情况选择浅拷贝、深拷贝、禁止拷贝或使用智能指针。
- 遵循 Rule of Five (或 Rule of Zero) 来确保资源管理的正确性。