Сложная круговая зависимость - PullRequest
6 голосов
/ 19 марта 2011

Какова лучшая практика решения круговой зависимости в C++?

Я мог бы использовать предварительное объявление, но затем я получаю ошибку pointer to incomplete class type is not allowed.Означает ли это, что два класса, которые используют указатель друг на друга, не могут быть зависимыми?

Кроме того, я подумал о том, чтобы заранее объявить каждый класс и затем включить каждый заголовок решения в main.cpp, так что все это в одном месте.,Вы бы порекомендовали это?

Ниже приведен фрагмент всего проекта, так что вы можете обратиться к нему, если проблему лучше объяснить на примере, с которым я знаком, но это будет просто теоретическим.Спасибо

Models depending on each other

Ответы [ 3 ]

10 голосов
/ 19 марта 2011

Вам просто нужно правильно использовать прямое объявление:

  1. Поместить весь код в файлы cpp
  2. Поместить только объявление класса в заголовочный файл
  3. В заголовочном файле:
    1. Используйте прямое объявление, если вы используете только указатель или ссылку.
    2. В противном случае вы должны включить заголовочный файл.( Не добавить необязательные включает) *
  4. В файле cpp
    1. включает все требуемые заголовочные файлы.

Примечание: Добавить включить охранников.

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

7 голосов
/ 19 марта 2011

Одна мысль состоит в том, чтобы ввести интерфейсы и удалить круговые зависимости. Таким образом, у вас есть IEffect, от которого зависят Effect, Player и EffectContainer. Возможно, если Player зависит от определенного поведения Effect, а EffectContainer зависит от другого набора поведения, я бы рассмотрел возможность введения двух интерфейсов, эффективно следуя принципу разделения интерфейсов . Это также будет следовать принципу обращения .

3 голосов
/ 19 марта 2011

Обычно это достигается тем, что каждый заголовочный файл предварительно объявляет классы, которые ему нужны до его #include.Кроме того, в заголовочных файлах не должно быть кода.Таким образом, вы получите:

class Effect;
class Player;
class GameStack;

#include <vector>
// more includes

class EffectContainer { ... }

и эквивалент в каждом месте.Затем в ваших .cpp файлах вы на самом деле #include заголовки для других классов.Это будет работать, если ваши объекты не имеют круговой зависимости от расположения в памяти других классов.Это означает, что методы и члены могут ссылаться на другие классы только по ссылке или по указателю (но не по значению).Это может немного усложниться, если у вас есть такие вещи, как

 class EffectContainer {
   std::Vector<Effect> effects;
 }

 class Effect {
   boost::shared_ptr<EffectContainer> parent;
 }

, поскольку расширение шаблона иногда требует полных типов, а не просто предварительно объявленных типов.Один из способов избежать этой проблемы во многих библиотеках - указатель на частный шаблон impl (часто называемый шаблоном PIMPL), где каждый класс определяется следующим образом:

class FooImpl;
class Foo {
  FooImpl* impl;
}

, тогда FooImpl полностью определяется вфайл .cpp и ваши проблемы округлости могут быть решены.Этот дизайн также полезен, поскольку поддерживает двоичную совместимость между версиями вашей библиотеки.Это немного многословно, но никто не сказал, что C++ был лаконичным языком.

...