2

我在头文件中定义了一个函数。在调试中,它可以很好地编译和链接。在发行版中,对于包含标头的每个类的每个目标文件,我都会收到编译错误“`blah::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 文件中。但为什么这是必要的?为什么这只会在发布时发生?

4

4 回答 4

2

由于您已将函数定义放在标题中,因此您需要对其进行标记inline

inline bool smellsFunny(const ParentClass& someData) { ...

这是因为包含标头的每个地方都会有函数的定义,从而打破了单一定义规则 (ODR)inline让您解决 ODR。

至于为什么在发布模式下没有问题,那是没有意义的:代码不正确。可能是发布模式正在为您声明内联函数。

于 2013-11-05T09:08:37.160 回答
2

这是因为您在头文件中定义了函数,然后将该头文件包含在多个源文件中。这意味着该函数将在包含标题的每个源文件(技术上,翻译单元)中定义。

有几个解决方案:

  1. 将函数标记为static。这意味着不会导出翻译单元中的每个定义。
  2. 将函数标记为inline。这与第一个替代方案基本相同。
  3. 将函数的定义放在源文件中,并且只在头文件中声明。

对于像您拥有的那样的小功能,那么替代 2 可能是一个不错的选择。如果你有一个较大的函数,不能轻易内联,你应该选择替代 3。

另外,如果你选择 3,那么你不需要包含头文件,然后减少循环包含的风险。

于 2013-11-05T09:08:49.973 回答
1

如果函数在头文件中,则需要声明它inline,除非它是在类中定义的。

于 2013-11-05T09:09:01.863 回答
1

我不能确切地说,但也许在调试模式下,该函数被视为内联函数。

于 2013-11-05T09:09:14.110 回答