C++ 在c+中定义全局数据的最佳方式是什么+;?

C++ 在c+中定义全局数据的最佳方式是什么+;?,c++,map,global-variables,C++,Map,Global Variables,我有一个映射,我想在应用程序启动时构建(在运行时从文件中读取),然后由多个类/函数使用 最好的方法是什么 Struct GlobalData { static map<int,int> aMap; static void buildMap(); //fill in the map } Struct GlobalData { 静态地图; 静态void buildMap();//填充映射 } 然后在main()中调用GlobalData::buildMap(),稍后使

我有一个映射,我想在应用程序启动时构建(在运行时从文件中读取),然后由多个类/函数使用

最好的方法是什么

Struct GlobalData
{
    static map<int,int> aMap;
    static void buildMap(); //fill in the map
}
Struct GlobalData
{
静态地图;
静态void buildMap();//填充映射
}
然后在main()中调用GlobalData::buildMap(),稍后使用映射GlobalData::someMap

或按以下方式操作:

map<int,int>& getMap()
{
    static map<int,int> aMap;
    return aMap
}

void buildMap()
map&getMap()
{
静态地图;
返回aMap
}
void buildMap()

然后在main()中调用buildMap()并在以后调用getMap()以获取映射

您可以定义一个名为
ApplicationContext
的类。此类的目的是初始化和保存应用程序所需的“全局”数据。您可以将从文件中读取的
映射
放在此
ApplicationContext
实例中,并允许其他类接受
ApplicationContext
的实例并使用其中的映射

例如:

// Part of context's construction would be to read the map
ApplicationContext context;
//... After a while
useMap(context);
ApplicationContext
类看起来像:

class ApplicationContext {
public:
    ApplicationContext() {
        // Some initial stuff before reading map from file
        loadMapFromFile();
        // Some global stuff to load after
    }

    const std::map<int, int>& getMap() const {
        return aMap;
    }
private:
    void loadMapFromFile() {
        // Code to read your 'global' map from file.
    }

    std::map<int, int> aMap;
};
class ApplicationContext{
公众:
ApplicationContext(){
//从文件中读取地图之前的一些初始内容
loadMapFromFile();
//之后要加载的一些全局内容
}
常量std::map&getMap()常量{
返回aMap;
}
私人:
void loadMapFromFile(){
//从文件中读取“全局”地图的代码。
}
映射aMap;
};
您可能希望向
ApplicationContext
类添加更多参数,但您已经了解了一般的想法。如果每个对象的构造都接受一个上下文实例,那么您不必在应用程序中使用单例,而且应用程序的初始化就在您的
ApplicationContext
阅读内容中。根据你的帖子,我认为这可能是一个很好的解决方案

class GlobalData
{
public:
    static GlobalData* getInstance()
    {
        if (nullptr == sm_Instance) { sm_Instance = new GlobalData(); }

        return sm_Instance;
    }

    map<int, int> getSomeMap() { return m_SomeData; }

private:
    static GlobalData* sm_Instance;
    map<int, int> m_SomeData;

    GlobalData() { buildMap(); }

    void buildMap() { /* build map */ }
};

GlobalData* GlobalData::sm_Instance = nullptr;

int main()
{
    map<int, int> someMap = GlobalData::getInstance()->getSomeMap();

    return 0;
}
类全局数据
{
公众:
静态GlobalData*getInstance()
{
如果(nullptr==sm_实例){sm_实例=new GlobalData();}
返回sm_实例;
}
map getSomeMap(){return m_SomeData;}
私人:
静态全局数据*sm_实例;
映射一些数据;
GlobalData(){buildMap();}
void buildMap(){/*build map*/}
};
GlobalData*GlobalData::sm_Instance=nullptr;
int main()
{
map-someMap=GlobalData::getInstance()->getSomeMap();
返回0;
}

GlobalData
在您的第一个选项中,最好是一个名称空间,因为它的实例是无意义的

在实践中,你们的选择并没有太大的不同。第一个是在main之前构建地图。第二个函数在第一次调用
getMap
时构造映射。在任何一种情况下,地图都将填充到main中。如果这样做,那么映射对于在静态初始化期间运行的任何代码都将不可用(在其他转换单元中的静态对象取消初始化期间也不可用)。另一个缺点是您公开了对映射的非常量访问,如果您希望在启动时初始化映射,并且只在以后读取它,这可能是不可取的

可以通过在静态初始化期间填充映射来改善这种情况。Vite Falcons的回答是在静态对象的构造函数中调用
buildMap
(重命名为
loadMapFromFile
)。这还不够。这是因为映射(或者更确切地说,拥有映射的
context
)可以在依赖它的静态对象之前初始化,也可以不初始化

这个答案可以通过在第一次使用时使用construct来改进,但是如果您更喜欢简单而不是热情的封装,那么这里有一个示例,对您的第二个选项进行了最小的更改,它既允许静态对象使用映射,也允许对映射的引用是常量,这是上述封装中最重要的部分

static map<int,int>* buildMap() {
    auto aMap = new map<int,int>();
    // load the map here
    return aMap;
}

const map<int,int>& getMap() { // use const if you don't need to modify the map
    static map<int,int>* aMap = buildmap();
    return *aMap;
}
静态映射*buildMap(){
自动映射=新映射();
//把地图放在这里
返回aMap;
}
const map&getMap(){//如果不需要修改映射,请使用const
静态映射*aMap=buildmap();
返回*aMap;
}

如果在静态(de)初始化期间不需要使用映射,并且确实希望在初始加载后修改映射,则两个选项都可以。请记住,当有人在某个点上添加一个依赖于映射的静态对象(到另一个翻译单元中)时,程序可能会崩溃(甚至更糟:它可能不会!)。

我有一个非常适合您的要求的设计模式,我已经多次使用过它

请阅读密码

简要视图:有一个GlobalData类继承自GlobalDataInfoIface和GlobalDataPopulateFace

用户无法直接访问GlobalData。根据其用途,它只能通过两个接口访问全局数据

class GlobalDataPopulateIface {
    public:
        virtual void buildMap() = 0;       
        virtual ~GlobalDataPopulateIface() {}
};

class GlobalDataInfoIface {
    public:
        virtual map<int,int>& getMap() = 0;       
        virtual ~GlobalDataInfoIface() {}
};

class GlobalData : public GlobalDataInfoIface, public GlobalDataPopulateIface
{
    public:
        void buildMap();
        map<int,int>& getMap();

    private:

        // All constructors and destructors are made private
        GlobalData ();   
        ~GlobalData ();   
        GlobalData ( const GlobalData& other );   
        const GlobalData& operator = ( const GlobalData &other ); 

        map<int,int> aMap;

};

class GlobalDataImplInfo {
    public:
        CGSimWaveformImplInfo();
        ~CGSimWaveformImplInfo();

        GlobalDataInfoIface* GetGlobalDataInfoIface();
        GlobalDataPopulateIface* GetGlobalDataPopulateIface();
        static void Destroy();

    private:
        static GlobalData* global_data;

        GlobalDataImplInfo(GlobalDataImplInfo const&);
        GlobalDataImplInfo& operator= (GlobalDataImplInfo const&);
};
要使用全局数据的客户端应具有以下代码:

GlobalDataImplInfo global_data;
GlobalDataPopulateIface * global_data_populate = global_data.GetGlobalDataPopulateIface();
GlobalDataImplInfo global_data;
GlobalDataInfoIface * global_data_info = global_data.GetGlobalDataInfoIface();
在这个架构中,我们在架构本身中定义了全局数据的实现者和用户的边界。因此,这有助于引导正确使用全球数据

全局数据的用户无法修改全局数据


如果您不理解代码,请告诉我。我们可以讨论一下。

只需使用静态构造函数

Struct GlobalData {
    static map<int,int> aMap;
    static void buildMap() { ... } //fill in the map
    GlobalData() { buildMap(); }
}
GlobalData TheGlobalData;

main() { ... }
Struct GlobalData{
静态地图;
静态void buildMap(){…}//填充映射
GlobalData(){buildMap();}
}
GlobalData the GlobalData;
main(){…}

为了保证正确的初始化,静态构造函数必须与可能使用它的代码位于同一个翻译单元中,例如main()。

启动时“是什么意思?映射内容在编译时已知,还是必须在运行时计算?它将在运行时从文件中读取。看起来您应该检查关键字
extern
的使用。然后可能有一个头/cpp文件组合专用于
全局