2008-11-20 71 views
47

什麼是C++的最小塊,您可以想出安全清理向量或指針列表的方法? (假設你必須調用指針刪除?)清理指針的STL列表/向量

list<Foo*> foo_list; 

我寧可不使用Boost或用智能指針包裝我的指針。

+2

智能指針(包括Boost :: shared_ptr)會在您很難看到手動完成的情況下刪除您的對象。 – 2010-04-19 20:45:16

+2

依賴容器外的代碼刪除指針是非常危險的。例如,當容器由於拋出的異常而被銷燬時會發生什麼?我知道你說過你不喜歡boost,但請考慮[boost指針容器](http://www.boost.org/doc/libs/1_37_0/libs/ptr_container/doc/ptr_container.html)。 – 2008-11-20 23:14:26

+0

我在第二你的意見 – 2010-03-22 03:52:40

回答

53

因爲我們在這裏拋下戰書......「最短的C++大塊」

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

foo_list . remove_if (deleteAll); 

,我認爲我們可以信任誰想出了STL有高效算法的鄉親。爲什麼重新發明輪子?

8
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>()); 
28
for(list<Foo*>::const_iterator it = foo_list.begin(); it != foo_list.end(); ++it) 
{ 
    delete *it; 
} 
foo_list.clear(); 
+0

Upvoted,因爲它表現良好,而且很短。我添加了一個修改後的版本,這個版本比較短(但依賴於C++ 11)。 – Adisak 2014-02-12 22:06:30

+0

這個問題沒有指定const列表。爲什麼使用const iterator? – SRINI794 2014-09-16 11:35:30

+1

@ SRINI794我用一個常量迭代器來最大限度地提高代碼的可用性,並且因爲在迭代過程中我不打算改變列表,所以我不需要一個可變的迭代器。 – 2015-04-16 07:29:27

6

我不知道該函子的做法贏得了簡短這裏。

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

雖然我通常會建議這個。包裝智能指針中的指針或使用專業指針容器通常會更加健壯。有很多方法可以從列表中刪除項目(各種風格的erase,clear,銷燬列表,通過迭代器分配到列表中等等)。你能保證把他們都抓住嗎?

+0

爲了簡潔起見,仿函數法可能不會取勝,但這不是什麼大獎。通過使用仿函數,您不必編寫自己的循環,這是軟件中許多缺陷的來源。 – 2008-11-21 15:26:26

+0

具體詢問「最短」的問題。我不知道手動循環是缺陷的主要來源,你能提供一個參考嗎?如果我的團隊成員在爲循環寫一個bug時遇到了問題,我寧願不讓他放鬆一個函子解決方案。 – 2008-11-23 11:55:02

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

對於std::list<T*>使用:

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

對於std::vector<T*>使用:

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

不知道爲什麼我把front而不是back以上std::list。我想這是更快的感覺。但實際上兩者都是恆定的時間:)。無論如何將它包裝成一個功能,並獲得樂趣:

template<typename Container> 
void delete_them(Container& c) { while(!c.empty()) delete c.back(), c.pop_back(); } 
13

依靠容器外的代碼來刪除指針是非常危險的。例如,當容器由於拋出的異常而被銷燬時會發生什麼?

我知道你說你不喜歡助推,但請考慮boost pointer containers

4

至少對於一個列表來說,迭代和刪除,然後在最後調用clear會有點不方便,因爲它涉及遍歷列表兩次,當你真的只需要執行一次。這裏是一個小更好的辦法:

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); 
} 

這就是說,你的編譯器可能是足夠聰明循環二者結合起來反正,這取決於如何目錄::明確的實現。

3

其實,我相信STD庫提供的allocator class

形式管理內存可以擴展基本分配的解除分配()方法來自動刪除任何容器的成員的直接方法。

我/認爲/這是它打算用於的東西的類型。

5

當您的列表使用RAII超出範圍或您調用list :: clear()時,以下hack刪除指針。

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(); 
1
void remove(Foo* foo) { delete foo; } 
.... 
for_each(foo_list.begin(), foo_list.end(), remove); 
15

如果讓C++ 11,你可以做道格拉斯Leeder先生的回答很短的版本:

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

有這是你不想這麼做的一個小原因 - 你正在有效地迭代該列表兩次。

std :: list <> :: clear在複雜度上是線性的;它會在循環中一次刪除並銷燬一個元素。

考慮到上述情況的最簡單的在我看來,閱讀的解決方案是:

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

由於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>>());