2012-01-08 64 views
2

我需要一個集合,其中我可以存儲具有虛擬功能的堆分配對象。我知道約boost::shared_ptr,std::unique_ptr(C++ 11)和boost::ptr_(vector|list|map),但它們不能解決重複的指針問題。C++指針容器與引用計數

只是爲了說明一個問題 - 我有接受堆上分配的指針,並將其存儲以備將來使用的功能:

void SomeClass::add(T* ptr) 
{ 
    _list.push_back(ptr); 
} 

但是,如果我用同樣的參數調用add兩次ptr - _list將包含兩個指針到相同的對象,並且當_list被銷燬時,會發生多個相同對象的刪除。

如果_list將計算他存儲的指針並在刪除時使用它們,則此問題將被解決,並且對象不會被多次刪除。

所以,問題是:

是否有人知道指針的一些庫集合(向量,列表,地圖在本質),支持自動刪除破壞和支持引用計數?

或者我可以使用其他技術解決這個問題?

更新:

我需要支持重複的指針。所以我不能使用std::set

由於Kerrek SBGrizzly提到的 - 這是一個壞主意,一般使用原始指針,並建議使用std::make_shared並通過new忘掉實例。但這是客戶端代碼的責任 - 不是我設計的類。即使我改變add簽名(當然_list容器),以

void SomeClass::add(std::shared_ptr<T> ptr) 
{ 
    _list.push_back(ptr); 
} 

然後有人(誰不知道std::make_shared)還可以這樣寫:

SomeClass instance; 
T* ptr = new T(); 
instance.add(ptr); 
instance.add(ptr); 

因此,這不是一個完整的我等待的解決方案,但如果您單獨編寫代碼,則會很有用。

更新2:

作爲一種替代的解決方案,我發現一個clonning(使用所生成的拷貝構造)。我的意思是,我可以改變我的add功能是這樣的:

template <typename R> 
void SomeClass::add(const R& ref) 
{ 
    _list.push_back(new R(ref)); 
} 

這將使虛擬方法(R - 類擴展一些基本的類(接口))呼叫和不允許重複的指針。但是這個解決方案有克隆的開銷。

+0

如何存儲共享指針?如果一個人被摧毀,另一個人仍然有效。只有當兩者都被銷燬(並且其他共享指針不存在於同一個對象中)時,它們指向的對象纔會被銷燬。 – cHao 2012-01-08 01:55:17

+1

將兩個'shared_ptr'刪除到同一個對象只會摧毀一次對象,我沒有看到問題所在。 – 2012-01-08 02:04:11

+0

@BenVoigt:只會工作,如果兩個'shared_ptrs'之一是通過複製其他創造,沒有創造的時候無論從原料三分球,這是增加似乎想做 – Grizzly 2012-01-08 02:11:11

回答

1

是:std::list<std::shared_ptr<T>>

共享指針可從<memory><tr1/memory>或Boost的<boost/shared_ptr.hpp>的較舊平臺獲得。您不需要手動刪除任何東西,因爲共享指針自己處理此事。但是,您將需要保持所有你堆指針共享裏面的指針從一開始:

std::shared_ptr<T> p(new T); // legacy 
auto p = std::make_shared<T>(); // better 

如果另一共享指向同一個對象,使共享指針的拷貝(而不是構建從底層的原始指針新的共享指針):auto q = p;


這裏的寓意是:如果你使用裸指針,什麼是錯的。

+0

他在說他正在用相同的指針構造兩個'shared_ptr',這會導致雙重刪除 – 2012-01-08 01:57:27

+0

@SethCarnegie:添加註釋! – 2012-01-08 01:58:23

1

實現智能指針通過比較底層容器進行比較。所以你可以使用你喜歡的智能指針的std::set。我個人使用std::unique_ptr而不是shared_ptr,只要我可以避開它,因爲它使得所有權更清晰(持有unique_ptr的是擁有者)並且開銷也低得多。我發現這幾乎可以滿足我所有的代碼。該代碼看起來像以下:

std::set<std::unique_ptr<T> > _list; 

void SomeClass::add(T* ptr) 
{ 
    std::unique_ptr<T> p(ptr); 
    auto iter = _list.find(p); 
    if(iter == _list.end()) 
    _list.insert(std::move(p)); 
    else 
    p.release(); 
} 

我現在如果說是矯枉過正我不知道(有檢查insert保證不會做任何事情,如果插入失敗),但它應該工作。這樣做與shared_ptr<T>看起來相似,但由於缺少relase成員,會稍微複雜一些。在這種情況下,我可能會首先構造一個shared_ptr<T>,而不做任何事情deleter也會傳遞給調用以查找,然後再插入另一個shared_ptr<T>

當然,我個人會避免這樣做,當指針的所有權易手總是繞過智能指針。因此,我會將SomeClass::add改寫爲void SomeClass::add(std::unique_ptr<T> ptr)void SomeClass::add(std::shared_ptr<T> ptr),它幾乎可以解決具有多個實例的問題(只要指針總是被包裝)。

+0

這有點危險,因爲用戶不清楚這個函數有時會佔用指針。 – 2012-01-08 02:03:02

+0

@Kerrek SB:我確實意識到這一點,但正如我所看到的那樣,這就是問題所要求的。但要知道,你提醒我,我確實想添加一張便條,爲什麼這是一個糟糕的主意。 – Grizzly 2012-01-08 02:04:12