在c++编程中,数组的深度拷贝指的是将原数组的所有元素完整复制到新的内存空间中,修改新数组不会影响原数组的数据。实现数组深度拷贝的方式有多种,其中std::copy和memcpy是两种常用的手段,二者在使用场景和特性上存在明显差异。

c++中实现数组深度拷贝的基础方法
对于普通的内置类型数组,深度拷贝的核心是为目标数组分配独立的内存空间,然后将原数组的每个元素逐个复制到目标空间中。如果是自定义类型的数组,还需要确保拷贝过程能正确触发自定义类型的拷贝逻辑,避免浅拷贝带来的问题。
普通内置类型数组的深度拷贝示例
以下是使用循环实现int类型数组深度拷贝的基础示例:
#include <iostream>
int main() {
int src_arr[5] = {1, 2, 3, 4, 5};
int dest_arr[5];
// 循环逐个拷贝元素,实现深度拷贝
for (int i = 0; i < 5; i++) {
dest_arr[i] = src_arr[i];
}
// 修改目标数组验证是否深度拷贝
dest_arr[0] = 100;
std::cout << "原数组第一个元素: " << src_arr[0] << std::endl;
std::cout << "目标数组第一个元素: " << dest_arr[0] << std::endl;
return 0;
}
std::copy的用法与特性
std::copy是c++标准库
std::copy拷贝数组示例
使用std::copy实现上述int数组的深度拷贝代码如下:
#include <iostream>
#include <algorithm>
int main() {
int src_arr[5] = {1, 2, 3, 4, 5};
int dest_arr[5];
// 使用std::copy拷贝数组,需要传入起始迭代器、结束迭代器、目标起始迭代器
std::copy(std::begin(src_arr), std::end(src_arr), std::begin(dest_arr));
dest_arr[0] = 100;
std::cout << "原数组第一个元素: " << src_arr[0] << std::endl;
std::cout << "目标数组第一个元素: " << dest_arr[0] << std::endl;
return 0;
}
std::copy的优势在于它是类型安全的,对于自定义类型数组,只要自定义类型实现了正确的拷贝赋值运算符,std::copy就能正确完成深度拷贝,不会像memcpy那样可能出现浅拷贝问题。
memcpy的用法与特性
memcpy是c标准库
memcpy拷贝数组示例
使用memcpy实现int数组深度拷贝的代码如下:
#include <iostream>
#include <cstring>
int main() {
int src_arr[5] = {1, 2, 3, 4, 5};
int dest_arr[5];
// 使用memcpy拷贝,需要传入目标地址、源地址、拷贝的总字节数
memcpy(dest_arr, src_arr, sizeof(src_arr));
dest_arr[0] = 100;
std::cout << "原数组第一个元素: " << src_arr[0] << std::endl;
std::cout << "目标数组第一个元素: " << dest_arr[0] << std::endl;
return 0;
}
memcpy的拷贝效率通常比std::copy更高,因为它直接操作内存字节,没有额外的赋值逻辑开销,但它仅适用于平凡可复制(trivially copyable)的类型,对于包含指针、虚函数表或者自定义拷贝逻辑的自定义类型,使用memcpy会导致浅拷贝,引发内存问题。
std::copy与memcpy的核心区别
二者的差异主要体现在以下几个方面:
- 类型安全性:std::copy是类型安全的,会调用元素类型的赋值运算符;memcpy不关心类型,仅做二进制拷贝,无类型安全检查。
- 适用类型:std::copy适用于所有可赋值的类型,包括自定义类型;memcpy仅适用于平凡可复制的内置类型和符合平凡可复制要求的自定义类型。
- 拷贝逻辑:std::copy会触发元素的正确拷贝逻辑,支持深度拷贝自定义类型;memcpy对自定义类型可能只拷贝指针值,导致浅拷贝。
- 使用方式:std::copy基于迭代器操作,不需要手动计算字节数;memcpy需要手动传入拷贝的总字节数,容易计算错误。
两种方式的适用场景对比
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 内置类型数组拷贝 | 二者均可,追求效率可选memcpy | 内置类型都是平凡可复制的,两种方式都能正确工作 |
| 自定义类型数组拷贝 | std::copy | memcpy可能导致浅拷贝,std::copy能触发正确的拷贝逻辑 |
| 需要类型安全的拷贝操作 | std::copy | memcpy无类型检查,容易出现未定义行为 |
自定义类型拷贝的注意事项
如果要对包含动态内存的自定义类型数组做深度拷贝,一定要使用std::copy,并且为自定义类型实现拷贝赋值运算符和拷贝构造函数。以下是示例:
#include <iostream>
#include <algorithm>
#include <cstring>
class MyString {
private:
char* data;
int len;
public:
// 构造函数
MyString(const char* str = nullptr) {
if (str) {
len = strlen(str);
data = new char[len + 1];
strcpy(data, str);
} else {
len = 0;
data = new char[1];
data[0] = ' ';
}
}
// 拷贝构造函数
MyString(const MyString& other) {
len = other.len;
data = new char[len + 1];
strcpy(data, other.data);
}
// 拷贝赋值运算符
MyString& operator=(const MyString& other) {
if (this == &other) return *this;
delete[] data;
len = other.len;
data = new char[len + 1];
strcpy(data, other.data);
return *this;
}
// 析构函数
~MyString() {
delete[] data;
}
void print() const {
std::cout << data << std::endl;
}
};
int main() {
MyString src_arr[2] = {MyString("hello"), MyString("world")};
MyString dest_arr[2];
// 使用std::copy正确完成自定义类型数组的深度拷贝
std::copy(std::begin(src_arr), std::end(src_arr), std::begin(dest_arr));
dest_arr[0] = MyString("test");
std::cout << "原数组第一个元素: ";
src_arr[0].print();
std::cout << "目标数组第一个元素: ";
dest_arr[0].print();
return 0;
}
如果使用memcpy拷贝上述MyString类型的数组,只会拷贝data指针的值,不会拷贝指针指向的内存内容,导致两个数组的元素指向同一块内存,析构时会出现重复释放的问题。