【问题标题】:LNK1169 one or more multiply defined symbols found And LNK2005LNK1169 找到一个或多个多重定义符号 和 LNK2005
【发布时间】:2018-10-15 14:21:22
【问题描述】:

我在尝试编译我的代码时遇到了这个问题 我认为这可能是由包含彼此的头文件引起的。但据我所知,我的头文件没有发现任何问题

错误 LNK1169 一个或多个多重定义符号 找到 Homework2 D:\05Development\04 C_C++\C\DS Alg 类\Homework2\Debug\Homework2.exe 1

另外,还有一个错误告诉我函数 Assert() 已在别处声明。

错误 LNK2005 "void __cdecl Assert(bool,class std::basic_string,类 标准::分配器 >)" (?Assert@@YAX_NV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) 已在 DataBase.obj Homework2 D:\05Development\04 中定义 C_C++\C\DS算法类\Homework2\Homework2\dbTest.obj 1

这是我的代码结构:

功能

void Assert(bool val, string s)
{
    if (!val)
    {
        cout << "Assertion Failed!!: " << s << endl;
        exit(-1);
    }
}

在常量.h中

一个虚拟类 List 包括 Constants.h

#pragma once // List.h
#include "Constants.h"

数组列表包含List类,在AList类中调用Assert函数

#pragma once //AList.h
#include "List.h"
...
Assert((pos >= 0) && (pos < listSize), "Position out of range");

在DataBase 类中我创建了一个AList 成员

private:
    AList<CData> set;

标题如下所示: #pragma 一次 #include "AList.h" #include "CData.h"

CData.h 看起来像这样:

#pragma once
class CData
{
private:
    std::string m_name;

    int m_x;
    int m_y;

public:
    CData(std::string str = "null", int x = 0, int y = 0) : m_name(str), m_x(x), m_y(y) {}

    // Helper functions
    const std::string& GetName() const { return this->m_name; }
    const int& GetX() const { return this->m_x; }
    const int& GetY() const { return this->m_y; }
};

【问题讨论】:

  • 不要将代码放在头文件中,除非它是类定义中的内联代码或模板化代码。仅在 .cpp 文件中。这里Assert函数实现不应该在Constants.h,只有声明应该在那里(void Assert(bool val, string s);

标签: c++ class header linker


【解决方案1】:

当您构建项目时,每个 .cpp 文件都会被分别编译成不同的目标文件。 #pragma once 中的once 仅适用于单个.cpp 文件的编译,不适用于整个项目。因此,如果一个.cpp文件包括头A和头B,并且头B也包括头A,那么头A的第二个包含将被跳过。

但是,如果您有另一个包含 A 的 .cpp 文件,则 A 将再次包含在该目标文件中 - 因为 #pragma once 仅在编译单个 .cpp 文件时有效。

#include 语句从字面上获取包含文件的内容并将其“粘贴”到包含它的文件中。您可以通过查看 C 预处理器工具(gcc 工具链中的cpp)的输出来尝试此操作。如果您使用的是 gcc 工具链,您可以尝试这样的方式在应用包含文件后查看文件:

cpp file.cpp -o file_with_includes.cpp

如果您的标头中有一个函数,例如您的示例中的 Assert,该函数将被复制到您包含它的每个 .cpp 文件中。

如果您有 A.cpp 和 B.cpp,它们都包含您的 Constants.h 文件,则每个目标文件(.o 或 .obj 取决于您的环境)都将包含您的 Assert 函数的副本。当链接器结合目标文件创建二进制文件时,两个目标文件都会声明它们提供了 Assert 的定义,链接器会抱怨,因为它不知道使用哪一个。

这里的解决方案是内联您的 Assert 函数,如下所示:

inline void Assert(bool val, string s)
{
    if (!val)
    {
        cout << "Assertion Failed!!: " << s << endl;
        exit(-1);
    }
}

或者在它自己的.cpp文件中提供它的主体,只在头文件中留下函数原型。

常量.h:

void Assert(bool val, string s);

常量.cpp:

void Assert(bool val, string s)
{
    if (!val)
    {
        cout << "Assertion Failed!!: " << s << endl;
        exit(-1);
    }
}

请注意,标准库还提供assert(),它也很好用。 (见https://en.cppreference.com/w/cpp/error/assert)。

#include <cassert>
...
assert(is_my_condition_true());
assert(my_variable > 23);
// etc..

请记住,cassert 中声明的断言仅在为 Debug 编译时有效,并在为 Release 构建时被编译出来(以加快执行速度),所以不要在 assert 中放入任何有副作用的代码.

#include <cassert>
...
// Don't call functions with side effects.
// Thus function decreases a "count" and returns the new value
// In Release builds, this line will disappear and the decrement
// won't occur.
assert(myclass.decrement_count() > 0);

【讨论】:

  • 我建议你明确展示如何inline它。
  • @CharlieLee 它解决了你的问题 => 你应该 accept the answer
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-22
  • 1970-01-01
相关资源
最近更新 更多