【问题标题】:how to declare and define global variables in order to access them from all headers/source files properly如何声明和定义全局变量以便从所有头文件/源文件中正确访问它们
【发布时间】:2021-08-09 23:42:36
【问题描述】:

嗯,我正在学习 C++,但从未真正学会如何做非面向对象的事情。 我正在尝试获得更多 C 风格的编码经验。

GobalInformation.h

#pragma once

#ifndef GLOBALINFORMATION_H
#define GLOBALINFORMATION_H

#include "MapInformation.h"

namespace gi {
    MapInformation mapInf;
};

#endif

我希望能够从我项目中的每个标头和 cpp 访问 gi::mapInf。现在我在每个头文件中都包含 globalinformation.h,所以我遇到了多个定义的链接器错误。

我该如何解决这个问题?

【问题讨论】:

  • 你现在可能认为你想要这个全局变量,但我保证你会后悔的。
  • @BjörnPollex 请详细说明
  • OT:为什么要使用#pragma once 标头保护(顺便说一句,没有唯一标识符 => 危险)?
  • @Andre 嗯,我在 wikipedia 上读过它可能会很好:en.wikipedia.org/wiki/Pragma_once
  • @xcrypt:使用标头保护时,您应该附加一个唯一部分以避免名称冲突。您可以像这样添加 GUID:#ifndef GLOBALINFORMATION_H_8FF7B23E1B4D11E18852C5D64824019B#define GLOBALINFORMATION_H_8FF7B23E1B4D11E18852C5D64824019B。如果您不需要可移植性,我建议您仅在编译器支持的情况下使用#pragma once

标签: c++ c global-variables


【解决方案1】:

只在头文件中做

namespace gi {
    extern MapInformation mapInf;
};

在 CPP 文件中提供实际定义。

namespace gi {
    MapInformation mapInf;
};

它将按您的意愿工作。

如果您跨动态链接库边界使用 MapInformation,您可能必须链接到包含定义 cpp 文件的库。同样在 Window 上,您可能必须使用 dllimport/dllexport

【讨论】:

  • 请注意,定义必须在恰好一个翻译单元中。
  • 在 globalinformation.cpp 我应该做“MapInformation mapInf;”?
  • @xcrypt .. 我已经更新了我的答案以反映在 CPP 文件中要做什么
  • 这是什么意思:“如果您跨动态链接库边界使用 MapInformation,您可能必须链接到包含定义 cpp 文件的库。”你能解释更多吗?
【解决方案2】:

请注意,在多个编译单元中使用全局变量很容易导致初始化顺序问题。您可能希望考虑用返回引用的函数替换每个全局变量。在您的情况下,将其放在一个 cpp 文件中并在标题中声明:

namespace gi {
    MapInformation& getMapInf()
    {
        static MapInformation result;
        return result;
    }
}

【讨论】:

  • 您能否在您的回答中解释这个初始化顺序问题的原因是什么?
  • @xcrypt:全局对象的初始化顺序是未指定的(跨 TU),所以如果你有一个需要另一个全局对象的全局对象,你不能确定它们会被初始化以正确的顺序。拥有一个本地静态对象(本质上也是全局的,只是可见性受限)可以避免这种情况,因为您可以保证在 getMapInf() 返回时对象已经构建完毕。
  • 我想这个函数应该被内联或声明为extern?好吧,无论如何我不得不这样做
  • 好建议,但我还有一个问题:如何在您的情况下设置“全局”变量?如何编写 set-Method?
  • @xMutzelx,您可以使用 getMapInf() 返回的引用来设置或获取全局的值。例如。 gi::地图信息 otherMapInf(); getMapInf() = otherMapInf();
【解决方案3】:

全局变量是 C,但命名空间是 C++。关于使用全局变量以及如何用单例模式替换它们有一个很好的讨论:Globals and Singletons

这是一个简单的示例:CPP/Classes/Singleton

【讨论】:

    【解决方案4】:

    也许更好的解决方案是创建一个包含所有全局数据的全局对象。然后将智能指针传递给实际需要访问此共享全局数据的类。 示例:

    class GlobalData
    {
    public:
        int ticks_;
    };   
    
    
    //Other file
    class ThatNeedsGlobalData
    {
    public:
    ThatNeedsGlobalData(std::shared_ptr<GlobalData> globalData);
    };
    

    这将为您省去一些麻烦。

    祝你好运!

    【讨论】:

    • 这也是一个可能的解决方案,所以我会投赞成票。但我希望它作为全局的原因之一是防止每个类都引用信息(节省内存)。最后可能没什么关系,但是嘿,我认为这很酷:p
    • 那么你想在每个需要访问全局数据的类中保存一个指针/智能指针的大小吗?好的。只需尝试一下,看看您是否认为值得。您以后可以随时尝试其他解决方案。
    【解决方案5】:

    以下是您在尝试以以往的方式使用全局变量时需要注意的一些事项。

    1. 确保 GobalInformation.h 包含的头文件包含的所有头文件也包含在 #ifndefs 内。 (我看不到 mapinformation.h 所以我假设你已经完成了)

    2. 就像 CPP 一样,C 编译器也不保证变量在不同翻译单元(不同的 C/CPP 文件)中的初始化顺序。 因此将头文件声明为

      //GlobalInformation.h
      
      namespace gi {
          extern MapInformation mapInf;
      };
      
    3. 在您知道将被调用的函数中,首先初始化变量。这种方式也可以在 C 中实现延迟初始化。

    【讨论】:

    • 我不确定您所说的 [2] 是什么意思,请详细说明?似乎很重要
    猜你喜欢
    • 2018-04-04
    • 2021-12-28
    • 2021-05-29
    • 1970-01-01
    • 2020-11-09
    • 1970-01-01
    • 2016-11-07
    • 1970-01-01
    相关资源
    最近更新 更多