Написание определения функции в заголовочных файлах на C ++ - PullRequest
61 голосов
/ 17 января 2009

У меня есть класс, который имеет много маленьких функций. Под небольшими функциями я подразумеваю функции, которые не выполняют никакой обработки, а просто возвращают литеральное значение. Что-то вроде:

string Foo::method() const{
    return "A";
}

Я создал заголовочный файл "Foo.h" и исходный файл "Foo.cpp". Но так как функция очень мала, я думаю поместить ее в сам заголовочный файл. У меня есть следующие вопросы:

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

Ответы [ 7 ]

66 голосов
/ 17 января 2009

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

headera.h

inline string method() {
    return something;
}

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

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

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

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

12 голосов
/ 17 января 2009

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

  • Может игнорировать встроенное ключевое слово это просто подсказка компилятору, а не команда) и генерировать автономные функции. Это может сделать это, если ваш функции превышают компилятор-зависимый порог сложности. например Очень много вложенные циклы.
  • Это может решить, чем ваш автономный функция является хорошим кандидатом на встроенное расширение.

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

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

  1. Компилятор, строящий программу использование DLL может решить не встроить функцию, так что это будет генерировать символьную ссылку на функция, которая не существует, и DLL не будет загружаться.
  2. Если вы обновите DLL и измените встроенная функция, клиентская программа все еще будет использовать старую версию этой функции, так как функция вставлен в код клиента.
4 голосов
/ 17 января 2009

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

То, что вы говорите о компиляторе, также верно. Нет никакой разницы для компилятора - кроме вставки & ndash; между кодом в заголовочном файле или .cpp файле.

2 голосов
/ 17 января 2009

Это зависит от стандартов кодирования, которые применяются в вашем случае, но:

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

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

Перед созданием объектного файла компилятором вызывается препроцессор (опция -E для gcc), и результат отправляется компилятору, который создает объект из кода.

Итак, короткий ответ:

- Объявление функций в заголовке полезно для скорости (но не для пробела) -

2 голосов
/ 17 января 2009
  1. Если ваши функции настолько просты, сделайте их встроенными, и вам все равно придется втыкать их в заголовочный файл. Кроме этого, любые соглашения - это просто соглашения.

  2. Да, компилятор разворачивает заголовочный файл, где встречаются операторы #include.

1 голос
/ 05 декабря 2016

C ++ не будет жаловаться, если вы это сделаете, но, вообще говоря, вы не должны.

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

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

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

Источник: http://archive.li/ACYlo (предыдущая версия главы 1.9 на learncpp.com)

0 голосов
/ 17 января 2009

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

...