他の人はすでに他の問題に対処しているので、1 つの点だけを見ていきます。オブジェクトを手動で削除したいと思ったことはありませんか。
答えはイエスです。@DavidSchwartz は 1 つの例を挙げましたが、かなり珍しいものです。多くの C++ プログラマーが常に使用しているものの内部にある例を示しますstd::vector
(そしてstd::deque
、あまり使用されていませんが)。
ほとんどの人が知っているようにstd::vector
、現在の割り当てよりも多くのアイテムを追加すると、より大きなメモリ ブロックが割り当てられます。ただし、これを行うと、現在ベクトルにあるよりも多くのオブジェクトを保持できるメモリ ブロックが作成されます。
それを管理するために、内部で行うことは、オブジェクトを介して生メモリをvector
割り当てることです (特に指定しない限り、それは を使用することを意味します)。次に、(たとえば)を使用して に項目を追加すると、内部的にベクトルは を使用して、そのメモリ空間の (以前は) 未使用の部分に項目を作成します。Allocator
::operator new
push_back
vector
placement new
erase
では、ベクトルからアイテムを取得した場合はどうなりますか? ただ使用することはできませんdelete
-- メモリのブロック全体を解放します。そのメモリ内の 1 つのオブジェクトを他のオブジェクトを破壊せずに破壊するか、それが制御するメモリ ブロックを解放する必要があります (たとえば、erase
ベクトルから 5 つのアイテムを取得した場合、すぐにpush_back
さらに 5 つのアイテムを追加すると、ベクトルが再割り当てされないことが保証されます)。あなたがそうするときの記憶。
そのために、ベクターは、 を使用するのではなく、デストラクタを明示的に呼び出して、メモリ内のオブジェクトを直接破棄しますdelete
。
ひょっとすると、他の誰かが連続したストレージを使って大まかにvector
(またはstd::deque
実際にそうしているように) コンテナーを作成した場合、ほぼ確実に同じ手法を使用したいと思うでしょう。
例として、循環リング バッファーのコードを記述する方法を考えてみましょう。
#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
標準のコンテナーとは異なり、これはoperator new
and をoperator delete
直接使用します。実際の使用では、おそらくアロケーター クラスを使用したいと思うでしょうが、現時点では、貢献するよりも気を散らすためのものです (とにかく)。