【问题标题】:error LNK2005: already defined - C++错误 LNK2005:已定义 - C++
【发布时间】:2010-10-11 22:56:16
【问题描述】:

背景

我有一个名为 PersonLibrary 的项目,它有两个文件。

  1. Person.h
  2. Person.cpp

这个库产生一个静态库文件。另一个项目是 TestProject,它使用 PersonLibrary(通过 VS008 中的项目依赖项添加)。一切正常,直到我向 Person.h 添加了一个非成员函数。 Person.h 看起来像

class Person
{
public:
    void SetName(const std::string name);

private:
    std::string personName_;
};

void SetPersonName(Person& person,const std::string name)
{
    person.SetName(name);
}

Person.cpp 定义了 SetName 函数。当我尝试使用 TestProject 中的 SetPersonName 时,我得到 error LNK2005: already defined。这是我的使用方法

#include "../PersonLibrary/Person.h"
int main(int argc, char* argv[])
{
    Person person;
    SetPersonName(person, "Bill");
    return 0;
}

尝试过的解决方法

1 - 我删除了 Person.cpp 并在 Person.h 中定义了整个类。错误消失了,一切正常。

2 - 将 SetPersonName 修饰符更改为 static。像下面这样

static void SetPersonName(Person& person,const std::string name)
{
    person.SetName(name);
}

问题

  1. 为什么首先显示的代码没有按我的预期工作?
  2. 静态在这里做了什么?
  3. 此问题的适当解决方案是什么?

谢谢

【问题讨论】:

    标签: c++ linker


    【解决方案1】:

    你要么必须

    • SetPersonName 的定义移动到.cpp 文件,编译并链接到生成的目标
    • 使SetPersonName内联

    这是一个众所周知的违反单一定义规则的案例。

    static 关键字使函数的链接成为内部链接,即仅对包含它的翻译单元可用。然而,这隐藏了真正的问题。我建议将函数的定义移到它自己的实现文件中,但将声明保留在标题中。

    【讨论】:

    • 感谢您的回答。我整理好了。
    • @dirkgently... 但 boost 是仅限标头的 IIRC,也可能是其他库。他们如何避免此类问题?
    • 一些可以为他人节省时间的东西:如果你在VS上编译C程序,你应该使用__inline使其内联(否则编译失败)
    • 我也面临着全局变量的问题,与这里的场景完全相同。只需将“SetPersonName() 替换为全局变量。我将如何解决这个问题?
    【解决方案2】:

    当你编译你的库时,它的 lib 文件包含一个 SetPersonName 的定义。当您编译使用该库的程序时,由于它包含标头,并且您已在标头中内联编写代码,因此它还会在 SetPersonName 的定义中进行编译。 (通常)不允许对同一函数进行两个定义。 static 关键字告诉编译器该函数不应暴露在当前翻译单元之外(您正在编译的离散代码段),因此库中的定义对链接器不可见。

    此问题的适当解决方案取决于您的目标。带有静态函数声明的头文件几乎从来都不是您想要的。从设计的角度来看,我建议完全摆脱 SetPersonName,而只使用 Person::SetName。

    但是,如果做不到这一点,我会像您对其余功能、标头中的声明和 .cpp 中的实现所做的那样来实现它。首先,与库相关联的内联函数往往会削弱使用库的许多优势。

    【讨论】:

      【解决方案3】:

      通过将函数声明为静态,您将其范围限定为当前翻译单元,因此实际上您在主文件中添加了一个新的 SetPersonName 函数,并且将调用该函数而不是库中定义的函数。

      正确的解决方案是在person.h中将SetPersonName声明为extern,并在person.cpp中实现

      Person.h

      extern void SetPersonName(Person& person,const std::string name);
      

      Person.cpp

      void SetPersonName(Person& person,const std::string name)
      {
          person.SetName(name);
      }
      

      【讨论】:

        【解决方案4】:
        1. 函数 SetPersonName 将被编译到包含 Person.h 文件的每个对象文件中,从而使链接器看到多个函数并给出错误。

        2. 通过编写静态,您声明该函数仅在单个对象文件中可见。你仍然会在你的二进制文件中得到几个函数,但现在你不会得到错误。

        3. 尽量在函数like前写inline

          inline void SetPersonName(Person& person,const std::string name)
          {
              person.SetName(name);
          }
          

          ...因为函数非常简单,所以我认为可以将它作为内联函数。内联会将必要的代码放置在使用函数的位置,而不会实际创建要调用的函数。

        【讨论】:

          【解决方案5】:

          解决方案是使该函数成为静态方法。这将停止“已定义”错误。

          【讨论】:

            【解决方案6】:

            我遇到了与上面@logan-capaldo 清楚描述的类似情况。

            CPP 源文件 (myfile.cpp) 包含函数 MyFunction。在构建时,它被编译到 myfile.obj 中。但是主 CPP 文件(main.cpp)也包含了 myfile.cpp,所以函数 MyFunction 被包含/编译/链接了两次,导致“LNK2005 已定义”错误。

            这很乱,但我没有时间正确修复它。最快的修复方法(在 VS Express 2012 中)是在解决方案资源管理器中右键单击 myfile.cpp,转到属性并将“从构建中排除”更改为“是”。我想这会阻止创建和/或链接其中一个 OBJ 文件,从而消除错误。

            【讨论】:

              【解决方案7】:

              对于在 Qt 项目中处理此错误的任何人,请确保您的头文件中没有在 signals: 下定义任何非信号函数。

              不正确,在Foo::promiseData() 上抛出LNK2005

              class Foo : public QObject {
                      Q_OBJECT
              
                  public:
                      explicit Foo(QObject* parent = nullptr);
              
                  signals:
                      void dataReady(QList<QObject*> data) const;
              
                      void promiseData() const; // <-- This function is not supposed to be a signal.
              

              正确:

              class Foo : public QObject {
                      Q_OBJECT
              
                  public:
                      explicit Foo(QObject* parent = nullptr);
              
                      void promiseData() const;
              
                  signals:
                      void dataReady(QList<QObject*> data) const;
              

              【讨论】:

                猜你喜欢
                • 2012-04-25
                • 1970-01-01
                • 1970-01-01
                • 2022-01-13
                • 2015-08-10
                • 1970-01-01
                • 2014-12-21
                • 1970-01-01
                相关资源
                最近更新 更多