C++中的对象拷贝机制分为浅拷贝(shallow copy)和深拷贝(deep copy)。对于具有动态分配内存的类,这种差异尤为重要。
在C++中,许多数据结构使用动态内存(new/delete)。默认情况下,编译器会生成一个执行逐字节拷贝的拷贝构造函数和赋值运算符(shallow copy)。这种方法速度快,但如果对象管理外部资源,则会很危险。
浅拷贝仅拷贝动态分配资源的地址。当删除一个对象时,内存将被释放,而另一个实例将保留“悬空”(dangling)指针。因此,会导致双重删除、内存泄漏和崩溃。
深拷贝需要显式地创建所有动态资源的副本。为此,需要在类中手动实现拷贝构造函数和赋值运算符,以保证每个元素的拷贝。
以下是一个包含数组的类的代码示例:
class DynArray { int* data; size_t size; public: DynArray(size_t n) : size(n), data(new int[n]) {} ~DynArray() { delete[] data; } // 深拷贝构造函数 DynArray(const DynArray& other) : size(other.size), data(new int[other.size]) { for (size_t i = 0; i < size; ++i) data[i] = other.data[i]; } // 深拷贝赋值运算符 DynArray& operator=(const DynArray& other) { if (this != &other) { delete[] data; size = other.size; data = new int[size]; for (size_t i = 0; i < size; ++i) data[i] = other.data[i]; } return *this; } };
关键特性:
编译器总是正确生成拷贝构造函数和赋值运算符,对吗?
答案:
错误。对于具有动态资源的类,默认的拷贝是不正确的:两个对象将共享同一资源。需要在拥有外部资源时显式实现深拷贝。
如果只实现了深拷贝构造函数/赋值运算符,是否需要实现析构函数?
答案:
是的,否则会导致内存泄漏:如果在用户的拷贝构造函数中释放内存,但没有实现析构函数,内存将不会在对象销毁时被释放。
std::vector可以存储指针吗?为什么在其拷贝时可能会发生内存泄漏?
答案:
可以,std::vector可以安全地存储指针。在拷贝这样的std::vector时,拷贝的是指针本身,而不是它们所指向的对象。这是浅拷贝:如果需要深拷贝所有内容,则需要手动拷贝每个对象并在内存中独立存放。
示例:
std::vector<int*> v1; v1.push_back(new int(42)); std::vector<int*> v2 = v1; // 拷贝的是指针,而不是 *int
程序员实现了一个数组的包装类,但没有重写拷贝构造函数/赋值运算符。结果,两个对象拥有同一块内存,销毁一个时会导致访问另一个时崩溃。
优点:
缺点:
开发者实现了深拷贝:拷贝数组的内容,拥有自己的析构函数和防止自我赋值的赋值运算符。
优点:
缺点: