Bỏ qua để đến nội dung

Sao chép nông vs Sao chép sâu

Khi một struct/class chứa con trỏ, phép gán mặc định (=) chỉ sao chép địa chỉ mà con trỏ đang giữ, không sao chép dữ liệu mà nó trỏ đến - đây gọi là sao chép nông (shallow copy):

#include <iostream>
struct Buffer {
int* data;
Buffer(int value) {
data = new int(value);
}
};
int main() {
Buffer a(10);
Buffer b = a; // Sao chép NÔNG - b.data trỏ đến CÙNG địa chỉ với a.data!
*b.data = 100; // Thay đổi thông qua b...
std::cout << *a.data << std::endl; // 100 (!) - a cũng bị ảnh hưởng, dù không ai đụng vào a
return 0;
}

Nguy hiểm hơn: double free khi cả hai bị hủy

Phần tiêu đề “Nguy hiểm hơn: double free khi cả hai bị hủy”
struct Buffer {
int* data;
Buffer(int value) { data = new int(value); }
~Buffer() { delete data; } // Destructor tự động giải phóng
};
int main() {
Buffer a(10);
Buffer b = a; // Sao chép nông - cả a.data và b.data trỏ CÙNG một địa chỉ
return 0;
} // Khi a và b bị hủy: delete data ĐƯỢC GỌI HAI LẦN trên CÙNG địa chỉ -> double free, crash!

Sao chép sâu tạo ra một vùng nhớ mới, hoàn toàn độc lập, chứa cùng giá trị - thường được thực hiện bằng cách tự định nghĩa “copy constructor”:

#include <iostream>
struct Buffer {
int* data;
Buffer(int value) {
data = new int(value);
}
// Copy constructor tùy chỉnh - thực hiện sao chép SÂU
Buffer(const Buffer& other) {
data = new int(*other.data); // Cấp phát vùng nhớ MỚI, sao chép GIÁ TRỊ
}
~Buffer() {
delete data;
}
};
int main() {
Buffer a(10);
Buffer b = a; // Giờ gọi copy constructor tùy chỉnh -> sao chép sâu
*b.data = 100;
std::cout << *a.data << std::endl; // 10 (không đổi - a và b hoàn toàn độc lập)
return 0;
}

Cách hiện đại: dùng smart pointer thay vì con trỏ thô

Phần tiêu đề “Cách hiện đại: dùng smart pointer thay vì con trỏ thô”

Trong C++ hiện đại, cách tốt nhất để tránh toàn bộ lớp lỗi này là dùng std::unique_ptr/std::shared_ptr thay vì con trỏ thô - unique_ptr thậm chí còn chặn hoàn toàn việc sao chép ngầm định, buộc bạn phải xử lý tường minh.

Sao chép nông: chỉ sao chép địa chỉ con trỏ, khiến hai đối tượng “chia sẻ” cùng vùng nhớ một cách ngoài ý muốn. Sao chép sâu: tạo vùng nhớ mới, hai đối tượng hoàn toàn độc lập.

  • Struct/class chứa con trỏ thô cần tự định nghĩa copy constructor để thực hiện sao chép sâu
  • Dùng smart pointer hoặc std::vector/std::string để tránh lớp lỗi này gần như hoàn toàn