19

事实:

  • 我有两个主要类别:经理和专家。
  • 有几种不同类型的专家。
  • 专家通常需要其他专家的帮助才能完成工作。
  • 经理认识所有的专家,最初每个专家只认识他们的经理。(这就是问题。)
  • 在运行时,管理器创建并存储专家列表。然后 Manager 遍历列表并要求每个专家进行初始化。在初始化期间,每个专家都要求经理向他们提供满足某些描述的其他专家。一旦完成,经理就会进入一个循环,在这个循环中,专家被依次要求执行他们的专业任务。

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

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

4

5 回答 5

26

在这两种情况下,前向声明另一个类:

经理.h

class Specialist;

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

专家.h

class Manager;

class Specialist
{
    Manager* m_myManager;
};

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

请注意,前向声明不仅仅用于解决循环依赖。您应该尽可能使用前向声明。如果它完全可行的话,它们总是比包含一个额外的头文件更可取。

于 2010-10-25T21:04:48.383 回答
12

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

在 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 "Manager.h"

#include "Specialist.h"

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

在专家.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 "Specialist.h"

#include "Manager.h"

// Specialist definitions/implementations
// using the Manager class go here.
// Full Manager functionality can be used.
于 2010-10-25T21:14:47.113 回答
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.

于 2010-10-25T21:03:31.883 回答
1

这是正常的东西。您只需

class Manager;

在专家标题和

class Specialist; 

在经理标题中

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

于 2010-10-25T21:04:22.733 回答
1

当其他人都在回答核心问题时,我想我会指出这一点。

在运行时,管理器创建并存储专家列表。然后 Manager 遍历列表并要求每个专家进行初始化。在初始化期间,每个专家都要求经理向他们提供满足某些描述的其他专家。一旦完成,经理就会进入一个循环,在这个循环中,专家被依次要求执行他们的专业任务。

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

1) 经理检查专家名单并要求他们表明自己的身份。

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

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

于 2010-10-25T21:29:36.730 回答