次のコードがある場合、
Foo *f = new Foo();
vector<Foo*> vect;
vect.push_back(f);
// do stuff
vect.erase(f);
メモリリークを作成しましたか? そうだろうけど、消去という言葉は、それを削除しているような気がします。
これを書いていると、STLのベクタにポインタを入れるのは間違いではないかと思います。どう思いますか?
はい、それによってメモリリークが発生しました。std::vector およびその他のコンテナーはポインターを削除するだけで、ポインターが指すメモリを解放しません。
標準ライブラリ コンテナーにポインターを配置することは珍しくありません。ただし、問題は、コンテナーから削除するときに削除を追跡する必要があることです。上記を行うためのより良い、しかし簡単な方法は、boost::shared_ptr を使用することです。
{
boost::shared_ptr<foo> f(new foo);
std::vector< boost::shared_ptr<foo> > v;
v.push_back(f);
v.erase(v.begin());
} /* if the last copy of foo goes out of scope, the memory is automatically freed */
次の C++ 標準 (一般に C++1x および C++0x と呼ばれる) には、std::shared_ptr
. std::unique_ptr<T>
そこでは、コピーが許可されていないため、どちらが高速かを使用することもできます。c++0x のコンテナーでの使用は、booststd::unique_ptr
のライブラリーに似ています。ptr_container
もう 1 つのオプションは、Boost Pointer Containersを使用することです。それらは、あなたが望むことを正確に行うように設計されています。
あるいは、boost::ptr_vectorコンテナーがあります。
所有するポインターを保持していることを認識しているため、それらを自動削除します。
良い副作用として、要素にアクセスすると、ポインタではなくオブジェクトへの参照が返され、コードの見栄えが良くなります。
Foo *f = new Foo();
boost::ptr_vector<Foo> vect;
vect.push_back(f);
// do stuff
vect.erase(f);
ポインターが削除されない理由を明確にするために、次のことを考慮してください。
std::vector<char const*> strings;
strings.push_back("hello");
strings.push_back("world");
// .erase should not call delete, pointers are to literals
std::vector<int*> arrays;
strings.push_back(new int[10]);
strings.push_back(new int[20]);
// .erase should call delete[] instead of delete
std::vector<unsigned char*> raw;
strings.push_back(malloc(1000));
strings.push_back(malloc(2000));
// .erase should call free() instead of delete
一般に、vector<T*>::erase
を処分する方法を推測することはできませんT*
。
STL コンテナはメモリを解放しません。std::auto_ptr がコンテナー内に収まらないことを知って、スマート ポインターを使用することをお勧めします。boost::shared_ptr をお勧めします。または、コンパイラ ベンダーが TR1 拡張機能をサポートしている場合 (多くの場合)、std::tr1::shared_ptr を使用できます。
また、ベクトルはポインタ用に予約された内部メモリを解放しないことにも注意してください。std::vectors は、clear() を呼び出してもサイズを小さくすることはありません。ベクトルを縮小する必要がある場合は、別のベクトルを作成して内容を交換する必要があります。
ポインタを標準コンテナに向けることは間違いではありません (ただし、auto_ptr のコンテナを作成することは間違いです)。はい、個々の要素が指すメモリを解放するために明示的に削除する必要があります。または、ブーストスマート ポインタのいずれかを使用できます。
vector は含まれているデータを削除します。ベクターにはポインターが含まれているため、ポインターのみが削除され、ポインターが指している場合とそうでない場合があるデータは削除されません。
メモリが割り当てられた場所で解放されるのは、C++ ではかなり一般的なルールです。ベクターは、ポインターが指すものを割り当てていないため、解放してはなりません。
そもそもベクトルにポインターを格納するべきではありません。多くの場合、次のような方がよいでしょう。
vector<Foo> vect;
vect.push_back(Foo());
// do stuff
vect.erase(f);
もちろん、これは Foo がコピー可能であり、そのコピー コンストラクターが高すぎないことを前提としていますが、メモリ リークを回避し、Foo オブジェクトを削除することを覚えておく必要はありません。もう 1 つのアプローチは、スマート ポインター (Boost の shared_ptr など) を使用することですが、ポインター セマンティクスがまったく必要ない場合もあります。その場合は、単純な解決策が最適です。