在c++中定义全局数据的最佳方式是什么?
What it the best way to define global data in c++?
我有一个地图,我想建立(从文件在运行时读取),当应用程序启动,然后由多个类/函数使用。
最好的方法是什么?
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()来获取地图
阅读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;
}
直接使用静态构造函数。
Struct GlobalData {
static map<int,int> aMap;
static void buildMap() { ... } //fill in the map
GlobalData() { buildMap(); }
}
GlobalData TheGlobalData;
main() { ... }
为了保证正确的初始化,静态构造函数必须与可能使用它的代码在同一个翻译单元中,例如main()。
您可以定义一个名为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
中。
我有一个设计模式,非常适合您的要求,我已经用过好几次了。
请阅读代码
简要视图:有一个GlobalData类,它继承了GlobalDataInfoIface和globaldatappopulateiface。
用户不能直接访问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();
在这个体系结构中,我们在体系结构本身中定义了全局数据的实现者和用户的边界。因此,有助于引导正确使用全局数据。
全局数据的用户不能修改全局数据。
请让我知道,如果你不明白的代码。
第一个选项中的GlobalData
也可以是一个名称空间,因为它的实例是没有意义的。
getMap
时构造映射。在任何一种情况下,映射都将在main中填充。如果这样做,那么在静态初始化期间运行的任何代码都将无法使用该映射(在其他转换单元中对静态对象进行反初始化期间也无法使用该映射)。另一个缺点是暴露了对map的非const访问,如果您希望在开始时初始化map,然后再读取它,这可能是不可取的。
可以通过在静态初始化期间填充映射来改善这种情况。Vite Falcons的解决方法是在静态对象的构造函数中调用buildMap
(重命名为loadMapFromFile
)。这还不够。这是因为map(或者更确切地说,拥有map的context
)可能会在依赖它的静态对象之前初始化,也可能不会。
这个答案可以通过使用第一次使用时构造(construct-on-first-use)的习惯用法得到改进,但是如果您更喜欢简单而不是过度封装,这里有一个对第二个选项进行了最小更改的示例,它既允许静态对象使用映射,又允许对映射的引用为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)初始化期间使用映射,并且您确实希望在初始加载后修改映射,那么您的两个选项都可以。请记住,当某人在某个时刻添加依赖于映射的静态对象(到另一个翻译单元)时,程序可能会中断(甚至更糟:它可能不会!)。
- 在c代码之间共享数据的最佳方式
- 使用QQuickFramebufferObject时同步数据的最佳方式是什么
- 从嵌套在std::映射中的std::列表中删除元素的最佳方式
- 如果条件为TRUE(最佳方式?),则在do while循环中后置增量
- 在reactor中存储eventHandlers的最佳方式是什么
- 在AVX通道中混洗的最佳方式
- 从 T 创建 std::future 的最佳方式<T>
- C++:使用 std::unique_ptr 访问重载运算符++的最佳方式?
- 对列表列表中的元素进行分组的最佳方式
- 利用 GPU 的最佳方式
- 使用 QT C++过滤大数据的最佳方式
- 算法设计:用边界数字表示 2D 网格的最佳方式,以C++?
- 在C++中共享键值对的最佳方式
- 为Catch2中的外部文本文件指定路径的最佳方式
- 代表Quarto棋盘游戏棋子的最佳方式
- 等待线程的最佳方式是什么
- 将uint8_t*buffer和size_tbufferlen从C++传递到C中的API函数的最佳方式是什么
- 创建控制台菜单C++的最佳方式
- 只显示片段着色器的最佳方式是什么
- 复制文件的最佳方式是什么,以便我可以在复制过程中轻松取消复制?