错误LNK2005:已定义 - C ++

时间:2009-03-07 18:05:48

标签: c++ linker

背景

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

  1. Person.h
  2. Person.cpp
  3. 该库生成一个静态库文件。另一个项目是 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 时,我得到错误LNK2005:已定义。以下是我如何使用它

    #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. static 在这里有什么区别?
    3. 这个问题的最佳解决方案是什么?
    4. 由于

7 个答案:

答案 0 :(得分:22)

你要么

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

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

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

答案 1 :(得分:3)

编译库时,其lib文件包含SetPersonName的定义。当您编译使用该库的程序时,由于它包含标题,并且您已在头文件中内联编写代码,因此它也会编译为SetPersonName的定义。不(通常)允许使用相同功能的两个定义。 static关键字告诉编译器该函数不应该暴露在当前转换单元之外(您正在编译的离散代码段),因此链接器看不到库中的定义。

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

然而,如果失败了,我会像你为其余的功能,标题中的声明和.cpp中的实现那样实现它。与库相关联的内联函数往往会降低使用库的许多优点。

答案 2 :(得分:1)

  1. 函数SetPersonName将被编译到包含Person.h文件的每个目标文件中,从而使链接器看到多个函数并给出错误。

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

  3. 尝试在函数之前写inline,如

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

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

答案 3 :(得分:1)

通过声明函数static,您将其范围限定为当前的转换单元,因此实际上您已在主文件中添加了一个新的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 :(得分:0)

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

答案 5 :(得分:0)

我遇到了类似上面@ Logan-capaldo所描述的情况。

CPP源文件(myfile.cpp)包含一个函数MyFunction。在构建时,这被编译成myfile.obj。但是主CPP文件(main.cpp)也包含了myfile.cpp,因此函数MyFunction被包含/编译/链接两次,导致已经定义了&#34; LNK2005&#34;错误。

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

答案 6 :(得分:0)

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

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

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;