Cấp phát động và quản lý bộ nhớ trong C++

Trong C++, ngoài việc khai báo biến với kích thước cố định tại thời điểm biên dịch, chúng ta còn có thể cấp phát bộ nhớ động tại thời điểm chạy chương trình (runtime). Điều này giúp viết chương trình linh hoạt hơn khi kích thước dữ liệu không biết trước, như khi làm việc với mảng lớn hoặc cấu trúc dữ liệu động như danh sách liên kết, cây, đồ thị.


1. Khái niệm cấp phát động (dynamic allocation)

Cấp phát động cho phép chương trình yêu cầu bộ nhớ tại thời điểm chạy, thay vì khai báo tĩnh từ trước. C++ cung cấp hai toán tử chính:

  • new để cấp phát bộ nhớ.
  • delete để giải phóng bộ nhớ.

Khi dùng new, bộ nhớ được lấy từ heap, và chương trình chịu trách nhiệm giải phóng nó bằng delete. Nếu không, sẽ gây rò rỉ bộ nhớ (memory leak).


2. Cấp phát và giải phóng bộ nhớ với con trỏ

Ví dụ cấp phát một số nguyên:

int* p = new int;    // cấp phát một ô nhớ cho một số nguyên
*p = 42;             // gán giá trị

delete p;            // giải phóng bộ nhớ

Ví dụ cấp phát một mảng động:

int* arr = new int[10];  // cấp phát mảng 10 phần tử

for (int i = 0; i < 10; ++i)
    arr[i] = i * i;

delete[] arr;            // giải phóng mảng

Lưu ý: với mảng, dùng delete[] thay vì delete.


3. Kiểm tra lỗi khi cấp phát

Khi new không cấp phát được bộ nhớ, nó có thể ném ngoại lệ std::bad_alloc. Có thể bắt lỗi như sau:

try {
    int* p = new int[10000000000];
} catch (std::bad_alloc& e) {
    std::cerr << "Lỗi cấp phát: " << e.what() << 'n';
}

4. Cấp phát động với kiểu dữ liệu tùy biến

Bạn có thể cấp phát động cho bất kỳ kiểu dữ liệu nào, kể cả cấu trúc:

struct Point {
    int x, y;
};

Point* p = new Point{3, 4};
std::cout << p->x << ", " << p->y << 'n';
delete p;

5. Lỗi thường gặp khi dùng cấp phát động

  • Rò rỉ bộ nhớ: quên delete sau khi new.
  • Giải phóng bộ nhớ chưa cấp phát: dẫn tới lỗi không xác định.
  • Dùng delete thay vì delete[] với mảng.
  • Sử dụng con trỏ sau khi đã delete: lỗi truy cập bộ nhớ.

6. Smart Pointer (giới thiệu sơ lược)

Từ C++11 trở đi, có thể dùng smart pointer như std::unique_ptr, std::shared_ptr để tự động quản lý bộ nhớ thay vì phải delete thủ công. Smart pointer giúp tránh rò rỉ bộ nhớ và lỗi giải phóng trùng lặp.


Bài sau: Giới thiệu con trỏ thông minh và cách dùng unique_ptr, shared_ptr để quản lý tài nguyên an toàn hơn trong C++.