В C вы не можете иметь определение / реализацию функции внутри заголовочного файла. Тем не менее, в C ++ вы можете иметь полную реализацию метода внутри заголовочного файла. Почему поведение отличается?
В C, если вы определите функцию в файле заголовка, то эта функция появится в каждом компилируемом модуле, который включает этот файл заголовка, и для этой функции будет экспортирован открытый символ. Так, если функция additup определена в header.h, и foo.c и bar.c оба включают header.h, то foo.o и bar.o будут включать копии additup.
Когда вы перейдете связать эти два объектных файла вместе, компоновщик увидит, что символ additup определен более одного раза, и не допустит этого.
Если вы объявите функцию статической, то символ не будет экспортирован. Объектные файлы foo.o и bar.o будут по-прежнему содержать отдельные копии кода для функции, и они смогут их использовать, но компоновщик не сможет увидеть ни одну копию функции, поэтому он не буду жаловаться Конечно, ни один другой модуль не сможет увидеть эту функцию. И ваша программа будет раздута с двумя одинаковыми копиями одной и той же функции.
Если вы только объявите функцию в заголовочном файле, но не определите ее, а затем определите ее только в одном модуле, то компоновщик увидит одну копию функции, и каждый модуль в вашей программе сможет увидеть ее и используй это. И ваша скомпилированная программа будет содержать только одну копию функции.
Таким образом, вы можете иметь определение функции в заголовочном файле на C, это просто плохой стиль, плохая форма и общая плохая идея.
(Под «объявить» я имею в виду предоставление прототипа функции без тела; под «определением» я подразумеваю фактический код тела функции; это стандартная терминология Си.)
#ifndef HEADER_H
не должна предотвратить?В этом отношении C и C ++ ведут себя примерно одинаково - у вас могут быть
inline
функции в заголовках. В C ++ любой метод, тело которого находится внутри определения класса, неявноinline
. Если вы хотите сделать то же самое в C, объявите функцииstatic inline
.источник
static inline
» ... и вы по-прежнему будете иметь несколько копий функции в каждой единице перевода, которая ее использует. В C ++ сstatic
inline
нефункциональным у вас будет только одна копия. Чтобы фактически иметь реализацию в заголовке на C, вы должны 1) пометить реализацию какinline
(напримерinline void func(){do_something();}
) и 2) фактически сказать, что эта функция будет в некоторой конкретной единице перевода (напримерvoid func();
).Концепция заголовочного файла требует небольшого пояснения:
Либо вы даете файл в командной строке компилятора, либо делаете '#include'. Большинство компиляторов принимают командный файл с расширением c, C, cpp, c ++ и т. Д. В качестве исходного файла. Однако они обычно включают параметр командной строки, чтобы разрешить использование любого произвольного расширения для исходного файла.
Обычно файл, указанный в командной строке, называется «Источник», а включенный файл называется «Заголовок».
Шаг препроцессора фактически берет их все и заставляет все выглядеть как один большой файл для компилятора. То, что было в заголовке или в источнике, на самом деле не имеет значения на данный момент. Обычно есть опция компилятора, которая может показать результаты этого этапа.
Таким образом, для каждого файла, который был указан в командной строке компилятора, огромный файл предоставляется компилятору. Это может иметь код / данные, которые будут занимать память и / или создавать символ для ссылки из других файлов. Теперь каждый из них будет генерировать изображение объекта. Компоновщик может дать «дубликат символа», если один и тот же символ найден в более чем двух объектных файлах, которые связаны друг с другом. Возможно, это причина; не рекомендуется помещать код в заголовочный файл, который может создавать символы в объектном файле.
«Встроенные» обычно встроены ... но при отладке они могут не быть встроенными. Так почему же компоновщик не дает многократно определенных ошибок? Простые ... Это «слабые» символы, и пока все данные / код для слабого символа из всех объектов имеют одинаковый размер и содержание, связанная копия будет сохранять одну копию и отбрасывать копию из других объектов. Оно работает.
источник
Вы можете сделать это в C99:
inline
функции гарантированно будут предоставлены где-то еще, поэтому, если функция не была встроенной, ее определение переводится в объявление (т. Е. Реализация отбрасывается). И, конечно, вы можете использоватьstatic
.источник
С ++ стандартные кавычки
В проекте стандарта C ++ 17 N4659 10.1.6 « Встроенный спецификатор» говорится, что методы неявно встроены:
и далее далее мы видим, что встроенные методы не только могут, но и должны быть определены во всех единицах перевода:
Это также явно упоминается в примечании к 12.2.1 «Функции-члены»:
GCC 8.3 реализация
main.cpp
Компилировать и просматривать символы:
выход:
затем мы видим,
man nm
чтоMyClass::myMethod
символ помечен как слабый в объектных файлах ELF, что означает, что он может появляться в нескольких объектных файлах:источник
Вероятно, по той же причине, по которой вы должны поместить полную реализацию метода в определение класса в Java.
Они могут выглядеть одинаково, с извилистыми скобками и многими одинаковыми ключевыми словами, но это разные языки.
источник