【问题标题】:multiple definition in header file头文件中的多重定义
【发布时间】:2011-02-13 05:31:16
【问题描述】:

鉴于此代码示例:

complex.h:

#ifndef COMPLEX_H
#define COMPLEX_H

#include <iostream>

class Complex
{
public:
   Complex(float Real, float Imaginary);

   float real() const { return m_Real; };

private:
   friend std::ostream& operator<<(std::ostream& o, const Complex& Cplx);

   float m_Real;
   float m_Imaginary;
};

std::ostream& operator<<(std::ostream& o, const Complex& Cplx) {
   return o << Cplx.m_Real << " i" << Cplx.m_Imaginary;
}
#endif // COMPLEX_H

complex.cpp:

#include "complex.h"

Complex::Complex(float Real, float Imaginary) {
   m_Real = Real;
   m_Imaginary = Imaginary;
}

ma​​in.cpp:

#include "complex.h"
#include <iostream>

int main()
{
   Complex Foo(3.4, 4.5);
   std::cout << Foo << "\n";
   return 0;
}

编译此代码时,我收到以下错误:

multiple definition of operator<<(std::ostream&, Complex const&)

我发现制作这个函数inline 可以解决问题,但我不明白为什么。为什么编译器会抱怨多重定义?我的头文件受到保护(使用#define COMPLEX_H)。

而且,如果抱怨operator&lt;&lt; 函数,为什么不抱怨public real() 函数,它也在标头中定义?

除了使用inline 关键字之外,还有其他解决方案吗?

【问题讨论】:

  • 您也可以将函数设为静态。 inline 说明符通常用于强制函数具有内部链接。
  • @Akanksh,实际上这正是“内联”的用途。
  • @Akanksh:在 C++ 中不推荐使用 static 来实现此目的。 static 已被匿名命名空间完全取代,尽管在这种特殊情况下,inline 是要走的路。
  • @Akanksh:这会通过在不同的翻译单元中赋予相同的名称 (operator&lt;&lt;) 不同的含义来引发 ODR 违规。

标签: c++ header-files


【解决方案1】:

问题是下面这段代码是定义,而不是声明:

std::ostream& operator<<(std::ostream& o, const Complex& Cplx) {
   return o << Cplx.m_Real << " i" << Cplx.m_Imaginary;
}

您可以标记上面的函数并将其设为“内联”,以便多个翻译单元可以定义它:

inline std::ostream& operator<<(std::ostream& o, const Complex& Cplx) {
   return o << Cplx.m_Real << " i" << Cplx.m_Imaginary;
}

或者您可以简单地将函数的原始定义移动到“complex.cpp”源文件中。

编译器不会抱怨“real()”,因为它是隐式内联的(在类声明中给出其主体的任何成员函数都被解释为好像它已被声明为“内联”)。预处理器保护防止你的头文件被一个翻译单元(“*.cpp”源文件“)多次包含。但是,两个翻译单元看到相同的头文件。基本上,编译器将“main.cpp”编译为“main.o”(包括“main.cpp”包含的头文件中给出的任何定义),并且编译器将“complex.cpp”单独编译为“complex.o”(包括“complex”包含的头文件中给出的任何定义.cpp")。然后链接器将“main.o”和“complex.o”合并为一个二进制文件;此时链接器为同名函数找到两个定义。也是在这个指出链接器尝试解析外部引用(例如,“main.o”指的是“Complex::Complex”,但没有该函数的定义……链接器从“complex.o”中找到定义,并解析那个参考)。

【讨论】:

    【解决方案2】:

    将实现移至 complex.cpp

    现在,在包含此文件后,实现正在编译到每个文件中。 稍后在链接期间,由于重复实现,存在明显的冲突。

    ::real() 未报告,因为它是隐式内联的(在类定义中实现)

    【讨论】:

      【解决方案3】:

      我遇到了这个问题,即使我的源文件和头文件是正确的。

      事实证明 Eclipse 使用的是以前(失败的)构建中的陈旧工件。

      要修复,请使用Project &gt; Clean 然后重建。

      【讨论】:

        【解决方案4】:

        将头文件中的函数定义指定为inline 的另一种方法是将其定义为static。这也将避免多重定义错误。

        【讨论】:

        • 这使得函数具有内部链接 - 不同于inline。特别是,如果内联函数试图在不同的翻译单元中调用它们,这将导致 ODR 违规(格式错误的 NDR)。
        猜你喜欢
        • 2014-08-04
        • 1970-01-01
        • 2021-04-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多