【问题标题】:C++ code generating errors LNK2005 & LNK1169 on VS2019VS2019 上的 C++ 代码生成错误 LNK2005 和 LNK1169
【发布时间】:2021-06-02 12:06:03
【问题描述】:

以下代码在 Visual Studio 2019 上生成错误 LNK2005 和 LNK1169。

LNK2005: "void__cdecl OverloadingPlus(void)" (?OverloadingPlus@@YAXXZ) already defined in main.obj
LNK1169: one or more multiply defined symbols found

我通过学习 Udemy 的课程达到了这一点,讲师似乎对此没有任何问题。

类似的问题似乎与使用全局变量、在头文件中定义函数、不使用 #ifndef 警卫有关……但据我所知,情况似乎并非如此。

此问题仅在包含 operator+ 重载后才开始出现,但 operator<< 不会触发任何错误,即使它们已以相同的方式声明和定义。
有趣的是,如果我删除对 OverloadingPlus.h 文件的任何引用,并将 #include Complex.h 添加到 main.cpp,问题就会消失。
我看不到在OverloadingPlus.h 中包含Complex.h 是如何专门为operator+ 的多重定义做出贡献的,即使它在整个项目中只包含一次并且定义是在Complex.cpp 中实现的,而不是在头文件,正如对类似问题的回答中所指出的那样。
还尝试使用#ifndef 语句包围OverloadingPlus.h 的内容,以确保它只使用一次而不会改变任何事情。
我试图在类中声明一个不同的 operator+ 重载(你可以看到它被注释掉了),但它会产生相同的错误。
注释了对任何其他文件的所有引用,以确保仅包含一次 Complex 定义。也没有帮助。

代码如下:
main.cpp

//#include "OverloadingAssignmentOperator.h"
//#include "OverloadingLeftBitShiftOperator.h"
//#include "ComplexNumberClass.h"
#include "OverloadingPlus.h"

int main() {
    //OverloadingAssignmentOperator();
    //OverloadingLeftBitShiftOperator();
    //ComplexNumberClass();
    OverloadingPlus();

    return 0;
}

OverloadingPlus.h

#ifndef OVERLOADING_PLUS_H
#define OVERLOADING_PLUS_H

#include "Complex.hpp"

using namespace complex;

void OverloadingPlus() {
    Complex c1(1, 4);
    Complex c2(3, 2);

    std::cout << c1 + c2 << std::endl;
}
#endif 

复杂的.hpp

#ifndef COMPLEX_HPP
#define COMPLEX_HPP

#include <iostream>

namespace complex {

    class Complex {
    private:
        double real;
        double imaginary;

    public:
        Complex();
        Complex(double, double);
        Complex(const Complex&);

        const Complex& operator=(const Complex& other);
        //const Complex operator+(const Complex& r);

        double getReal() const { return real; }
        double getImaginary() const { return imaginary; }
    };

    Complex operator+(const Complex& l, const Complex& r);
    std::ostream& operator<<(std::ostream& out, const Complex& c);
}

#endif // !__COMPLEX_HPP__

复杂的.cpp

#include "Complex.hpp"

namespace complex {
    Complex::Complex() : real(0), imaginary(0) {}
    Complex::Complex(double r, double i) : real(r), imaginary(i) {}
    Complex::Complex(const Complex& other) {

        std::cout << "Copy constructor..." << std::endl;

        real = other.real;
        imaginary = other.imaginary;
    }

    const Complex& Complex::operator=(const Complex& other) {
        real = other.real;
        imaginary = other.imaginary;

        return *this;
    }

    //const Complex Complex::operator+(const Complex& r) {
    //  return Complex(real + r.getReal(), imaginary + r.getImaginary());
    //}

    // 

    Complex operator+(const Complex& l, const Complex& r) {
        return Complex(l.getReal()+r.getReal(), l.getImaginary()+r.getImaginary());
    }

