У меня есть файл hpp, как это:
#ifndef __MYLIB_H
#define __MYLIB_H
class A {
public:
void func1();
void func2();
};
void A::func1() {
// maybe do something
}
#endif
Существует соответствующий файл cpp, в котором есть реализация func2. Заголовочный файл включен в другие файлы проекта (и они включены в еще большее количество файлов). Когда я пытаюсь построить это, я получаю ошибки компоновщика «множественного определения» для func1
, Почему это должно произойти? Так как я защитил заголовочный файл с помощью #ifndef
Я не ожидал ошибки.
Если я добавлю ключевое слово inline
к определению func1
, то все в порядке. Так что, если мне не важно, будет ли функция встроенной, и я не хочу иметь определение в теле класса, я не могу включить определение в файл hpp? Если кто-то может объяснить, что здесь происходит, это будет очень полезно. Я использую GCC 6.
Напомним, что содержимое всего заголовка, а также любые файлы, которые он может включать, практически «вставляются» в каждый блок перевода* это включает в себя заголовок в точке включения.
Вот почему эффект включения заголовка в несколько файлов CPP такой же, как если бы вы буквально копировали определение функции в каждый CPP, т. Е. Он такой же, как если бы вы написали несколько идентичных определений одной и той же функции-члена.
Проблема решается объявлением функции inline
потому что C ++ допускает несколько определений встроенных функций, если они идентичны друг другу. Перемещение тела функции в объявление класса также работает, потому что такие функции автоматически рассматриваются inline
,
* В этом контексте «единица перевода» — это причудливое имя для вашего файла CPP.
Проблема в том, что вы переопределяете func1
в каждом файле.
#include
Директива — это очень простая команда, которая просто вставляет содержимое файла заголовка в указанное место.
Результатом этого является то, что каждый раз, когда вы #include
ваш заголовок вы «переопределяете» func1
в этом файле.
Реализации обычно идут в связанных .cpp
файлы. Поместить их в заголовки проблематично. inline
это хак, чтобы обойти это, вы, вероятно, не хотите использовать, так как он удаляет этот код везде, где он появляется, что приводит к дикому дублированию.
Помните, #include
очень похоже на вставку этого файла в код там, где появляется эта директива. Неверно иметь более одной реализации для функции, поэтому они разделены между заголовком для целей подписи, поэтому понятно, как правильно использовать эти функции, и реализацией для реального кода.