Должен ли каждый файл C или C ++ иметь связанный заголовочный файл? - PullRequest
13 голосов
/ 04 марта 2009

Должен ли каждый файл .C или .cpp иметь для него файл заголовка (.h)?

Предположим, есть следующие файлы C:

  1. main.C

  2. Func1.C

  3. Func2.C

  4. Func3.C

где main() в файле Main.C. Должно быть четыре заголовочных файла

  1. main.h

  2. Func1.h

  3. Func2.h

  4. Func3.h

Или для всех файлов .C должен быть только один заголовочный файл?

Какой подход лучше?

Ответы [ 15 ]

30 голосов
/ 04 марта 2009

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

Вы можете иметь либо один заголовочный файл на файл C, либо, что наиболее вероятно, заголовочный файл для связанной группы файлов C.

Один из примеров этого - если у вас есть реализация BTree и вы добавили add, delete, search и т. Д. В их собственные файлы C, чтобы минимизировать перекомпиляцию при изменении кода.

В этом случае не имеет смысла иметь отдельные файлы заголовков для каждого файла C, поскольку заголовок - это API, представление библиотеки пользователю. Я сомневаюсь, что пользователь захочет включить 6 заголовочных файлов, чтобы иметь возможность использовать функции. Там будет один btree.h файл и один btree.lib файл, содержащий все объекты BTree, построенные из отдельных файлов C.

Другой пример можно найти в стандартных заголовках C. Мы не знаем наверняка, есть ли несколько файлов C для функций stdio.h (это то, как я бы это сделал, но это не единственный способ), но даже если бы они были, они рассматриваются как единое целое API. Вам не нужно включать stdio_printf.h, stdio_fgets.h и т. Д. - есть одна stdio.h для стандартной части ввода-вывода библиотеки времени выполнения C.

10 голосов
/ 04 марта 2009
  1. Заголовочные файлы не являются обязательными.

  2. #include просто скопируйте / вставьте любой включенный файл (включая исходные файлы .c)

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

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

  5. Используйте здравый смысл. C / C ++ на самом деле не для тех, кто без него.

6 голосов
/ 05 марта 2009

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

То, что я имею в виду, таково: пара файлов .cpp / .h - это почти то же, что все "модули" в любом случае заканчивают. Выполнение обоих требований избавляет от путаницы и плохой инженерии.

Например, когда я вижу какой-то интерфейс в заголовочном файле, я точно знаю, где искать / размещать его реализацию. И наоборот, если мне нужно раскрыть интерфейс чего-то, что было ранее скрыто в файле .cpp (например, статическая функция становится глобальной), я точно знаю, где ее разместить.

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

Итак: всегда определяйте как файлы .h, так и .c. Сделайте его стандартным, следуйте ему и смело полагайтесь на него . Жизнь намного проще, и простота является самой важной вещью в программном обеспечении.

4 голосов
/ 04 марта 2009

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

3 голосов
/ 04 марта 2009

Обычно для каждого файла .c / .cpp будет один файл .h.

2 голосов
/ 14 марта 2012

Бьярн Страуструп прекрасно объясняет это в своей книге «Язык программирования C ++» ....

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

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

Альтернативная физическая организация позволяет каждому логическому модулю иметь собственный заголовок, определяющий предоставляемые им средства. Каждый файл .c имеет соответствующий h. файл, указывающий, что он предоставляет (его интерфейс). Каждый модуль .c включает в себя свой собственный файл .h и, как правило, также другие файлы .h, которые определяют, что ему нужно от других модулей для реализации сервисов, объявленных в его интерфейсе. Эта физическая организация соответствует логической организации модуля. Подход с несколькими заголовками позволяет легко определить зависимости. Подход с использованием одного заголовка заставляет нас просматривать все объявления, используемые любым модулем, и решать, является ли он соответствующим. Простой факт заключается в том, что обслуживание кода всегда выполняется с неполной информацией и с локальной точки зрения. Лучшая локализация приводит к меньшему количеству информации для компиляции модуля и, следовательно, к более быстрой компиляции.

2 голосов
/ 04 марта 2009

Мне нравится помещать интерфейсы в заголовочные файлы и реализацию в файлах cpp. Мне не нравится писать C ++, где мне нужно добавить переменные-члены и прототипы в заголовок, а затем снова метод в C ++. Я предпочитаю что-то вроде:

module.h

struct IModuleInterface : public IUnknown
{
    virtual void SomeMethod () = 0;
}

module.cpp

class ModuleImpl : public IModuleInterface,
                   public CObject // a common object to do the reference
                                              // counting stuff for IUnknown (so we
                                              // can stick this object in a smart 
                                              // pointer).
{
    ModuleImpl () : m_MemberVariable (0)
    {
    }

    int m_MemberVariable;

    void SomeInternalMethod ()
    {
        // some internal code that doesn't need to be in the interface
    }

    void SometMethod ()
    {
        // implementation for the method in the interface
    }

    // whatever else we need
};

Я считаю, что это действительно чистый способ разделения реализации и интерфейса.

2 голосов
/ 04 марта 2009

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

1 голос
/ 04 марта 2009

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

1 голос
/ 04 марта 2009

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

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

Как уже говорили другие, если в файле cpp нет ничего, что могло бы быть доступно другим частям кода, как это обычно происходит с main.c, тогда файл заголовка не нужен.

Иногда стоит поместить все, что вы хотите показать, в один заголовочный файл (например, Func1and2and3.h), так что все, что знает о Func1, знает и о Func2, но я лично не заинтересован в этом, так как означает, что вы склонны загружать чертовски много мусора вместе с тем, что вам действительно нужно.

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

...