Циклическая зависимость в C++

факты:

  • у меня есть два преобладающих класса: менеджер и специалист.
  • есть несколько различных типов специалистов.
  • специалисты часто нуждаются в помощи других специалистов, чтобы выполнить свою работу.
  • менеджер знает всех специалистов, и изначально каждый специалист знает только своего менеджера. (Вот в чем проблема.)
  • во время выполнения менеджер создает и сохраняет список специалистов. Затем менеджер перебирает список и просит каждого специалиста инициализировать. Во время их инициализации каждый специалист просит менеджера предоставить им других специалистов, которые выполняют какое-то описание. Как только это будет завершено, менеджер переходит в цикл, в течение которого специалистам предлагается последовательно выполнять свою специализированную задачу.

Мне кажется, что это приличный шаблон, но так как у менеджера есть список специалистов, а у специалиста есть менеджер у меня проблемы с круговой зависимостью.

в этом случае я должен как-то заявить о существовании одного класса из другого? (Если да, то как?) Или я должен использовать некоторый шаблон дизайна, чтобы исправить эту проблему? (Если да, то что?) Также... Я думал, что сам шаблон был довольно o.к. так что я не против, чтобы кто-то помог мне понять, почему это плохо.

5 ответов


в обоих случаях forward объявляет другой класс:

диспетчер.h

class Specialist;

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

специалист.h

class Manager;

class Specialist
{
    Manager* m_myManager;
};

единственный раз, когда вам нужно ввести файл заголовка для класса, - это когда вам нужно использовать функцию-член или переменную в этом классе или нужно использовать класс как тип значения и т. д. Если вам нужен только указатель или ссылка на класс, достаточно прямого объявления.

обратите внимание, что вперед объявления предназначены не только для решения круговых зависимостей. Вы должны использовать forward объявления, где это возможно. Они всегда предпочтительнее включить дополнительный файл заголовка, если он вообще жизнеспособен.


это дело вкуса, но вперед декларации часто является хорошей альтернативой включениям в заголовочные файлы даже без круговых зависимостей. (Я не хочу обсуждать это в этом месте.) Итак, вот пример того, как применять прямые объявления для вашей проблемы:

В Диспетчере.h:

// Forward declaration:
class Specialist;

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

В Диспетчере.cpp:

#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.
};

В Специалист.cpp:

#include "Manager.h"

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

один из вариантов заключается в том, чтобы вперед объявить одного из людей, как вы предлагаете:

struct specialist;

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

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

однако, если вы в конечном итоге имеете больше древовидной структуры (где у вас есть несколько уровней управления, a 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.


это нормальные вещи. Вам просто нужно

class Manager;

в заголовке специалиста и

class Specialist; 

в заголовке менеджера

Если вы используете shared_ptrs вы можете найти shared_from_this полезно. (Не для зацикливания, а потому, что похоже, что вам это все равно понадобится)


пока все остальные отвечают на основной вопрос, я думал, что укажу на это.

во время выполнения менеджер создает и сохраняет список специалистов. Затем менеджер перебирает список и просит каждого специалиста инициализировать. Во время их инициализации каждый специалист просит менеджера предоставить им других специалистов, которые выполняют какое-то описание. Как только это будет завершено, менеджер переходит в цикл, во время которого специалистов просят последовательно выполнять свою специализированную задачу.

Я просто хочу подчеркнуть, что это должен быть двухступенчатый процесс. Как менеджер может сказать специалисту 1, какие специалисты существуют для задачи Б, если менеджер пока знает только об одном специалисте? Нужно так:

1) менеджер просматривает список специалистов и просит их назвать себя.

2) менеджер просматривает список специалистов и спрашивает их, к каким специальностям им нужен доступ, рассказать им, кто может выполнить их требования.

3) менеджер идет через список специалистов и говорит им, чтобы выполнить свои действия.