Лучшие практики заголовочного файла для определений типов

Я использую shared_ptr и STL экстенсивно в проекте, и это является ведущим к слишком долгим, подверженным ошибкам типам как shared_ptr< vector< shared_ptr<const Foo> > > (Я - программист ObjC предпочтением, где длинные имена являются нормой, и все еще это слишком очень.) Это было бы намного более ясно, я верю, для последовательного вызова этого FooListPtr и документируя соглашение о присвоении имен, которое означает "Ptr", shared_ptr и "Список" означают вектор shared_ptr.

Это легко к определению типа, но оно вызывает головные боли с заголовками. У меня, кажется, есть несколько опций того, где определить FooListPtr:

  • Foo.h. Это переплетает все заголовки и создает серьезные проблемы сборки, таким образом, это - обреченное на неудачу.
  • FooFwd.h ("передают заголовок"). Это - то, что Эффективный C++ предлагает, на основе iosfwd.h. Это очень последовательно, но издержки поддержания дважды количества заголовков кажутся раздражающими в лучшем случае
  • Common.h (соединяет всех их в один файл). Это уничтожает возможность многократного использования путем переплетения большого количества несвязанных типов. Вы теперь не можете только взять один объект и переместить его в другой проект. Это - обреченное на неудачу.
  • Некоторое воображение #define волшебство, что определение типа, если это уже не был typedefed. У меня есть прочная неприязнь к препроцессору, потому что я думаю, что он мешает новым людям к grok код, но возможно....
  • Используйте векторный подкласс, а не определение типа. Это кажется опасным...

Есть ли здесь лучшие практики? Как они складываются в реальном коде, когда возможность многократного использования, удобочитаемость и непротиворечивость являются главными?

Я отметил эту общественную Wiki, если другие хотят добавить дополнительные опции для обсуждения.

51
задан Rob Napier 1 March 2010 в 04:45
поделиться

3 ответа

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

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

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

6
ответ дан 7 November 2019 в 10:25
поделиться

+1 для документирования соглашений typedef.

  • Foo.h - не могли бы вы подробно рассказать о своих проблемах?
  • FooFwd.h - Я бы не стал использовать их обычно, только на «очевидных горячих точках». (Да, «горячие точки» трудно определить). Это не меняет правил IMO, потому что, когда вы вводите заголовок fwd, связанные typedefs из foo.h перемещаются туда.
  • Common.h - круто для небольших проектов, но не масштабируется, согласен.
  • Какой-то причудливый #define ... ПОЖАЛУЙСТА, НЕТ! ...
  • Используйте подкласс векторов - ничего лучше. Но вы можете использовать сдерживание.

Итак, вот предварительные предложения (измененные на основе того другого вопроса ..)

  1. Заголовки стандартного типа , и т. Д.может перейти в предварительно скомпилированный заголовочный / общий включаемый файл для проекта. Это неплохо. (Я лично все еще включаю их там, где это необходимо, но это работает в дополнение к помещению их в PCH.)

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

  3. Связанные типы (например, FooListPtr ) идут туда, где объявлен Foo, , если связанный тип является основным использованием типа. Это почти всегда верно для некоторых типов - например, shared_ptr .

  4. Если Foo получает отдельный заголовок прямого объявления и связанный тип с этим согласен, он также перемещается в FooFwd.h.

  5. Если тип связан только с определенным интерфейсом (например, параметр для общедоступного метода), он попадает туда.

  6. Если тип является общим (и не соответствует ни одному из предыдущих критериев), он получает свой собственный заголовок. Обратите внимание, что это также означает включение всех зависимостей.

Мне это кажется «очевидным», но я согласен, что он не годится в качестве стандарта кодирования.

4
ответ дан 7 November 2019 в 10:25
поделиться

К сожалению, при использовании типизаторов вам придется выбирать между не самыми лучшими вариантами для ваших заголовочных файлов. Есть особые случаи, когда вариант один (прямо в заголовке класса) работает хорошо, но похоже, что вам он не подойдет. Есть также случаи, когда последний вариант работает хорошо, но обычно это происходит, когда вы используете подкласс для замены шаблона, включающего класс с единственным членом типа std::vector. В вашей ситуации я бы использовал решение с объявлением заголовка вперед. Это дополнительная типизация и накладные расходы, но иначе это не был бы C++, верно? Это сохраняет вещи раздельными, чистыми и быстрыми.

1
ответ дан 7 November 2019 в 10:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: