Я определил функцию в заголовочном файле. В отладке он компилируется и связывается нормально. В выпуске я получаю ошибку компиляции «множественное определение` blah :: blah (blah& бла) «» для каждого объектного файла каждого класса, который включает заголовок.
Я использую gcc-4.8.1. Я не могу опубликовать фактический код, в этой версии имена изменены, чтобы защитить невинных:
#ifndef INCLUDE_SMELLS_FUNNY
#define INCLUDE_SMELLS_FUNNY
#include "ParentClass.h"#include "ChildClassA.h"#include "ChildClassB.h"
namespace someplace {
bool smellsFunny(const ParentClass& someData) {
// All ChildClass As smell funny
if(dynamic_cast<const ChildClassA*>(&someData)) {
return true;
}
// If ChildClass B, need to check if smells funny
const ChildClassB* childB = dynamic_cast<const ChildClassB*>(&someData)
if(childB) {
return childB->MoreThanAWeekOld();
}
// Default is smells OK
return false;
}
}
#endif // INCLUDE_SMELLS_FUNNY
Я не смог найти какой флаг gcc ответственен.
Конечно, исправить это просто перенести реализацию в файл cpp. Но почему это необходимо? Почему это происходит только в релизе?
Я не могу сказать точно, но, возможно, в режиме отладки функция рассматривается как встроенная функция.
Это потому что ты определять функция в заголовочном файле, а затем включить этот заголовочный файл в несколько исходных файлов. Это означает, что функция будет определена в каждом исходном файле (технически, в модуле перевода), в который вы включаете заголовок.
Есть несколько решений для этого:
static
, Это означает, что каждое определение в единицах перевода не будет экспортировано.inline
, Это работает в основном так же, как первый вариант.Для небольшой функции, такой как у вас, альтернатива 2 может быть хорошей. Если у вас есть функция большего размера, которую нельзя легко встроить, вам следует использовать альтернативу 3.
Кроме того, если вы идете с номером 3, то вам не нужно включать файлы заголовков, а затем уменьшить риск циклического включения.
Поскольку вы поместили определение функции в заголовок, вам нужно пометить его inline
:
inline bool smellsFunny(const ParentClass& someData) { ...
Это связано с тем, что каждое место, в которое вы включаете заголовок, будет иметь определение функции, нарушая одно определение правила (ODR). inline
позволяет вам работать вокруг ODR.
Что касается того, почему нет проблемы в режиме выпуска, это не имеет смысла: код не верен. Может случиться так, что режим выпуска объявляет функцию встроенной для вас.
Если функция находится в заголовочном файле, вам необходимо объявить ее inline
, если это не определено в классе.