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

时间:2014-07-07 22:27:33

标签: c++ map global-variables

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

最好的方法是什么?

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

然后在main()中调用GlobalData :: buildMap()并稍后使用地图GlobalData :: someMap

或者按照以下方式执行:

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

void buildMap()

然后在main()中调用buildMap()并调用getMap()以稍后获取地图

5 个答案:

答案 0 :(得分:1)

了解Singleton。根据你的帖子,我认为这可能是一个很好的解决方案。

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;
}

答案 1 :(得分:1)

只需使用静态构造函数。

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

main() { ... }

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

答案 2 :(得分:0)

您可以定义一个名为ApplicationContext的类。本课程的目的是初始化并保持“全球化”。您的应用程序所需的数据。您可以将您从文件中读取的Map放在此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;
};

您可能希望为ApplicationContext课程添加更多参数,但您可以获得一般性的想法。如果每个对象的构造都接受上下文的实例,那么您不必在应用程序中使用单例,并且应用程序的初始化位于ApplicationContext内。

答案 3 :(得分:0)

您的第一个选项中的

GlobalData也可能是命名空间,因为它的实例毫无意义。

您的选择在实践中彼此并无太大差异。第一个在main之前构造地图。第二个是在第一次调用getMap时构建地图。在任何一种情况下,地图都将填充在main中。如果这样做,那么地图将无法用于在静态初始化期间运行的任何代码(在其他翻译单元中静态对象的去初始化期间它也无法使用)。另一个缺点是你暴露了对地图的非常量访问,如果你想在开始时初始化地图而只是稍后阅读它,这可能是不可取的。

通过在静态初始化期间填充地图可以改善这种情况。 Vite Falcons&#39;答案通过在静态对象的构造函数中调用buildMap(重命名为loadMapFromFile)来完成此操作。这仍然不够。这是因为地图(或者更确切地说,拥有地图的context)可能会或者可能不会在依赖它的静态对象之前进行初始化。

通过使用构造首次使用的习惯用法可以改善这个答案,但是如果你喜欢简单而不是热心的封装,这里有一个例子,对你的第二个选项进行了微小的改动,它们都允许地图由静态对象使用,并允许对map的引用为const,这是所提到的封装中最重要的部分。

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;
}

如果您不想在静态(de)初始化期间使用地图,并且您确实想在初始加载后修改地图,那么您的任何一个选项都可以。请记住,当某人在某个时刻添加一个依赖于地图的静态对象(进入另一个翻译单元)时,程序可能会中断(或者更糟糕的是:它可能不会!)。

答案 4 :(得分:0)

我的设计模式非常适合您的要求,我已多次使用它。

请阅读代码。

简要说明:有一个GlobalData类,它继承自GlobalDataInfoIface和GlobalDataPopulateIface。

用户无法直接访问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();

在这个架构中,我们已经定义了架构本身中全局数据的实现者和用户的边界。因此,有助于引导全球数据的正确使用。

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

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