2010-10-25 164 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)经理经过专家名单并告诉他们执行他们的行为。