2013-01-16 46 views
0

我正在使用shared_ptr<Base>爲某種具有派生類的樹列表。但是當我的樹遭到破壞時,我得到一個指針違規。C++ shared_ptr <Base>指針訪問衝突

我的代碼看起來是這樣的,再說,這其實similates我運行時錯誤:

#include <iostream> 
#include <memory> 
#include <vector> 


class Base; 
typedef std::shared_ptr<Base> pBase; 
class Derived; 

class Base { 
public:   
    std::vector<pBase> children; 

    pBase parent; 

    Base() {} 
    virtual ~Base() {} 

    virtual void doSomething() {} 
    void add(pBase i); 
}; 

class Derived : public Base { 
    void doSomething() { 
     // Do something... 
    } 
}; 

void Base::add(pBase i) { 
    i->parent = pBase(this); 
    children.push_back(i); 
} 


int main() { 
    pBase tree = pBase(new Derived()); 
    pBase child(new Derived()); 
    child->add(pBase(new Derived())); 
    tree->add(child); 
} 

此外,當我下面的行添加到Base::~Base: 的std ::法院< < 「破壞」 < <名< < std :: endl;

然後在Base中實現一個名爲name的std :: string,這對每個實例都是不同的,我可以看到析構函數被多次調用(因爲我認爲是Base::parent引用)。這當然引發了我的錯誤,但我仍然不明白爲什麼會發生這種情況,因爲shared_ptr<Base>預計會在實際銷燬它之前計算其參考值!!

我希望有人能告訴我我做錯了什麼! 但更重要的是,我該如何解決這個問題!

+0

你沒有初始化'tree',您有其他問題... – ybungalobill

+0

我@ybungalobill很抱歉,我不能看到你闖民宅什麼太? – Tim

+0

因爲你在我指出它之後修復了它...... – ybungalobill

回答

2

這裏

i->parent = pBase(this); 

您從一個普通的老指向一個對象,你沒有從直接的新獲得一個智能指針。永遠不要這樣做。

正如@Roddy所解釋的那樣,您將獲得單獨的智能指針對象,並帶有單獨的引用計數器。一個指針的兩個參考計數器不起作用。

就你的情況而言,可能正確的做法是使父母成爲正常的指針,就像@Roddy提出的那樣。這樣,你不會遇到循環引用的麻煩。只要確保在刪除父項之後永遠不會訪問父指針。沒問題,如果你刪除所有的孩子一起與父母(這會自動發生,除非你存儲更多的智能指針,他們,其他地方)

如果你想初始化一個智能指針,你有兩個選擇,基本上:在每個界面中使用智能指針。不幸的是,這不適用於「這」,因爲這是一個隱含的參數。您需要將已創建的智能指針手動傳遞給該方法,並使用額外的參數。就像這樣:

tree->add(tree, child); 

這是一種醜惡的,所以你可能要考慮把「增加」一個靜態方法,所以你不會需要通過父的兩倍。

另一種選擇:使用另一種智能指針,如boost :: intrusive_ptr,您可以在其中存儲引用計數的引用計數。這樣,即使只有像「this」這樣的啞指針,也能找到引用計數。

編輯:下面@jpalecek的答案比較好。使用那個。塞巴斯蒂安。

+0

非常感謝你! – Tim

3

看在加入這一行()

i->parent = pBase(this); 

每次調用添加的時候,你要創建一個新共享指針this。這些共享指針是分開的 - 也就是說,它們是不是'共享',如你所想。所以,第一次刪除一個孩子時,它的父母被刪除(因爲它是一個共享指針)。因此你的代碼爆炸了。

嘗試(作爲開始)使父母成爲一個簡單的啞指針。

Base *parent; 
+0

謝謝!應該是這樣! – Tim

+0

如果這是你的答案,請將它標記爲 – Mordachai

+0

我不建議在一個對象上混合普通指針和智能指針。最後,可能會發生這樣的情況:父項被刪除,而留下懸掛指針的子項。 – jpalecek

3

剛剛添加到別人的答案:的規範的方式做你想做的事,行

i->parent = pBase(this); 

是使用std::enable_shared_from_this。您

  1. 派生Base從中

    class Base : std::enable_shared_from_this<Base> { 
    
  2. 確保每一個Base實例是由std::shared_ptr擁有。這是你的情況OK,既然你創建了

    pBase child(new Derived()); 
    
  3. 使用shared_from_this()而不是this在表達式中的對象,當你想要一個std::shared_ptr。然後有問題的生產線將成爲

    i->parent = shared_from_this(); 
    
+0

謝謝,這實際上是解決它的最簡單的方法!:) 編輯:這是一個比較快的方式,如果它被大規模使用? – Tim

+0

@Tim:它的速度應該和塞巴斯蒂安一樣快,而且空間很小。 – jpalecek

+0

整潔,它看起來像是一個提升等價物,因爲沒有C++ 11編譯器就卡住了,所以更加便於使用。 – Roddy