简体   繁体   English

header 文件中的多重定义

[英]multiple definition in header file

Given this code sample:鉴于此代码示例:

complex.h:复杂的.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:复杂的.cpp:

#include "complex.h"

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

main.cpp:主.cpp:

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

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

When compiling this code, I get the following error:编译此代码时,我收到以下错误:

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

I've found that making this function inline solves the problem, but I don't understand why.我发现使这个 function inline可以解决问题,但我不明白为什么。 Why does the compiler complain about multiple definition?为什么编译器会抱怨多重定义? My header file is guarded (with #define COMPLEX_H ).我的 header 文件受到保护(使用#define COMPLEX_H )。

And, if complaining about the operator<< function, why not complain about the public real() function, which is defined in the header as well?而且,如果抱怨operator<< function,为什么不抱怨public real() function,它也在 header 中定义?

And is there another solution besides using the inline keyword?除了使用inline关键字之外,还有其他解决方案吗?

The problem is that the following piece of code is a definition, not a declaration:问题在于以下代码是定义,而不是声明:

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

You can either mark the function above and make it "inline" so that multiple translation units may define it:您可以标记上面的函数并使其“内联”,以便多个翻译单元可以定义它:

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

Or you can simply move the original definition of the function to the "complex.cpp" source file.或者您可以简单地将函数的原始定义移动到“complex.cpp”源文件中。

The compiler does not complain about "real()" because it is implicitly inlined (any member function whose body is given in the class declaration is interpreted as if it had been declared "inline").编译器不会抱怨“real()”,因为它是隐式内联的(其主体在类声明中给出的任何成员函数都被解释为好像它已被声明为“内联”)。 The preprocessor guards prevent your header from being included more than once from a single translation unit ("*.cpp" source file"). However, both translation units see the same header file. Basically, the compiler compiles "main.cpp" to "main.o" (including any definitions given in the headers included by "main.cpp"), and the compiler separately compiles "complex.cpp" to "complex.o" (including any definitions given in the headers included by "complex.cpp"). Then the linker merges "main.o" and "complex.o" into a single binary file; it is at this point that the linker finds two definitions for a function of the same name. It is also at this point that the linker attempts to resolve external references (eg "main.o" refers to "Complex::Complex" but does not have a definition for that function... the linker locates the definition from "complex.o", and resolves that reference).预处理器守卫防止您的头文件从单个翻译单元(“*.cpp”源文件”)中多次包含。但是,两个翻译单元看到相同的头文件。基本上,编译器将“main.cpp”编译为“main.o”(包括“main.cpp”包含的头文件中给出的任何定义),编译器分别将“complex.cpp”编译为“complex.o”(包括“complex”包含的头文件中给出的任何定义) .cpp”)。然后链接器将“main.o”和“complex.o”合并成一个二进制文件;此时链接器找到了同名函数的两个定义。也是在此指向链接器尝试解析外部引用的点(例如,“main.o”指的是“Complex::Complex”,但没有该函数的定义……链接器从“complex.o”中定位定义,并解析那个参考)。

And is there another solution as using the inline keyword? 还有使用inline关键字的另一种解决方案吗?

Yes, there is. 就在这里。 Apart form defining the method inside the implementation file complex.cpp as mentioned by others, you can also put the definition into a nameless namespace. 除了在其他人提到的在实现文件complex.cpp定义方法的形式之外,您还可以将定义放入无名的命名空间中。

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

In practice, this will create a unique namespace for each compilation unit. 实际上,这将为每个编译单元创建一个唯一的命名空间。 That way, you prevent the name clashes. 这样,您可以防止名称冲突。 However, the names are still exported from the compilation unit but useless (since the names are unknown). 但是,名称仍从编译单元导出,但是没有用(因为名称未知)。

Putting the definition inside the implementation file is often a better solution. 将定义放在实现文件中通常是一个更好的解决方案。 However, for class templates you can't do that since C++ compilers don't support instantiating templates in a compilation unit different from the one they were defined in. In those case, you have to use either inline or an unnamed namespace. 但是,对于类模板,您不能这样做,因为C ++编译器不支持在不同于其定义单位的编译单元中实例化模板。在这种情况下,您必须使用inline或未命名的命名空间。

Move implementation to complex.cpp将实现移至 complex.cpp

Right now after including this file implementation is being compiled to every file.在包含此文件实现之后,现在正在将其编译到每个文件中。 Later during linking there's a obvious conflict because of duplicate implementations.稍后在链接过程中,由于重复实现,存在明显的冲突。

::real() is not reported because it's inline implicitly (implementation inside class definition) ::real() 未报告,因为它是隐式内联的(在类定义中实现)

I was having this problem, even after my source and header file were correct.我遇到了这个问题,即使我的源文件和头文件是正确的。

It turned out Eclipse was using stale artifacts from a previous (failed) build.事实证明,Eclipse 正在使用来自先前(失败)构建的陈旧工件。

To fix, use Project > Clean then rebuild.要修复,请使用Project > Clean然后重建。

An alternative to designating a function definition in a header file as inline is to define it as static .将头文件中的函数定义指定为inline的另一种方法是将其定义为static This will also avoid the multiple definition error.这也将避免多重定义错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM