Страницы

Поиск по вопросам

пятница, 29 ноября 2019 г.

В каком случае нужно определять функцию в заголовочном файле?

#c++


Стоит ли определять функции сразу в заголовочном файле .h, или всегда определять
в файле .cpp?
    


Ответы

Ответ 1



Если функция будет использоваться только внутри одного CPP, то выносить в заголовочный файл большого смысла нет. Если функции вызывают друг друга, то можно разделить объявление и реализацию. Типа инкапсуляция, "приватная функция". Если функция будет использоваться в нескольких файлах, то следует объявить её в H и реализовать в CPP. Таким образом функция будет существовать в единственном экземпляре и её можно будет использовать где угодно, просто включив заголовочный файл. Типа публичный интерфейс, "публичная функция". Если функция шаблонная (template) или инлайновая (inline), то без вариантов следует размещать и объявление, и реализацию в заголовочном файле, потому что шаблонные функции по сути текстовые шаблоны, и их компилятор не сможет "извлечь" из CPP. Если объявление и определение инлайновой функции не разместить в заголовочном файле, то на стадии сборки посыпятся ошибки, что "определение функции не найдено". Если неинлайновую функцию разместить в заголовочном файле, то на стадии сборки могут посыпаться ошибки про "несколько определений функции", если заголовочный файл включён в нескольких модулях. В целом, вопрос сводится к "делать ли функцию инлайновой". А вот делать ли её инлайновой — это уже вопрос производительности выполнения против возможности перелинковки против скорости сборки. См. также When should I write the keyword 'inline' for a function/method?

Ответ 2



Формально вы можете класть реализацию функции в header. В некоторых случаях (например, шаблон) это даже необходимо¹. Но тем не менее, имеет смысл предпочитать класть функцию в .cpp-файл. Когда вы кладёте в header лишь декларацию, вы тем самым улучшаете возможности для раздельной компиляции. Например, если вы поменяете код функции, то компилятор перекомплирует лишь исходный файл, содержащий функцию, но ему не нужно будет перекомпилировать остальные файлы, включающие header. Следующая причина: инкапсуляция. Если вы положили функцию в header, то она видна всем. А значит, другие пользователи будут видеть функцию (и, возможно, писать свой код ориентируясь не на документацию, а на конкретную имплементацию). Пользователь, конечно, может заглянуть и в .cpp-файл, но если вы поставляете библиотеку, вы можете отдать лишь header + скомпилированный объектный файл, так что заглянуть будет некуда! Затем, ещё одна проблема есть для функций-членов. Допустим, у вас есть класс A с публичной функцией-членом f, и класс B с публичной функцией-членом g. Пусть реализация класса A пользуется B::g, а реализация класса B — функцией A::f. Пусть реализация класса A лежит в header-файле a.h, а класса B — в b.h. Поскольку A пользуется B::g, то a.h должен включать b.h. По аналогичной причине b.h должен включать a.h. Таким образом, если первым при компиляции какого-нибудь cpp-файла подключается a.h, то в нём подключается b.h, который теперь не может рекурсивно подключить a.h. Проблема с взаимно-рекурсивными функциями-членами решилась бы, если бы в header-файлах были только декларации классов, и вместо включения b.h можно было бы просто написать class B;². Ещё один случай, упомянутый в других ответах — локальная функция внутри .cpp-файла, которая нужна только внутри данного .cpp и не экспортируется. В этом случае, однако, вам нужно поместить её в анонимный namespace, чтобы избежать неприятных сюрпризов от компоновщика. Подумайте, что будет, если вы определите нечаянно две «невидимые» функции с одинаковыми сигнатурами (и ужаснитесь). ¹ Почти необходимо на самом деле, есть трюки, которые позволяют обходиться без этого. ² Поскольку форвард-декларации функций работают на уровне отдельной функции, эта проблема, кажется, не возникает для свободных функций (не-членов).

Ответ 3



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

Ответ 4



Лучше всегда в .cpp (кроме шаблонных методов, разумеется)

Ответ 5



На самом деле разницы нет, главное чтобы функция определялась до её использования. Я на практике встречал часто, что в хедере определяется функция, а в срр уже описывается. Если функция будет описана после её вызова (по коду)то она сработает только в том случае если её определили заранее допустим в хедере.

Комментариев нет:

Отправить комментарий