C ++中的循环依赖

时间:2010-10-25 21:00:43

标签: c++ design-patterns circular-dependency

事实:

  • 我有两个主要课程:经理和专家。
  • 有几种不同类型的专家。
  • 专家经常需要其他专家的帮助才能完成工作。
  • 经理知道所有专家,最初每个专家只知道他们的经理。 (这是问题。)
  • 在运行时,Manager会创建并存储专家列表。然后,Manager迭代列表并要求每个专家进行初始化。在初始化期间,每位专家都要求经理向其提供满足某些描述的其他专家。完成后,管理器将进入循环,在此循环期间,专家被要求按顺序执行其专门任务。

对我来说,这似乎是一个不错的模式,但由于经理有一个专家列表,而专家有一个经理我遇到循环依赖问题。

这是一个我应该以某种方式向前宣布一个类存在于另一个类的情况吗? (如果是这样,怎么样?)或者我应该使用一些设计模式来解决这个问题? (如果是这样的话?)另外......我虽然模式本身很漂亮。所以我不介意有人帮我理解为什么这是一件坏事。

5 个答案:

答案 0 :(得分:23)

在这两种情况下,转发声明另一个类:

<强> Manager.h

class Specialist;

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

<强> Specialist.h

class Manager;

class Specialist
{
    Manager* m_myManager;
};

您需要为类引入头文件的唯一时间是您需要在该类中使用成员函数或变量,或者需要将该类用作值类型等。当您只需要一个指针时或者引用一个类,前瞻性声明就足够了。

请注意,前向声明不仅仅用于解决循环依赖关系。您应该尽可能使用前向声明。如果它们完全可行,它们始终更可取包括额外的头文件。

答案 1 :(得分:10)

这是一个品味问题,但前向声明通常是头文件中包含的一个很好的替代品,即使没有循环依赖。 (我不想在这个地方就此进行讨论。)所以,这里有一个关于如何为你的问题应用前向声明的例子:

在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.

答案 2 :(得分: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

答案 3 :(得分:1)

这是正常的事情。你只需要

class Manager;

在专家标题中

class Specialist; 

在经理标题中

如果您使用的是shared_ptrs,您可能会发现shared_from_this非常有用。 (不是为了循环而是因为听起来你无论如何都需要它)

答案 4 :(得分:1)

虽然其他人都在回答核心问题,但我认为我会指出这一点。

  

在运行时,Manager会创建并存储专家列表。然后,Manager迭代列表并要求每个专家进行初始化。在初始化期间,每位专家都要求经理向其提供满足某些描述的其他专家。完成后,管理器将进入循环,在此循环期间,专家被要求按顺序执行其专门任务。

我只想指出这需要分两个步骤。如果经理到目前为止只知道一位专家,经理如何告诉专家1任务B有哪些专家?所以你需要:

1)经理浏览专家名单并要求他们表明身份。

2)经理浏览专家名单,询问他们需要获得哪些专业,告诉他们谁可以满足他们的要求。

3)经理通过专家名单并告诉他们执行他们的行动。