2011-11-15 50 views
9

我正在創建一個將成爲DAG一部分的類。構造函數將指向其他實例並使用它們初始化依賴項列表。
在依賴列表被初始化之後,它只能被縮短 - 實例永遠不能被添加爲它自己或它的任何子項的依賴。如何在構造函數中將weak_ptrs分發給此對象?

::std::shared_ptr是一個很自然的處理這個。爲處理DAG做了參考計數。

不幸的是,依賴關係需要知道它們的依賴關係 - 當一個依賴關係被更新時,它需要告訴它的所有依賴關係。
這會產生一個微不足道的循環,可以用::std::weak_ptr來打破。依賴關係可能會忘記消失的依賴關係。

但我找不到一個方法讓依賴者在構建時自己創建一個::std::weak_ptr

這不起作用:

object::object(shared_ptr<object> dependency) 
{ 
    weak_ptr<object> me = shared_from_this(); 
    dependency->add_dependent(me); 
    dependencies_.push_back(dependency); 
} 

在析構函數的代碼會導致構造函數退出之前被調用。

有沒有很好的方法來處理這個問題?我非常滿意C++ 11-only解決方案。

回答

9

不用構造函數,而是使用函數來構建圖的節點。

std::shared_ptr<Node> mk_node(std::vector<std::shared_ptr<Node>> const &dependencies) 
{ 
    std::shared_ptr<Node> np(new Node(dependencies)); 
    for (size_t i=0; i<dependencies.size(); i++) 
     dependencies[i].add_dependent(np);  // makes a weak_ptr copy of np 
    return np; 
} 

如果你把這個static成員函數或您Node類的friend,可以使實際構造private

+2

這就是'enable_shared_from_this'已經做了什麼。你的聲明中還有一個雜散的* - 你想要一個shared_ptr,而不是指向shared_ptr的指針。 :) –

+0

這是一個很好的答案,但在我的情況下,我不會有'家屬'的論點。該函數將循環遍歷所有依賴項,將新創建的節點(即'np')添加到每個依賴項的依賴項列表中。 – Omnifarious

+0

@Omnifarious:更新了示例。 –

5

基本上,你不能。你需要一個shared_ptrweak_ptr來製作一個weak_ptr,顯然,自己只能以weak_ptr的形式注意到它自己的shared_ptr(否則在計算引用時沒有意義)。當然,當對象尚未構建時,可能不存在自我shared_ptr

1

我理解你的問題,因爲在概念上與garbage collection問題有關。

GC是一個非模塊化的特性:它處理程序的某些全局屬性(更確切地說,實時數據是一個全局的,非模塊化的程序內的屬性 - 有些情況下你無法處理在模塊化的&構圖方式。)。 AFAIK,STL或C++標準庫對全球程序功能幫助不大。

一個可能的答案可能是使用(或實現你自己)的垃圾收集器; Boehm's GC可能對您有用,如果您可以在整個程序中使用它。

你也可以使用GC算法(甚至是複製代碼)來處理你的問題。

+0

我真的不明白這是如何回答這個問題。 –

+0

你是最現成的答案,有趣的是,我已經想到了,並放棄了這個想法。但是,你是對的。不幸的是,這是爲其他人重用的庫的一部分,並且要求GC對於許多C++人員(包括我經常包括的人員)來說是不起步的。 – Omnifarious

+0

@BillyONeal:Basile建議我放棄'shared_ptr'引用計數並改用一個完整的垃圾回收器。 – Omnifarious

0

也許這將幫助:

繼承enable_shared_from_this基本上持有weak_ptr。 這將允許您使用this->shared_from_this();

的shared_ptr的瞭解看,如果將該類從類繼承,並使用指向對象時的類的weak_ptr(防止2共享指針計數,引用不同)

更多一點:cppreference

+0

我不認爲你可以調用shared_from_this,除非已經擁有'shared_ptr';在構造函數中沒有。(20.7.2.4/7) –

+1

enable_shared_from_this +1 - 它基本上實現爲一個'weak_ptr'成員,就像OP想要的一樣。 –

