Hãy dùng std::vector thay vì cấp phát động một mảng

Dùng thư viện STL sẽ giúp ích rất nhiều trong việc lập trình trong C++, việc coding sẽ trở nên dễ dàng nếu bạn biết tận dụng các lớp và tính năng mà thư viện STL cung cấp, ở bài này mình sẽ giới thiệu cách sử dụng std::vector thay thế cho việc cấp phát động, trong trường hợp bạn không muốn tạo vùng nhớ trên stack và bạn muốn hủy vùng nhớ sau khi kết thúc hàm hoặc ra ngoài phạm vi của scope giới hạn bằng cặp ngoặc { … }

Cấp phát bộ nhớ theo cách thông thường

Thông thường nếu bạn muốn cấp phát động một vùng nhớ để sử dụng thì bạn sẽ làm như sau:

unsigned char *dataBuffer = new unsigned char[1000];

// Do something here

delete [] dataBuffer;

Việc làm này có một số bất lợi sau:

  • Cú pháp khá phức tạp
  • Quên delete vùng nhớ đã cấp phát
  • Nhầm lẫn giữa delete và delete [] có thể dẫn tới rò rỉ bộ nhớ
  • Việc delete hai lần có thể dẫn tới lỗi không lường trước được
  • Exception trong quá trình xử lý giữa vùng cấp phát và xóa vùng nhớ sẽ gây rò rỉ vùng nhớ

Một cách làm khác bằng cách sử dụng std::vector

Nhờ việc sắp sếp các phần tử trên bộ nhớ của std::vector là liền kề nhau nên ta có thể dùng nó như một mảng các phần tử

std::vector<unsigned char> dataBuffer(1000);

// Do something

Lợi thế:

  • Đơn giản và gọn gàng
  • Tự động dọn dẹp vùng nhớ sau khi sử dụng xong
  • Exception xãy ra thì vector vẫn được hủy
  • Tận dụng được các phương thức mà std::vector cung cấp
  • Thư viện giải thuật của STL cung cấp nhiều giải thuật và tính năng cho std::vector như sorting, sum, for_each, …

Truy xuất vùng nhớ của std::vector như thế nào?

  • Truy xuất các phần tử trong dataBuffer: tương tự với mảng, std::vector cung cấp operator[] để truy xuất tới các phần tử VD: dataBuffer[0] = ‘a’;

  • Truyền dataBuffer tới các hàm trong C: giả sử mình có hàm như sau proccess(unsigned char *data); thì để truyền vector dataBuffer vào hàm này mình có thể làm như sau:

process(&dataBuffer[0]);
Hoặc
process(&dataBuffer.front());

Có nhiều phương thức tiện lợi trong std::vector bạn có thể tìm hiểu ở đây:

9 Likes

Anh ơi, vector hình như đâu cần cấp phát nó trước 1000 phần tử đâu nhỉ? Nó có chế độ tu động thêm phần tu vào vector mà…
Với lái delete a khác delete[a] chỗ nào v anh? chỉ biết dùng con trõ là sử dụng dấu [] nhưng ko biết vi sao :slight_smile:

  • Chính xác là vector cho phép không cần cấp phát phần tử trước, nhưng ở đây mình mong muốn là cấp phát trước 1000 phần tử để có thể truy xuất được
    VD: std::vector arrayInteger; thì việc phép gán arrayInteger[999] = 1 sẽ bị lỗi do vector arrayInteger chưa có phần tử nào

  • delete và delete [] khác nhau ở chổ delete chỉ hủy 1 phần tử, còn delete [] sẽ hủy nhiều phần tử
    VD: std::string *s = new std::string(); thì ta dùng delete s; do ở đây s là con trỏ chỉ đến 1 phần tử duy nhất
    còn std::string *s = new std::string[20]; ở đây s trỏ đến một mảng 20 phần tử thì việc dùng delete ở đây chỉ delete phần tử s[0] có thể dẫn đến leak 19 phần tử còn lại

Một số compiler có thể đủ thông minh để delete vẫn hoạt động tốt trên mảng nhưng tốt nhất là nên dùng chính xác delete [] cho mảng và delete cho một phần tử

2 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?