Đó thực sự là loại bắt buộc để thực hiện bất kỳ loại cấu trúc dữ liệu nào phân bổ nhiều bộ nhớ hơn mức yêu cầu tối thiểu cho số lượng phần tử được chèn (nghĩa là, bất kỳ thứ gì khác ngoài cấu trúc được liên kết phân bổ một nút tại một thời điểm).
Container mất thích unordered_map
, vector
hoặc deque
. Tất cả đều phân bổ nhiều bộ nhớ hơn mức yêu cầu tối thiểu cho các yếu tố bạn đã chèn cho đến nay để tránh yêu cầu phân bổ heap cho mỗi lần chèn. Hãy sử dụng vector
như một ví dụ đơn giản nhất.
Khi bạn làm:
vector<Foo> vec;
// Allocate memory for a thousand Foos:
vec.reserve(1000);
... điều đó không thực sự tạo ra một ngàn Foos. Nó chỉ đơn giản là phân bổ / dự trữ bộ nhớ cho họ. Nếu vector
không sử dụng vị trí mới ở đây, nó sẽ được mặc định - xây dựng ở Foos
khắp mọi nơi cũng như phải gọi các hàm hủy của chúng ngay cả đối với các phần tử mà bạn chưa từng chèn vào vị trí đầu tiên.
Phân bổ! = Xây dựng, Giải phóng! = Phá hủy
Nói chung, để thực hiện nhiều cấu trúc dữ liệu như trên, bạn không thể coi việc phân bổ bộ nhớ và xây dựng các phần tử là một thứ không thể tách rời và bạn cũng không thể coi việc giải phóng bộ nhớ và phá hủy các phần tử là một thứ không thể chia tách.
Phải có một sự tách biệt giữa các ý tưởng này để tránh việc gọi các nhà xây dựng và phá hủy một cách không cần thiết trái và phải, và đó là lý do tại sao thư viện tiêu chuẩn tách biệt ý tưởng std::allocator
(không xây dựng hoặc phá hủy các phần tử khi phân bổ / giải phóng bộ nhớ *) các thùng chứa sử dụng nó để xây dựng các phần tử theo cách thủ công bằng cách sử dụng các phần tử mới và hủy thủ công bằng cách sử dụng các lệnh hủy rõ ràng.
- Tôi ghét thiết kế của
std::allocator
nhưng đó là một chủ đề khác tôi sẽ tránh nói. : -D
Vì vậy, dù sao đi nữa, tôi có xu hướng sử dụng nó rất nhiều vì tôi đã viết một số bộ chứa C ++ tuân thủ tiêu chuẩn cho mục đích chung không thể được xây dựng theo các cái hiện có. Bao gồm trong số đó là một triển khai vectơ nhỏ mà tôi đã xây dựng vài thập kỷ trước để tránh phân bổ heap trong các trường hợp phổ biến và bộ ba hiệu quả bộ nhớ (không phân bổ một nút tại một thời điểm). Trong cả hai trường hợp, tôi không thể thực sự sử dụng chúng bằng cách sử dụng các container hiện có, và vì vậy tôi phải sử dụng placement new
để tránh việc gọi các nhà xây dựng và phá hủy một cách không cần thiết trên những thứ không cần thiết trái và phải.
Đương nhiên, nếu bạn từng làm việc với các bộ cấp phát tùy chỉnh để phân bổ các đối tượng riêng lẻ, như một danh sách miễn phí, thì bạn cũng thường muốn sử dụng placement new
, như thế này (ví dụ cơ bản không liên quan đến an toàn ngoại lệ hoặc RAII):
Foo* foo = new(free_list.allocate()) Foo(...);
...
foo->~Foo();
free_list.free(foo);