2010-10-25 162 views
15

事實:循環依賴++

  • 我有兩個主要的類:經理和專家。
  • 有幾種不同類型的專家。
  • 專家通常需要其他專家的幫助才能完成工作。
  • 經理知道所有的專家,最初每個專家只知道他們的經理。 (這是問題。)
  • 在運行時,管理器創建並存儲專家列表。然後經理遍歷列表並要求每個專家初始化。在初始化期間,每位專家要求經理爲他們提供滿足一些描述的其他專家。一旦完成,管理員進入一個循環,在這個循環中,專家被依次要求執行他們的專門任務。

對我來說,似乎這是一個體面的方式,但由於經理有專家的名單和專家有一個經理,我得到循環依賴問題。

這是一個情況,我應該以某種方式宣佈一個類別的存在嗎? (如果是這樣,如何?)或者我應該使用一些設計模式來解決這個問題? (如果是這樣?)另外...我雖然模式本身很漂亮哦。所以我不介意有人幫助我理解爲什麼這是一件壞事。

+0

你能告訴我們你有什麼樣的樣品,以及你有什麼問題? – 2010-10-25 21:18:41

+0

我見過這個非常類似的問題最近問幾次 - 這是最近一次http://stackoverflow.com/questions/4016471/c-circular-reference-problem – 2010-10-25 21:40:01

+0

@Greg - 問題是類似的,但是我我不僅對解決循環依賴感興趣,而且對我使用的模式是否因某種原因有缺陷有所瞭解。 – JnBrymn 2010-10-26 19:16:10

回答

23

在這兩種情況下,向前聲明其他類:

Manager.h

class Specialist; 

class Manager 
{ 
    std::list<Specialist*> m_specialists; 
}; 

專家。ħ

class Manager; 

class Specialist 
{ 
    Manager* m_myManager; 
}; 

需要在一類的頭文件帶來的唯一的時間是當你需要使用一個成員函數或變量,類內,或需要使用類作爲值類型等當你只需要一個指針或者一個類的引用時,一個前向聲明就足夠了。

請注意,前向聲明不僅僅用於求解循環依賴。儘可能使用前向聲明。他們總是最好包括一個額外的頭文件,如果它是完全可行的。

+4

+1 - 「前向宣言」是關鍵詞 – leonbloy 2010-10-25 21:13:29

+4

「他們總是比較可取的」我強烈反對,他們認爲在任何可以避免的地方都更喜歡他們。在大量使用前向聲明會導致難以理解代碼,因爲它更難追查依賴關係。此外,當使用現代編譯器積極緩存文件並支持預編譯頭文件時,通常幾乎沒有性能提升。 [在一個不相關的筆記,爲什麼你建議'std :: list'?] – 2010-10-25 21:26:03

+3

追查什麼依賴關係?如果前向聲明有效,那麼不再有依賴關係。而且,在任何半途體面的IDE中,按照課程跟蹤作爲標題跟蹤一樣簡單。至於使用'std :: list',他在OP中說,Manager存儲了一個專家列表,所以我決定毫不誇張地解釋它。我可能會自己使用'vector',但它當然取決於特定的用例。 – 2010-10-25 21:34:38

1

一種選擇是轉發申報的人之一,你的建議:

struct specialist; 

struct manager 
{ 
    std::vector<std::shared_ptr<specialist> > subordinates_; 
}; 

struct specialist 
{ 
    std::weak_ptr<manager> boss_; 
}; 

但是,如果你最終有更多的樹結構的(你有管理的多層次,一個person基類也將工作:

struct person 
{ 
    virtual ~person() { } 
    std::weak_ptr<person> boss_; 
    std::vector<std::shared_ptr<person> > subordinates_; 
}; 

然後可以推導出層次不同類型的人的特定的類無論你需要這個取決於你打算如何準確地使用類

如果您的實施不支持std::shared_ptr,它可能支持std::tr1::shared_ptr或者您可以使用boost::shared_ptr

+0

在這個模型中,你是否必須確保所有'manager'指針都被包裝在'shared_ptr'中?否則無法驗證'weak_ptr'是否存在?對此感到好奇,因爲它對我來說很重要。 – 2010-10-25 21:09:17

+0

@Steve:是的。如果在層次結構中只有一個類類型(如我的第二個示例中的「person」),這會更容易。 – 2010-10-25 21:14:50

1

這是正常的東西。你只需要

class Manager; 
專科醫生頭

class Specialist; 

的經理頭

如果你正在使用shared_ptrs你可能會發現有用shared_from_this。 (不適用於循環,而是因爲它聽起來像是你將需要它無論如何)

8

這是一個味道的問題,但前向聲明通常是一個很好的選擇,即使沒有循環依賴包含在頭文件中。 (我不希望提高對在這個地方討論。)所以,這裏是如何爲您的問題申請前向聲明的例子:

在Manager.h:

// Forward declaration: 
class Specialist; 

// Class declaration: 
class Manager 
{ 
    // Manager declarations go here. 
    // Only pointers or references to 
    // the Specialist class are used. 
}; 

在Manager.cpp:

#include "Specialist.h" 

// Manager definitions/implementations 
// using the Specialist class go here. 
// Full Specialist functionality can be used. 

在Specialist.h:

// Forward declaration: 
class Manager; 

// Class declaration: 
class Specialist 
{ 
    // Specialist declarations go here. 
    // Only pointers or references to 
    // the Manager class are used. 
}; 

在Specialist.cpp:

#include "Manager.h" 

// Specialist definitions/implementations 
// using the Manager class go here. 
// Full Manager functionality can be used. 
+0

感謝您的明確示例,那是迄今爲止唯一有用的答案! – fuenfundachtzig 2013-09-01 15:28:46

1

雖然其他人都在回答核心問題,但我想我會指出這一點。

在運行時,管理器創建並存儲專家列表。然後經理遍歷列表並要求每個專家初始化。在初始化期間,每位專家要求經理爲他們提供滿足一些描述的其他專家。一旦完成,管理員進入一個循環,在這個循環中,專家被依次要求執行他們的專門任務。

我只想指出,這需要分兩步進行。如果經理只知道一位專家到目前爲止,那麼經理如何告訴專家1專家B有什麼專家?所以你需要:

1)經理經歷專家名單,並要求他們表明自己。

2)經理通過專家名單,並詢問他們需要訪問什麼專業,告訴他們誰能滿足他們的要求。

3)經理經過專家名單並告訴他們執行他們的行爲。