配置结构的常量正确性



我有一个配置文件,在我的程序运行时开始时被读入、解析并放入结构中。

我遇到的问题是我希望这些结构是恒定的,因为它们中的值在程序生命周期内不应该改变。

目前我正在做以下工作:

配置.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作为键,你不再需要MapStrCompstd::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++,因此可能存在一些语法错误:)

最新更新