C++ код в заголовочных файлах



мой личный стиль С C++ всегда должен помещать объявления классов в файл включения и определения в a .файл cpp, очень похожий на оговоренный в ответ Локи на C++ Заголовочные Файлы, Разделение Кода. По общему признанию, часть причины, по которой мне нравится этот стиль, вероятно, связана со всеми годами, которые я провел, кодируя Modula-2 и Ada, оба из которых имеют аналогичную схему с файлами спецификаций и файлами тела.



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



Я уже не так гибок, как раньше, так что я не очень-то стремлюсь вскарабкаться на его подножку, пока не увижу там еще несколько человек. Так как общее это идиома на самом деле?



просто чтобы дать некоторую структуру ответов: Это сейчас Путь, очень распространенный, несколько распространенный, необычный или сумасшедший?

618   14  

14 ответов:

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

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

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

итог, вы были правы, он ошибается.

EDIT: Я думал о вашем вопросе. Есть один случай, когда то, что он говорит, правда. шаблоны. Многие новые" современные " библиотеки, такие как boost, активно используют шаблоны и часто являются "только заголовком"."Однако это следует делать только при работе с шаблонами, поскольку это единственный способ сделать это при работе с шаблонами их.

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

Если вы будете искать вокруг, вы увидите довольно много людей, пытающихся найти способ уменьшить время компиляции при работе с boost. Например: как сократить время компиляции с помощью Boost Asio, который видит компиляцию 14s одного файла 1K с включенным повышением. 14s может показаться, что не " взрывается", но это, конечно, намного дольше, чем обычно, и может сложить довольно быстро. При работе с большим проектом. Только библиотеки заголовков влияют на время компиляции вполне измеримым образом. Мы просто терпим это, потому что boost настолько полезен.

кроме того, есть много вещей, которые нельзя сделать только в заголовках (даже boost имеет библиотеки, которые вам нужно связать с определенными частями, такими как потоки, файловая система и т. д.). Основным примером является то, что вы не можете иметь простые глобальные объекты в заголовке только libs (если вы не прибегаете к мерзости, которая является синглтоном), поскольку вы столкнетесь с несколькими ошибками определения. Примечание: встроенные переменные C++17 сделают этот конкретный пример выполнимым в будущем.

в качестве заключительной точки, при использовании boost в качестве примера кода только заголовка, огромная деталь часто пропускается.

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

день, когда кодеры C++ соглашаются на Путь ягнята лягут со львами, палестинцы обнимут израильтян, а кошкам и собакам разрешат жениться.

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

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

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

то, что может информировать Вас коллега, - это понятие о том, что большинство кода C++ должно быть шаблонизировано, чтобы обеспечить максимальное удобство использования. И если он шаблонный, то все должно быть в заголовочном файле, чтобы клиентский код мог видеть его и создавать его экземпляр. Если это достаточно хорошо для Boost и STL, это достаточно хорошо для нас.

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

Я думаю, что ваш коллега умный и правильный.

полезные вещи, которые я нашел, что положить все в заголовки это:

  1. нет необходимости писать и синхронизировать заголовки и источники.

  2. структура проста, и никакие циклические зависимости не заставляют кодер создавать "лучшую" структуру.

  3. портативное, легко встраивается в новый проект.

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

  1. изменение исходного файла, скорее всего, изменит заголовочные файлы, что приведет к повторной компиляции всего проекта.

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

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

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

помните: глупая последовательность-это Хобгоблин маленьких умов.

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

как сказал Туомас, ваш заголовок должен быть минимальным. Чтобы быть полным, я немного расширюсь.

я лично использую 4 типа файлов в моем C++ проекты:

  • общие:
  • заголовок переадресации: в случае шаблонов и т. д. Этот файл получает объявления о переадресации, которые появятся в заголовке.
  • заголовок: этот файл включает заголовок пересылки, если таковой имеется, и объявляет все, что я хочу быть общедоступным (и определяет класс...)
  • Private:
  • Private header: этот файл является заголовком, зарезервированным для реализации, он включает заголовок и объявляет вспомогательные функции / структуры (например, для Pimpl или предикатов). Пропустите, если это не нужно.
  • исходный файл: он включает в себя частный заголовок (или заголовок, если нет частного заголовка) и определяет все (не шаблон...)

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

это означает, что я предпочитаю прямое объявление над #include директива в моих заголовках, когда я могу уйти с ними.

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

положив его в целом:

// example_fwd.hpp
// Here necessary to forward declare the template class,
// you don't want people to declare them in case you wish to add
// another template symbol (with a default) later on
class MyClass;
template <class T> class MyClassT;

// example.hpp
#include "project/example_fwd.hpp"

// Those can't really be skipped
#include <string>
#include <vector>

#include "project/pimpl.hpp"

// Those can be forward declared easily
#include "project/foo_fwd.hpp"

namespace project { class Bar; }

namespace project
{
  class MyClass
  {
  public:
    struct Color // Limiting scope of enum
    {
      enum type { Red, Orange, Green };
    };
    typedef Color::type Color_t;

  public:
    MyClass(); // because of pimpl, I need to define the constructor

  private:
    struct Impl;
    pimpl<Impl> mImpl; // I won't describe pimpl here :p
  };

  template <class T> class MyClassT: public MyClass {};
} // namespace project

// example_impl.hpp (not visible to clients)
#include "project/example.hpp"
#include "project/bar.hpp"

template <class T> void check(MyClass<T> const& c) { }

// example.cpp
#include "example_impl.hpp"

// MyClass definition

Спаситель здесь что в большинстве случаев прямой заголовок бесполезен: необходим только в случае typedef или template и так это заголовок реализацию ;)

Если этот новый способ реально Путь, мы могли бы работать в другом направлении в наших проектах.

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

также мы используем "непрозрачный указатель"-шаблон когда это применимо.

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

, чтобы добавить больше удовольствия вы можете добавить .ipp файлы, которые содержат реализацию шаблона (который включается в .hpp), а .hpp содержит интерфейс.

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

Я лично делаю это в заголовочных файлах:

// class-declaration

// inline-method-declarations

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

Я бы не стал помещать все методы в заголовочный файл. Компилятор (обычно) не сможет встроить виртуальные методы и будет (вероятно) только встроенные малые методы без циклов (полностью зависит от компилятора).

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

ИМХО, у него есть заслуга, только если он делает шаблоны и/или метапрограммирование. Есть много причин, уже упомянутых, что вы ограничиваете заголовочные файлы только объявлениями. Они просто такие... заголовки. Если вы хотите включить код, скомпилировать его в библиотеку и связать его.

Я положил всю реализацию из определения класса. Я хочу иметь комментарии doxygen из определения класса.

разве это не зависит от сложности системы и внутренних соглашений?

на данный момент я работаю над симулятором нейронной сети, который невероятно сложен, и принятый стиль, который я должен использовать, - это:

определения классов в classname.h
Код класса в classnameCode.h
исполняемый код класса.cpp

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

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

Comments

    Ничего не найдено.