MFC 项目导致 "multiple definition" 链接器错误?

MFC projects cause "multiple definition" linker errors?

MFC 项目处理包含的方式有什么特别之处吗?

场景如下。我喜欢在 h 文件中定义我的 class 成员函数,而不是在两个文件之间拆分 class。

在 Visual Studio 中,我可以创建一个空的 Win32 项目并执行如下操作:

main.cpp:

#include "doubleDef.h"

int main()
{
    doubleDef t;
    t.func();
    return 0;
}

doubleDef.h:

#pragma once

class doubleDef
{
public:
   int func();
};

int doubleDef::func()
{
    return 4;
}

这构建得很好。

如果我将 doubleDef.h 放入 MFC 对话框项目中,并将 #include "doubleDef.h" 添加到主对话框的 h 文件中,我得到 LNK2005,表示 func已经定义,看起来好像 #pragma once 被忽略了。

如果我改为在主对话框的 cpp 文件中包含 doubleDef.h,一切都很好。但是在空的 Win32 中,我可以通过这样做来包含 doubleDef.h "multiple times":

Header.h

#pragma once
#include "doubleDef.h"

Header1.h

#pragma once
#include "doubleDef.h"

main.cpp:

#include "Header.h"
#include "Header1.h"

int main()
{
    doubleDef t;
    t.func();
    return 0;
}

也就是说,#pragma once 似乎按预期工作(防止 doubleDef::func() 的多重定义)。

如果我将 doubleDef 变成模板 class,那么函数定义 必须 h 文件中。同样,我可以通过添加关键字或通过在 class 中的声明旁边隐式定义它来使 func inline(如 int func() {return 4;}),然后,同样,定义 必须 h 文件中。

根据 documentation,编译器或多或少将 inline 视为可选的,所以如果我只想将所有内容保留在 h 文件中,我可以只是让一切 inline.

什么给了?

#pragma once 表示文件将只被包含 每个源文件一次 。如果你有很多包含它的源文件,你仍然会在每个源文件中得到一个副本。

通过声明函数 inline,您告诉编译器可以有多个副本 - 只要这些副本相同即可。

通常的工作方式是在头文件中有声明,在另一个源文件中有定义(实现) .

P.S。 MFC与你的问题无关。

之前已经回答过,这里更详细的解释一下:

您不能多次定义一个函数,除非它是 inline

您不能在同一个文件中多次声明一个函数。

如果函数在多个 .cpp 文件中被引用,您确实需要多次声明函数和 类。

#pragma once 防止在同一文件中进行多次声明。它不会阻止在不同文件中进行多次声明。多重声明正是您想要的,这也是您首先将文件包含在多个 .cpp 文件中的原因。

class N1 {
public:
    //Declaration 
    //Multiple .cpp files need to know about this class and its members
    int foo();
};

//Definition 
//should be done once. This should go to .cpp file
int N1::foo() {
    return 1;
}

在上面的示例中,编译将在多个 .cpp 文件中正常工作。编译例程不会注意到多个定义。但是链接器注意到多个定义并抱怨。您必须将定义移动到 .cpp 文件或使用 inline 函数。

class N2 
{
public:
    int foo(); 
};

inline int N2::foo() 
{ //valid, even if it is repeated in different files
    return 1;
}

class N3 
{
public:
    int foo() //always valid
    { 
        return 1;
    }
};

在您的简单 Win32 项目中,您有一个主文件,其中包含相同的项目,基本上是一个 no-op。在同一个文件中引用多个相同的 include 不会创建新的 link.

然而,对于 MFC 项目,您将 header 文件放入 mainfrm.h。该文件包含在该项目的其他几个文件中,而不仅仅是 mainfrm.cpp。本质上是为包含主要 header 的每个其他文件创建一个新的 link。

1>MainFrm.obj : error LNK2005: "public: int __thiscall doubleDef::func(void)" (?func@doubleDef@@QAEHXZ) already defined in MfcTest.obj 1>FileView.obj : error LNK2005: "public: int __thiscall doubleDef::func(void)" (?func@doubleDef@@QAEHXZ) already defined in MfcTest.obj 1>ClassView.obj : error LNK2005: "public: int __thiscall doubleDef::func(void)" (?func@doubleDef@@QAEHXZ) already defined in MfcTest.obj 1>OutputWnd.obj : error LNK2005: "public: int __thiscall doubleDef::func(void)" (?func@doubleDef@@QAEHXZ) already defined in MfcTest.obj 1>PropertiesWnd.obj : error LNK2005: "public: int __thiscall doubleDef::func(void)" (?func@doubleDef@@QAEHXZ) already defined in MfcTest.obj

看看那个输出。您可以看到其他每个认为它具有 object.

的组件

换句话说,您最初关于为什么它适用于一种方式而不适用于另一种方式的陈述是因为第二个示例 (MFC) 的复杂性实际上包括整个地方的 header。如果你只想让主窗体使用它,那么将它包含在它的 cpp 中。