Прототипы функций в заголовочном файле

Рейтинг: 2Ответов: 4Опубликовано: 19.04.2015

Допустим есть .cpp файл в котором мной написано некоторое количество функций стоит ли для него делать заголовочный файл с прототипами функций или можно переименовать его в .h файл и просто его подключать как обычный заголовок? что лучше?

Ответы

▲ 3Принят

Для начала, правильный путь — это конечно вынести прототипы отдельно, а имплементацию отдельно. Таким путём вы получаете преимущества раздельной компиляции: ваши функции компилируются отдельно, лишь один раз.

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

(Немного офтопика: inline в C++ не означает «встрой эту функцию в место вызова вместо реального вызова». Такими оптимизациями заведует компилятор и только он. Смысл inline именно такой: сообщить компоновщику, что функции с одним именем, которые он видит в разных модулях — это одна и та же функция, и не нужно выдавать ошибку.)

Ещё одна проблема, которую позволяют изящно решать header'ы — зависимости. Например, рассмотрим взаимно-рекурсивные функции. Когда вы пользуетесь функцией f, вы можете вызвать в ней другую функцию g. Допустим, вы пишете ваши функции в header'е. Если функция g определена ниже функции f, вам придётся ещё и добавлять декларацию функции g. В случае, если имплементация находится вне header'а, проблема не возникает (но зато, конечно, вам приходится делать декларации всех функций).

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

Существуют, однако, случаи, когда код приходится держать в header'е. Например, шаблонные функции (да и классы) должны быть в нём, иначе у вас могут быть проблемы с инстанциацией.

▲ 2

Оба варианта хороши.

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

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

▲ 1

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

    **header.h** 
    #ifndef H_HEADER
    #define H_HEADER
     
    void foo();
    #endif

    ****
    **functions.cpp**
    #include "header.h"
    void foo()
    {
         ****
    };

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

▲ 1

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