简体   繁体   English

配置结构的const正确性

[英]const correctness for configuration structures

I have a configuration file which gets read in, parsed and put into structures at the beginning of my programs run time. 我有一个配置文件,该文件在程序运行时开始读取,解析并放入结构中。

The problem I am having is that I want these structures to be constant since the values in them should not change during the programs lifespan. 我遇到的问题是我希望这些结构是恒定的,因为它们的值在程序生命周期中不应更改。

Currently I am doing the following: 目前,我正在执行以下操作:

config.h config.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;

config.cpp config.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;

This works fine. 这很好。 However my problem is that now I am dealing with more complicated configuration data which requires me to pull a structure back out of the list, modify it and then put it back. 但是,我的问题是,现在我要处理更复杂的配置数据,这需要我将结构从列表中拉出,进行修改,然后再放回去。

Obviously I can't modify it, so the solution would be something like: 显然我无法修改它,所以解决方案将是这样的:

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;

But this strikes me as bad programming practice. 但这使我感到不满意,因为这是不好的编程习惯。 Is there is a better way to achieve what I'm going for which doesn't require such a hacked looking solution? 是否有更好的方法来实现我想要的目标,而这些目标并不需要这种骇人听闻的解决方案?

For reference, I'm using Visual Studio 2010 供参考,我使用的是Visual Studio 2010

As always, the best thing you can do is not re-implement something that has already been written. 与往常一样,您可以做的最好的事情就是不要重新实现已经编写的内容。 There are a large number of libraries and frameworks that will help with serialization for c++: 有许多库和框架可帮助实现c ++的序列化:

Ideally the serialization framework you choose will exactly recreate the data graph that you are trying to store. 理想情况下,您选择的序列化框架将完全重新创建您要存储的数据图。 Regardless of whether you have done any fixup, your goal will likely be to only provide const access to the global configuration data. 无论您是否进行了任何修复,您的目标都可能仅是提供对全局配置数据的const访问。 Just make sure that mutators (including non const pointers) are not exposed via a header file. 只要确保不通过头文件公开增变器(包括非const指针)。

This is not an answer to your question, just some observations to your code. 这不是对您问题的答案,只是对代码的一些观察。

  • You don't need the typedef struct SerialNode { ... } SerialNode; 您不需要typedef struct SerialNode { ... } SerialNode; , this is a idiom. ,这是一个成语。 In , you just write struct SerialNode { ... }; ,您只需编写struct SerialNode { ... }; and use SerialNode as a type name. 并使用SerialNode作为类型名称。

  • If you want to prevent a default constructor, make it private as you already do with the assignment operator 如果要防止使用默认构造函数,请像使用赋值运算符一样将其设为私有

     class SerialNode { private: SerialNode(); SerialNode &operator=(SerialNode&); ... }; 
  • Don't use char* members, use std::string instead. 不要使用char*成员,而应使用std::string C++ strings are much easier and safer to use than plain char pointers and the associated heap allocation. 与纯char指针和关联的堆分配相比,C ++字符串更易于使用和安全。

  • Same goes for the map key; map键也是如此; if you use std::string as a key, you don't need MapStrComp anymore, because std::string already provides an appropriate comparison. 如果将std::string用作键,则不再需要MapStrComp ,因为std::string已经提供了适当的比较。

Probably nicer is to wrap the whole thing in a singleton class: 可能更好的方法是将整个内容包装在一个单例类中:

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
}

Disclaimer: not tested, and haven't used C++ in a while, so there might be some syntax errors :) 免责声明:未经测试,并且有一段时间未使用C ++,因此可能存在一些语法错误:)

The simple answer is what Thomas suggest, but correctly done (that is, not causing undefined behavior): 简单的答案是托马斯建议的,但是正确完成了(也就是说,不会导致未定义的行为):

Create a mutable configuration object but pass it to the rest of the components by constant reference. 创建一个可变的配置对象,但通过常量引用将其传递给其余组件。 When you create (and where you maintain) the real object you can change it, but the rest of the application won't be able to modify the config. 在创建(和维护位置)真实对象时,可以对其进行更改,但是应用程序的其余部分将无法修改配置。 A common pattern I have used in the past was: 我过去使用的常见模式是:

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();
// ...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM