Избежание круговых зависимостей заголовочных файлов [duplicate]

этот вопрос уже есть ответ здесь:

У вас есть хороший совет о том, как избежать циклические зависимости заголовочных файлов, пожалуйста?

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

существуют ли какие-либо общие, проверенные и рабочие правила? спасибо.

8 ответов


Если у вас есть циклическая зависимость, то вы делаете что-то неправильно.

например:

foo.h
-----
class foo {
public:
   bar b;
};

bar.h
-----
class bar {
public:
   foo f;
};

является незаконным, вы, вероятно, хотите:

foo.h
-----
class bar; // forward declaration
class foo {
   ...
   bar *b;
   ...
};

bar.h
-----
class foo; // forward declaration
class bar {
   ...
   foo *f;
   ...
};

и это нормально.

общие правила:

  1. убедитесь, что каждый заголовок может быть включен по себе.
  2. если вы можете использовать прямые объявления, используйте их!

  • используйте прямые объявления, где это возможно.
  • переместить любой заголовок включает из файла заголовка и в соответствующий файл cpp, если они нужны только файл cpp. Самый простой способ обеспечить это-сделать #include "myclass.h" первые включают в myclass.cpp.
  • введение интерфейсов в точке взаимодействия между отдельными классами может помочь уменьшить зависимости.

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

см. также Циклическая Зависимость Лучшая Практика


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

  1. придерживайтесь принципов OOAD. Не включать файл заголовка, если класс входит в композиции с текущим классом. Вместо этого используйте forward declaration.
  2. дизайн абстрактные классы в качестве интерфейсов для двух классов. Сделайте взаимодействие классов через этот интерфейс.

в зависимости от Ваших возможностей препроцессора:

#pragma once

или

#ifndef MY_HEADER_H
#define MY_HEADER_H
your header file
#endif

Если вам очень скучно создавать заголовочные файлы, возможно makeheaders от Hwaci (дизайнеров SQLite и ископаемых DVCS) может представлять интерес для вас.


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


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

также убедитесь, что вы придерживаетесь одного класса на заголовок.

тогда вы почти наверняка не ошибетесь.

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

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


Altough Artyom предоставил лучший ответ этот учебник также велик и предоставляет некоторые расширения http://www.cplusplus.com/forum/articles/10627/