    std::ostream& operator<<(std::ostream& out, const Complex& c) {
        out << "(" << c.getReal() << "," << c.getImaginary() << ")";
        return out;
    }
}

【问题讨论】:

  • 您收到的完整错误是什么?将它们复制/粘贴到问题中。
  • 不要在包含在多个翻译单元中的头文件中定义非内联和非模板函数。此类函数将在每个翻译单元中定义,标头包含保护不会保护您免受此影响。
  • 请在您的问题中包含 fullcomplete 构建输出。它通常应该包括第二个定义所在的目标文件的名称。
  • 我刚刚在 Visual Studio 2019 中自己测试了它。一个 cpp 文件包括一个带有函数的头文件。函数没有标记为内联,也没有包含警卫。工作正常。我知道您已经多次说过标题不会被多次包含,但所有证据都指向这一点。无论标题是否在“标题文件”或“源文件”过滤器组的解决方案中,结果都相同。尝试在项目设置中的 C++/命令行选项中添加/showIncludes
  • @RetiredNinja 正如你所建议的,添加了 /showIncludes 并且日志文件在 main.cpp 的包含下显示了一次 OverloadingPlus.h。将 OverloadingPlus 标记为内联和未将其标记为内联。我仍然看不到它可以在哪里被包含两次,但让我们假设它确实如此,为什么 operator

标签: c++ visual-studio-2019 one-definition-rule


【解决方案1】:

这个问题似乎与 VS2019 有关。将文件重命名为不同的名称、重建项目并将文件重命名为其原始名称可解决此问题。

虽然像其他人建议的那样使用inline 确实规避了这个问题,但它并不能解决这个特定问题,因为冲突文件没有被多次包含。

【讨论】:

  • 是的,如果您重命名某些内容或遇到其他奇怪的问题,请始终构建->清理。我通常使用 Build->Batch Build 并一次清理所有目标。很高兴你发现了,这很奇怪。
  • @RetiredNinja 确实很奇怪。感谢您的帮助
【解决方案2】:

您忘记“内联”您的 OverloadingPlus

inline void OverloadingPlus() {
    Complex c1(1, 4);
    Complex c2(3, 2);

    std::cout << c1 + c2 << std::endl;
}

应该使链接器错误消失。

可能发生的情况是:您在多个编译单元中包含“OverloadingPlus.h”(尽管您未能在问题中提供所有#include 实例)。

每次在您的一个 .cpp 文件中包含“OverloadingPlus.h”时,您添加另一个定义 void OverloadingPlus 到您的程序。链接器检测到这一点,无法决定“正确的”,因此给出了错误。

【讨论】:

  • 这确实解决了问题。但是,问题中提供的代码是产生此错误的实际代码,并且肯定没有其他包含引用此文件。但是,假设我的项目中有多个 #include "OverloadingPlus.h" 语句,#ifndef 守卫不应该防止任何重复声明吗?
  • @Lera 仅在一个 .cpp 文件中。如果你有多个实现文件,每个都会定义它
  • 如果这解决了您的问题,那么您肯定有多个包含。
  • 而 ifdef 守卫不会阻止这一点。这不可以。想一想:每个 .cpp 文件都是单独编译的。每个 .cpp 生成它自己的 .obj 文件。 ifdef 保护仅防止重复包含每个 cpp 文件。 IOW:如果这个 cpp 文件多次包含 .h 文件(直接或间接通过其他 .h 文件),则 .h 文件仅被处理一次。但是一旦编译了 .cpp,所有 ifdef 和其他宏都没有实际意义。它们不会延续到下一个 .cpp。
  • 因此,如果 file_one.cpp 包含它并且 file_two.cpp 也包含它,那么您最终会得到两个 .obj 文件,其中已生成函数。链接器抱怨。您可能想阅读“编译单元”并进一步了解编译器生成 .obj 文件时会发生什么。也许尝试使用 cl.exe 手动编译文件
猜你喜欢
  • 1970-01-01
  • 2014-10-14
  • 2019-08-12
  • 2021-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多