Những người khác đã giải quyết các vấn đề khác, vì vậy tôi sẽ chỉ xem xét một điểm: bạn có bao giờ muốn xóa một đối tượng theo cách thủ công.
Câu trả lời là có. @DavidSchwartz đã đưa ra một ví dụ, nhưng đó là một ví dụ khá bất thường. Tôi sẽ đưa ra một ví dụ về điều mà rất nhiều lập trình viên C ++ sử dụng mọi lúc: std::vector
(và std::deque
mặc dù nó không được sử dụng nhiều).
Như hầu hết mọi người đều biết, std::vector
sẽ cấp phát một khối bộ nhớ lớn hơn khi / nếu bạn thêm nhiều mục hơn mức phân bổ hiện tại của nó có thể giữ. Tuy nhiên, khi nó thực hiện điều này, nó có một khối bộ nhớ có khả năng chứa nhiều đối tượng hơn hiện tại trong vector.
Để quản lý điều đó, những gì vector
bên dưới là cấp phát bộ nhớ thô thông qua Allocator
đối tượng (đối tượng này, trừ khi bạn chỉ định khác, có nghĩa là nó sử dụng ::operator new
). Sau đó, khi bạn sử dụng (ví dụ) push_back
để thêm một mục vào vector
, bên trong vectơ sử dụng a placement new
để tạo một mục trong phần (trước đây) không sử dụng của không gian bộ nhớ của nó.
Bây giờ, điều gì sẽ xảy ra khi / nếu bạn erase
là một mục từ vector? Nó không thể chỉ sử dụng delete
- điều đó sẽ giải phóng toàn bộ khối bộ nhớ của nó; nó cần phải phá hủy một đối tượng trong bộ nhớ đó mà không phá hủy bất kỳ đối tượng nào khác hoặc giải phóng bất kỳ khối bộ nhớ nào mà nó kiểm soát (ví dụ: nếu bạn erase
5 mục từ một vectơ, thì ngay lập tức push_back
thêm 5 mục nữa, nó được đảm bảo rằng vectơ sẽ không phân bổ lại bộ nhớ khi bạn làm như vậy.
Để làm điều đó, vector trực tiếp phá hủy các đối tượng trong bộ nhớ bằng cách gọi rõ ràng hàm hủy, không phải bằng cách sử dụng delete
.
Nếu tình cờ, ai đó đã viết một vùng chứa bằng cách sử dụng lưu trữ liền kề gần giống như một vector
hiện (hoặc một số biến thể của điều đó, giống như std::deque
thực sự), bạn gần như chắc chắn muốn sử dụng kỹ thuật tương tự.
Ví dụ, hãy xem xét cách bạn có thể viết mã cho một bộ đệm vòng tròn.
#ifndef CBUFFER_H_INC
#define CBUFFER_H_INC
template <class T>
class circular_buffer {
T *data;
unsigned read_pos;
unsigned write_pos;
unsigned in_use;
const unsigned capacity;
public:
circular_buffer(unsigned size) :
data((T *)operator new(size * sizeof(T))),
read_pos(0),
write_pos(0),
in_use(0),
capacity(size)
{}
void push(T const &t) {
// ensure there's room in buffer:
if (in_use == capacity)
pop();
// construct copy of object in-place into buffer
new(&data[write_pos++]) T(t);
// keep pointer in bounds.
write_pos %= capacity;
++in_use;
}
// return oldest object in queue:
T front() {
return data[read_pos];
}
// remove oldest object from queue:
void pop() {
// destroy the object:
data[read_pos++].~T();
// keep pointer in bounds.
read_pos %= capacity;
--in_use;
}
~circular_buffer() {
// first destroy any content
while (in_use != 0)
pop();
// then release the buffer.
operator delete(data);
}
};
#endif
Không giống như các thùng chứa tiêu chuẩn, điều này sử dụng operator new
và operator delete
trực tiếp. Để sử dụng thực tế, bạn có thể muốn sử dụng một lớp cấp phát, nhưng hiện tại nó sẽ làm nhiều việc để phân tâm hơn là đóng góp (dù sao cũng là IMO).