我有一个配置文件,在我的程序运行时开始时被读入、解析并放入结构中。
我遇到的问题是我希望这些结构是恒定的,因为它们中的值在程序生命周期内不应该改变。
目前我正在做以下工作:
配置.h
#pragma warning(push)
#pragma warning(disable: 4510) /*-- we don't want a default constructor --*/
#pragma warning(disable: 4610) /*-- we don't want this to ever be user instantiated --*/
typedef struct SerialNode {
private:
void operator=(SerialNode&);
public:
const char* const port;
const char* const format;
} SerialNode;
#pragma warning(pop)
typedef std::map<const char*, const SerialNode*, MapStrComp> SerialMap;
SerialMap SerialConfig;
配置.cpp
/*-- so we don't fall out of scope --*/
SerialNode* global_sn;
SerialNode local_sn = {port, format};
global_sn = new SerialNode(local_sn);
SerialConfig[key_store] = global_sn;
这工作正常。但是我的问题是,现在我正在处理更复杂的配置数据,这需要我从列表中拉回结构,对其进行修改,然后将其放回原处。
显然我无法修改它,因此解决方案如下所示:
SerialNode* global_sn;
SerialNode* old_sn = SerialConfig[key_store];
SerialNode local_sn = {port, format, old_sn->old_data, old_sn->more_old_data};
global_sn = new SerialNode(local_sn);
SerialConfig[key_store] = global_sn;
delete old_sn;
但这在我看来是糟糕的编程实践。有没有更好的方法来实现我想要的不需要这种黑客外观的解决方案?
作为参考,我正在使用Visual Studio 2010
与往常一样,你能做的最好的事情就是不要重新实现已经编写的东西。有大量的库和框架可以帮助 c++ 的序列化:
- 提升序列化
- Qt
- 协议缓冲区
- 味精包
- 卡彭普罗托
理想情况下,您选择的序列化框架将完全重新创建您尝试存储的数据图。无论您是否进行了任何修正,您的目标都可能是仅提供对全局配置数据的 const 访问。只需确保突变器(包括非常量指针)不会通过头文件公开即可。
简单的答案是Thomas的建议,但做得正确(即不会导致未定义的行为):
创建一个可变的配置对象,但通过常量引用将其传递给其余组件。当您创建(以及维护)真实对象时,您可以更改它,但应用程序的其余部分将无法修改配置。我过去使用的一个常见模式是:
class SomeObject {
Configuration const & config;
public:
SomeObject(Configuration const & config) : config(config) {}
void f() {
if (config.someParam()) { ...
// ...
void loadConfiguration(Config & config) { ... }
int main() {
Configuration config;
loadConfiguration(config); // config is a non-const &, can modify
SomeObject object(config); // object holds a const&, can only read
object.f();
// ...
这不是对你的问题的答案,只是对你的代码的一些观察。
-
你不需要
typedef struct SerialNode { ... } SerialNode;
,这是一个c成语。在 c++ 中,您只需编写struct SerialNode { ... };
并使用SerialNode
作为类型名称。 -
如果要阻止默认构造函数,请将其设为私有,就像使用赋值运算符一样
class SerialNode { private: SerialNode(); SerialNode &operator=(SerialNode&); ... };
-
不要使用
char*
成员,而是使用std::string
。C++字符串比普通char
指针和关联的堆分配更容易、更安全。 -
map
键也是如此;如果你使用std::string
作为键,你不再需要MapStrComp
,std::string
因为它已经提供了适当的比较。
可能更好的是将整个事情包装在一个单例类中:
class Config {
public:
static Config const& get() { return *config; }
static void load();
SerialNode const* operator[](const char*);
private:
static Config* config;
SerialMap map;
};
void Config::load() {
config = new Config();
// put things into it
}
免责声明:未测试,并且有一段时间没有使用C++,因此可能存在一些语法错误:)