+0

@AlanStokes:你不能。但是看看OP的情況,他似乎只想要一個指向存儲在對象本身中的對象的弱指針,這正是enable_shared_from_this所做的。 –

1

你不能。

我所想到的最好的做法是使構造函數是私有的,並有一個公共工廠函數,它返回一個新對象shared_ptr;然後工廠函數可以在初始化weak_ptr的構造對象上調用私有方法。

+0

如果類是從派生的,構造函數將不得不被保護,而不是私有的。 – Omnifarious

+0

@Omnifarious是的,當然。 –

1

不幸的是,依賴需要知道他們的依賴。這是因爲當一個依賴被更新時,它需要告訴它的所有依賴。這是一個微不足道的循環。幸運的是,這個循環可以用:: std :: weak_ptr來打破。依賴關係可能會忘記消失的依賴關係。

聽起來像一個依賴不能引用一個依賴除非依賴存在。 (例如,如果依賴關係被破壞,依賴關係也被破壞 - 這就是DAG最終的結果)

如果是這樣的話,你可以發出純指針(在這種情況下,this)。如果依賴關係處於活動狀態,則永遠不需要檢查依賴關係,因爲如果依賴關係已經死亡,那麼依賴關係也應該死亡。

僅僅因爲該對象由shared_ptr擁有並不意味着所有指向它自己的指針必須是shared_ptrs或weak_ptrs - 只是必須定義清晰的語義來指示指針何時失效。

+0

如果不是因爲一個對象可以有多個依賴項,這將是可以的。這是一個DAG,而不是一棵樹。分支可以連接在一起,那裏不能有任何循環。 – Omnifarious

+0

@Omnifarious:好的。然後工廠方法是您唯一的選擇。例如,只有這樣才能強制實際上始終使用'shared_ptr'構建對象,而不是在靜態或自動存儲中。 (從好的方面來說,您可以利用工廠中的make_shared進行的各種優化)。 –

1

這聽起來像你試圖混淆有些不同的項目:單個對象(DAG中的一個節點),以及管理這些對象的集合。現在

class DAG { 

    class node { 
     std::vector<std::weak_ptr<node> > dependents; 
    public: 
     node(std::vector<weak_ptr<node> > d) : dependents(d) {} 
    }; 

    weak_ptr<node> root; 
}; 

,它可能是真實的,DAG僅會舉行weak_ptr<node>而不是處理一個node直接的一個實例。然而,對於節點本身來說,這或多或少是無關緊要的。它需要維護任何關鍵/數據/等,它包含,以及它自己的依賴列表。同時,通過在DAG中嵌套它(特別是如果我們使DAG的私有類定義爲node),我們可以最大限度地減少對node的訪問,所以其他代碼必須關注與node。根據情況,您可能還想要執行一些操作,例如刪除編譯器默認生成的一些(大部分?)節點函數(例如,默認的ctor,copy ctor,賦值運算符)。

+0

這是一個有趣的答案。我不確定是否可以通過這種方式改變我的設計。我試圖建立一個基於依賴關係的系統來處理多線程的未來/承諾類安排。節點是相當重要的實體。 OTOH,所有屬於給定線程的節點都會有一個管理器。嗯... – Omnifarious

0

這也一直在推動着我的堅果。

我認爲採用使用指針來打破循環的策略......但是我真的沒有找到這個,因爲我真的很喜歡在你的代碼中看到weak_ptr的目的是多麼的清楚(你知道它是那裏打破週期)。

現在我傾向於寫我自己的weak_ptr類。

+0

_chuckle_我最終創建了構造函數。雖然,這有一個不同的有趣問題:http://stackoverflow.com/questions/8147027/how-do-i-call-stdmake-shared-on-a-class-with-only-protected-or-private-const我會說這具有阻止人們將這些東西創建爲堆棧或嵌入對象,然後爲其創建'shared_ptr'的優點。如果人們這樣做會破壞一大堆東西。我懷疑你也可能是這種情況。 – Omnifarious

相關問題