web-dev-qa-db-ja.com

ポインターのSTLリスト/ベクターのクリーンアップ

ベクトルまたはポインターのリストを安全にクリーンアップするために思い付くことができるC++の最短チャンクは何ですか? (ポインターでdeleteを呼び出す必要があると仮定しますか?)

list<Foo*> foo_list;

Boostを使用したり、ポインターをスマートポインターでラップしたりすることは避けたいです。

49
twk

ここでガントレットを投げているので...「C++の最も短いチャンク」

static bool deleteAll( Foo * theElement ) { delete theElement; return true; }

foo_list . remove_if ( deleteAll );

STLを思いついた人々が効率的なアルゴリズムを持っていると信頼できると思います。なぜ車輪を再発明するのですか?

51
Mr.Ree

std::list<T*>の場合:

while(!foo.empty()) delete foo.front(), foo.pop_front();

std::vector<T*>の場合:

while(!bar.empty()) delete bar.back(), bar.pop_back();

上記のstd::listfrontの代わりにbackを使用した理由がわかりません。私はそれが速いという感じだと思います。しかし、実際には両方とも一定の時間です:)。とにかくそれを関数にラップして楽しんでください:

template<typename Container>
void delete_them(Container& c) { while(!c.empty()) delete c.back(), c.pop_back(); }
for(list<Foo*>::const_iterator it = foo_list.begin(); it != foo_list.end(); ++it)
{
    delete *it;
} 
foo_list.clear();
30
Douglas Leeder

C++ 11を許可する場合、Douglas Leederの答えの非常に短いバージョンを実行できます。

for(auto &it:foo_list) delete it; foo_list.clear();
16
Adisak

コンテナの外部のコードに依存してポインタを削除するのは本当に危険です。たとえば、スローされた例外のためにコンテナが破棄された場合はどうなりますか?

ブーストが嫌いだと言っていましたが、 ブーストポインターコンテナ を考慮してください。

13
Mark Ransom
template< typename T >
struct delete_ptr : public std::unary_function<T,bool>
{
   bool operator()(T*pT) const { delete pT; return true; }
};

std::for_each(foo_list.begin(), foo_list.end(), delete_ptr<Foo>());
9
John Dibling

ここでは、ファンクターアプローチが簡潔さで勝つかどうかはわかりません。

for( list<Foo*>::iterator i = foo_list.begin(); i != foo_list.end(); ++i )
    delete *i;

ただし、通常はこれに反対します。ポインターをスマートポインターにラップするか、専門のポインターコンテナーを使用すると、一般に、より堅牢になります。リストからアイテムを削除する方法はたくさんあります(さまざまなフレーバーのeraseclear、リストの破壊、イテレーターを介したリストへの割り当てなど)。それらをすべてキャッチすることを保証できますか?

6
CB Bailey

次のハックは、RAIIを使用してリストが範囲外になった場合、またはlist :: clear()を呼び出した場合にポインターを削除します。

template <typename T>
class Deleter {
public:
  Deleter(T* pointer) : pointer_(pointer) { }
  Deleter(const Deleter& deleter) {
    Deleter* d = const_cast<Deleter*>(&deleter);
    pointer_ = d->pointer_;
    d->pointer_ = 0;
  }
  ~Deleter() { delete pointer_; }
  T* pointer_;
};

例:

std::list<Deleter<Foo> > foo_list;
foo_list.Push_back(new Foo());
foo_list.clear();
5
Linoliumz

実際、STDライブラリは allocator class の形式でメモリを管理する直接的な方法を提供すると考えています

基本的なアロケータのdeallocate()メソッドを拡張して、コンテナのメンバーを自動的に削除できます。

私は/考え/これが対象とするもののタイプです。

4
Jeremy Cowles

少なくともリストの場合、繰り返し処理と削除を行い、最後にclearを呼び出すことは、リストを2回走査する必要があるため、実際には1回だけ実行する必要があるため、少し非効率的です。ここにもう少し良い方法があります:

for (list<Foo*>::iterator i = foo_list.begin(), e = foo_list.end(); i != e; )
{
    list<Foo*>::iterator tmp(i++);
    delete *tmp;
    foo_list.erase(tmp);
}

とは言っても、コンパイラはlist :: clearの実装方法によっては、とにかく2つをループ結合するのに十分賢いかもしれません。

4
Greg Rogers
for(list<Foo*>::const_iterator it = foo_list.begin(); it != foo_list.end(); it++)
{
    delete *it;
} 
foo_list.clear();

これをしたくない理由は少しあります-リストを2回繰り返して効果的に繰り返しているからです。

std :: list <> :: clearは複雑さが線形です。ループ内で一度に1つの要素を削除および破壊します。

私の意見では、上記を考慮して最も簡単な解決策は次のとおりです。

while(!foo_list.empty())
{
    delete foo_list.front();
    foo_list.pop_front();
}
4
Heero

C++ 11以降:

std::vector<Type*> v;
...
std::for_each(v.begin(), v.end(), std::default_delete<Type>());

または、テンプレートコードを記述していて、具体的な型の指定を避けたい場合:

std::for_each(v.begin(), v.end(),
    std::default_delete<std::remove_pointer<decltype(v)::value_type>::type>());

これは(C++ 14以降)短くすることができます:

std::for_each(v.begin(), v.end(),
    std::default_delete<std::remove_pointer_t<decltype(v)::value_type>>());
3
ostappus
void remove(Foo* foo) { delete foo; }
....
for_each( foo_list.begin(), foo_list.end(), remove );
1
kendotwill

これは最もきれいなimoのように見えますが、ご使用のc ++バージョンはこのタイプの反復をサポートする必要があります(c ++ 0xを含む、またはそれ以前のすべてが機能すると考えられます)。

for (Object *i : container) delete i;    
container.clear();
0
for (list<Foo*>::const_iterator i = foo_list.begin(), e = foo_list.end(); i != e; ++i)
    delete *i;
foo_list.clear();
0
Assaf Lavie