Только мои два цента. Речь идет не о виртуальных функциях в частности, а о встроенных функциях и функциях-членах в целом. Может быть, это полезно.
C ++
Что касается стандарта C ++, встроенная функция должна быть определена в каждой единице перевода, в которой она используется. И нестатическая встроенная функция будет иметь одинаковые статические переменные в каждой единице перевода и один и тот же адрес. Для достижения этого компилятор / компоновщик должен будет объединить несколько определений в одну функцию. Поэтому всегда помещайте определение встроенной функции в заголовок - или не помещайте его объявление в заголовок, если вы определяете его только в файле реализации (".cpp") (для функции, не являющейся членом), потому что если вы Если бы кто-то использовал его, вы бы получили ошибку компоновщика о неопределенной функции или о чем-то подобном.
Это отличается от не встроенных функций, которые должны быть определены только один раз во всей программе ( правило одного определения ). Для встроенных функций несколько определений, как указано выше, являются вполне нормальным случаем. И это не зависит от того, является ли вызов встроенным или нет. Правила о встроенных функциях все еще имеют значение. Придерживается ли компилятор Microsoft этих правил или нет - я не могу вам сказать. Если он придерживается стандарта в этом отношении, то он будет. Тем не менее, я мог бы предположить, что некоторые комбинации с использованием виртуальных, DLL и различных TU могут быть проблематичными. Я никогда не проверял это, но я думаю, что нет никаких проблем.
Для функций-членов, если вы определяете свою функцию в классе, она неявно встроена. И поскольку оно появляется в заголовке, автоматически выполняется правило о том, что оно должно быть определено в каждой единице перевода, в которой оно используется. Однако если вы определяете функцию вне класса и в файле заголовка (например, потому что существует циклическая зависимость с кодом между ними), то это определение должно быть встроенным, если вы включаете соответствующий файл более одного раза, чтобы Избегайте ошибок множественного определения, генерируемых компоновщиком. Пример файла f.h
:
struct f {
// inline required here or before the definition below
inline void g();
};
void f::g() { ... }
Это будет иметь тот же эффект, что и определение прямо в определении класса.
C99
Обратите внимание, что правила для встроенных функций более сложны для C99, чем для C ++. Здесь встроенная функция может быть определена как встроенное определение , из которых может существовать более одной во всей программе. Но если используется такое (встроенное) определение (например, если оно вызывается), то должно быть также точно одним внешним определением во всей программе, содержащейся в другой единице перевода. Обоснование этого (цитата из PDF-файла, объясняющая обоснование нескольких функций C99):
Встраивание в C99 расширяет спецификацию C ++ двумя способами. Во-первых, если функция объявлена встроенной в одной единице перевода, ее не нужно объявлять встроенной в любой другой единице перевода. Это позволяет, например, использовать библиотечную функцию, которая должна быть встроена в библиотеку, но доступна только через внешнее определение в другом месте. Альтернатива использования функции-оболочки для внешней функции требует дополнительного имени; и это также может отрицательно повлиять на производительность, если переводчик фактически не выполняет встроенную замену.
Во-вторых, требование, чтобы все определения встроенной функции были «точно такими же», заменялось требованием, чтобы поведение программы не зависело от того, реализован ли вызов с видимым встроенным определением или от внешнего определения , функции. Это позволяет встроенному определению быть специализированным для его использования в определенной единице перевода. Например, внешнее определение библиотечной функции может включать проверку некоторого аргумента, которая не требуется для вызовов, выполняемых из других функций в той же библиотеке. Эти расширения предлагают некоторые преимущества; и программисты, которые обеспокоены совместимостью, могут просто соблюдать более строгие правила C ++.
Почему я включаю C99 сюда? Потому что я знаю, что компилятор Microsoft поддерживает некоторые вещи C99. Так что на этих страницах MSDN некоторые вещи могут быть взяты и из C99 - но я ничего особенного не понял. Следует соблюдать осторожность при его чтении и применении техник к собственному коду C ++, предназначенному для переносимого C ++. Вероятно, информирование о том, какие части специфичны для C99, а какие нет.
Хорошим местом для тестирования небольших фрагментов C ++ на соответствие стандарту является онлайн-компилятор comeau . Если он отклонен, можно быть уверенным, что он не соответствует стандарту.