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

Giải phóng bộ nhớ hai lần (Double Free)

Gọi delete hai lần trên cùng một con trỏ (mà không cấp phát lại ở giữa) là undefined behavior, thường dẫn đến crash chương trình:

int* ptr = new int(42);
delete ptr; // Giải phóng lần 1 - OK
delete ptr; // Giải phóng lần 2 - NGUY HIỂM! Vùng nhớ này đã được giải phóng trước đó

Sau lần delete đầu tiên, vùng nhớ đó có thể đã được hệ thống cấp phát cho một mục đích hoàn toàn khác. Lần delete thứ hai có thể vô tình giải phóng vùng nhớ đang được dùng bởi một phần khác của chương trình - dẫn đến lỗi khó lường ở một nơi hoàn toàn không liên quan.

Trường hợp thực tế dễ mắc phải: hai con trỏ cùng trỏ một vùng nhớ

Phần tiêu đề “Trường hợp thực tế dễ mắc phải: hai con trỏ cùng trỏ một vùng nhớ”
int* a = new int(42);
int* b = a; // b và a CÙNG trỏ đến một vùng nhớ (không phải sao chép giá trị được trỏ đến!)
delete a; // Giải phóng vùng nhớ
delete b; // DOUBLE FREE! b vẫn đang trỏ đến vùng nhớ mà a vừa giải phóng

Đây thực chất là hệ quả của “sao chép nông” đã học ở bài trước - khi hai con trỏ vô tình trỏ chung một địa chỉ, cả hai đều có “trách nhiệm” giải phóng nó, dẫn đến giải phóng hai lần.

int* ptr = new int(42);
delete ptr;
ptr = nullptr; // Quan trọng!
// delete nullptr; là AN TOÀN - không làm gì cả, không gây lỗi
delete ptr; // An toàn vì ptr đã là nullptr

Đây là lý do vì sao thói quen “gán nullptr ngay sau delete” luôn được khuyến khích - delete trên một con trỏ null được chuẩn C++ đảm bảo là an toàn tuyệt đối (không làm gì cả).

Giải pháp 2: xác định rõ ai là “chủ sở hữu”

Phần tiêu đề “Giải pháp 2: xác định rõ ai là “chủ sở hữu””
int* a = new int(42);
int* b = a; // b chỉ là một cách tham chiếu tạm thời, KHÔNG sở hữu vùng nhớ này
delete a; // Chỉ CHỦ SỞ HỮU (a) mới được delete
a = nullptr;
b = nullptr; // b không delete, chỉ đơn giản không dùng đến nữa

std::unique_ptr khiến double free gần như không thể xảy ra, vì nó không cho phép sao chép (chỉ có thể move), nên không thể có hai đối tượng cùng “nghĩ mình sở hữu” một vùng nhớ:

#include <memory>
auto a = std::make_unique<int>(42);
// auto b = a; // LỖI BIÊN DỊCH! Không thể sao chép unique_ptr - ngăn chặn double free ngay từ khi biên dịch
auto b = std::move(a); // OK - chuyển quyền sở hữu, giờ chỉ b hợp lệ, a là nullptr

Double free: gọi delete hai lần trên cùng một vùng nhớ - undefined behavior, thường gây crash hoặc phá hỏng dữ liệu không liên quan.

  • Thường xảy ra khi hai con trỏ vô tình trỏ chung một địa chỉ (hệ quả của sao chép nông)
  • Luôn gán nullptr sau khi delete - delete nullptr luôn an toàn
  • unique_ptr ngăn chặn double free ngay từ khi biên dịch, vì không cho phép sao chép