Циркулярная зависимость в С++
Факты:
- У меня есть два преобладающих класса: менеджер и специалист.
- Существует несколько разных типов специалистов.
- Специалисты часто требуют помощи других специалистов, чтобы выполнить свою работу.
- Менеджер знает всех специалистов, и изначально каждый Специалист знает только своего Менеджера. (Это проблема.)
- Во время выполнения Менеджер создает и сохраняет список специалистов. Затем Менеджер выполняет итерацию по списку и просит каждого специалиста инициализировать. Во время их инициализации каждый специалист просит менеджера предоставить их другим специалистам, которые выполняют некоторое описание. Как только это будет завершено, Менеджер затем переходит в цикл, в течение которого специалистам предлагается последовательно выполнять свою специализированную задачу.
Мне кажется, что это приличный шаблон, но поскольку у менеджера есть список специалистов, а у специалиста есть менеджер, я получаю проблемы с круговой зависимостью.
Это случай, когда я должен каким-то образом объявить о существовании одного класса от другого? (Если это так, как?) Или я должен использовать некоторые шаблоны проектирования, чтобы исправить эту проблему? (Если это так?) Также... Я, хотя сам образец был довольно о.к. поэтому я не против, чтобы кто-то помог мне понять, почему это плохо.
Ответы
Ответ 1
В обоих случаях forward объявляет другой класс:
Manager.h
class Specialist;
class Manager
{
std::list<Specialist*> m_specialists;
};
Specialist.h
class Manager;
class Specialist
{
Manager* m_myManager;
};
Единственный раз, когда вам нужно ввести заголовочный файл для класса, - это когда вам нужно использовать функцию-член или переменную внутри этого класса или использовать класс как тип значения и т.д. Когда вам нужен только указатель или ссылки на класс, будет доступно декларацию вперед.
Обратите внимание, что форвардные объявления предназначены не только для решения круговых зависимостей. Вы должны использовать форвардные декларации, где это возможно. Они всегда предпочтительнее включать дополнительный файл заголовка, если он вообще жизнеспособен.
Ответ 2
Это вопрос вкуса, но forward декларация часто является хорошей альтернативой для включения в заголовочные файлы даже без круговых зависимостей. (Я не хочу поднимать обсуждение этого вопроса в этом месте.) Итак, вот пример того, как применять форвардные объявления для вашей проблемы:
В 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.
Ответ 3
Один из вариантов - переслать объявление одного из людей, как вы предлагаете:
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
.
Ответ 4
Это нормальный материал. Вам просто нужно
class Manager;
в заголовке специалиста и
class Specialist;
в заголовке менеджера
если вы используете shared_ptrs, вы можете найти shared_from_this полезным. (Не для петли, а потому, что это звучит так, как вам будет нужно в любом случае)
Ответ 5
В то время как все остальные отвечают на основной вопрос, я думал, что хочу указать на это.
Во время выполнения Менеджер создает и сохраняет список специалистов. Затем Менеджер выполняет итерацию по списку и просит каждого специалиста инициализировать. Во время их инициализации каждый специалист просит менеджера предоставить их другим специалистам, которые выполняют некоторое описание. Как только это будет завершено, Менеджер затем перейдет в цикл, в течение которого специалистам предлагается последовательно выполнять свою специализированную задачу.
Я просто хочу указать, что это должен быть двухэтапный процесс. Как менеджер может рассказать специалисту 1, какие специалисты существуют для задачи B, если менеджер знает об одном специалисте до сих пор? Поэтому вам нужно:
1) менеджер просматривает список специалистов и просит их идентифицировать себя.
2) менеджер просматривает список специалистов и спрашивает, к какому специальностям они нуждаются в доступе, сообщая им, кто может выполнять свои требования.
3) менеджер идет по списку специалистов и говорит им выполнять свои действия.