Определение встроенного ключевого слова vs заголовка
Какая разница между использованием ключевого слова inline перед функцией и просто объявлением всей функции в заголовке?
так...
int whatever() { return 4; }
против
.h:
inline int whatever();
.cpp
inline int myClass::whatever()
{
return 4;
}
что это делает:
inline int whatever() { return 4; }
Ответы
Ответ 1
Существует несколько аспектов:
Язык
- Когда функция помечена ключевым словом
inline
, тогда ее определение должно быть доступно в TU или программа плохо сформирована.
- Любая функция, определенная прямо в определении класса, неявно отмечена
inline
.
- Функция, помеченная
inline
(неявно или явно), может быть определена в нескольких TU (в отношении ODR), тогда как это не относится к регулярным функциям.
- Функции шаблона (не полностью специализированные) получают ту же обработку, что и
inline
.
Поведение компилятора
- Функция, отмеченная
inline
, будет излучаться как слабый символ в каждом объектном файле, где это необходимо, это может увеличить их размер (нарисуйте шаблон наворотом).
- В то время как компилятор фактически вводит вызов (т.е. копирует/вставляет код в точке использования вместо выполнения обычного вызова функции), полностью зависит от компилятора. Наличие ключевого слова может или не повлиять на решение, но в лучшем случае это намек.
поведение компоновщика
- Слабые символы объединяются вместе, чтобы иметь одно вхождение в финальной библиотеке. Хороший компоновщик может проверить, что несколько определений согласуются, но это не требуется.
Ответ 2
без inline
, вы, скорее всего, закончите с несколькими экспортированными символами, если функция объявлена в пространстве имен или глобальной области (приводит к ошибкам компоновщика).
однако, для класса (как показано в вашем примере) большинство компиляторов неявно объявляют метод как inline (-fno-default-inline
отключит это значение по умолчанию для GCC).
если вы объявляете функцию inline, компилятор может ожидать увидеть ее определение в переводе. поэтому вы должны зарезервировать его на время, когда определение будет видимым.
на более высоком уровне: определение в объявлении класса часто отображается для большего количества переводов. это может привести к лучшей оптимизации, и это может привести к увеличению времени компиляции.
Если ручная оптимизация и быстрые компиляции важны, необычно использовать ключевое слово в объявлении класса в наши дни.
Ответ 3
Цель inline
- разрешить определение функции в нескольких единицах перевода, что необходимо для того, чтобы некоторые компиляторы могли встроить ее там, где она использовалась. Он должен использоваться всякий раз, когда вы определяете функцию в файле заголовка, хотя вы можете опустить ее при определении шаблона или функции внутри определения класса.
Определение его в заголовке без inline
- очень плохая идея; если вы включаете заголовок из нескольких единиц перевода, тогда вы нарушаете правило одного определения; ваш код, вероятно, не будет ссылаться и может проявлять поведение undefined, если он это делает.
Объявление его в заголовке с inline
, но определение его в исходном файле также очень плохое; определение должно быть доступно в любой единицы перевода, которая его использует, но определяя его в исходном файле, он доступен только в одной единицы перевода. Если другой исходный файл содержит заголовок и пытается вызвать функцию, то ваша программа недействительна.
Ответ 4
Этот вопрос объясняет много о встроенных функциях Что означает __inline__? (хотя это было о ключевом слове inline.)
В принципе, он не имеет ничего общего с заголовком. Объявление всей функции в заголовке просто изменяет исходный файл, в котором находится источник функции. Ключевое слово Inline изменяет, где будет вставлена результирующая скомпилированная функция - на своем собственном месте, чтобы каждый вызов шел туда или на месте каждого звонка (лучше для производительности). Однако компиляторы иногда выбирают, какие функции или методы делают inline для себя, а ключевые слова - просто предложения для компилятора. Даже функции, которые не были указаны в строке, могут быть выбраны компилятором, чтобы стать встроенным, если это дает лучшую производительность.
Ответ 5
Если вы связываете несколько объектов с исполняемым файлом, обычно должен быть только один объект, содержащий определение функции. Для int whatever() { return 4; }
- любая единица перевода, которая используется для создания объекта, будет содержать определение (то есть исполняемый код) для функции whatever
. Компилятор не будет знать, на какой из них можно направлять абонентов. Если предоставляется inline
, тогда исполняемый код может быть или не быть встроенным на сайтах вызовов, но если ему не удастся, то компоновщику разрешено считать, что все определения одинаковы, и выберите один произвольно для прямого вызова. Если каким-то образом определения не совпадают, тогда он рассмотрел вашу ошибку и вы получите поведение undefined. Чтобы использовать inline
, определение должно быть известно при компиляции вызова, поэтому ваша идея поместить встроенное объявление в заголовок и встроенное определение в .cpp файле будет работать только тогда, когда все вызывающие абоненты будут позже в том же .cpp файл - в общем случае он сломан, и вы ожидаете, что (в номинальном выражении) встроенное определение функции появится в заголовке, который объявит его (или для него будет одно определение без предварительного объявления).