前向声明原因"one or more multiplied defined symbol found"?

Forward declaration cause "one or more multiplied defined symbol found"?

我使用的是前向声明,我已经注意不要在头文件中有任何定义(只有声明),并且在每个头文件之前都有#pragma once指令。然而,不知何故,多重定义错误仍在发生。所以在 GlobalSys.h 中,我使用前向声明,然后我将把这个文件包含到任何需要访问这个全局变量的文件中。在 application.h 中,我初始化了这个全局变量,所以我必须包含 EventManager.h 否则编译器会报错。我哪里做错了?

GlobalSys.h

#pragma once
class EventManager;

namespace GlobalSys {
    EventManager * eventManager;
}

Application.h

#include "GlobalSys.h"
#include "../Event/EventManager.h" 

class Application {

public:
    Application();
};

Application.cpp

#include "Application.h"

Application::Application() {
    GlobalSys::eventManager = new EventManager();
}

and I have already been careful not to have any definition inside header files (only declaration)

不,你 defined GlobalSys::eventManagerGlobalSys.h.

Definitions are declarations that fully define the entity introduced by the declaration. Every declaration is a definition, except for the following:

  • Any declaration with an extern storage class specifier or with a language linkage specifier (such as extern "C") without an initializer

您可以使用 extern.

将其更改为声明

GlobalSys.h

#pragma once
class EventManager;

namespace GlobalSys {
    extern EventManager * eventManager; // declaration
}

然后在另一个实现文件中定义它,例如

GlobalSys.cpp

#include "GlobalSys.h"

namespace GlobalSys {
    EventManager * eventManager; // definition
}

once(以及其他 include guards) won't prevent multiple definitions. once prevents a header from being included twice by a single cpp file (translation unit)。如果多个翻译单元包含相同的 header,它们都将拥有该 header 中所有内容的副本。这包括定义的变量和函数。所有这些翻译单元都是单独编译的,所以编译器的任何一个 运行 都不知道另一个 运行 已经包含了 header 并生成了一个 object 文件,其中有它的自己的相同内容副本。

但是,链接器必须将这些多个翻译单元放入一个程序中。它找到了所有的重复项。与其试图梳理程序员真正想要的是什么,不如放弃,请程序员澄清。

Songyuanyao的回答为这个问题提供了一种解决方法:用extern声明变量而不是在header中定义变量,然后在单个翻译单元中定义变量。这允许多个翻译单元共享一个变量。您可以使用 inline 关键字对函数定义执行相同的操作。

有时,但这次不会,您希望每个翻译单元都有自己的变量。那样的话

#pragma once
class EventManager;

namespace GlobalSys {
    namespace {
        EventManager * eventManager;
    }
}

匿名命名空间将 eventManager 的链接限制为单个翻译单元,因此每个翻译单元的 eventManager 不会冲突。