代码之家  ›  专栏  ›  技术社区  ›  Jason R. Mick

对于C++中的非序列常量,我应该使用枚举还是多重const?

  •  4
  • Jason R. Mick  · 技术社区  · 14 年前

    我将从C中将文件IO集合移植到C++类中。神奇的数字(未命名的常数)比比皆是。

    函数读取一个文件头,其中有许多特定的条目,这些条目的位置当前由幻数表示。

    几年前,一位经验丰富的程序员教我使用“幻数”本身就是邪恶的,因此,我一直试图避免在端口中使用未命名的常量。所以我想创建一个常量列表,列出条目存储的位置。

    到目前为止,我已经提出了两个看起来相对安全的解决方案——使用名称空间封闭的常量集或名称空间封闭的枚举。

    我可以安全地使用这两种解决方案吗?一个对另一个有什么好处吗?

    例如
    选项1

    namespace hdr_pos {
       const unsigned int item_1_pos=4;
       const unsigned int item_2_pos=8;
       const unsigned int item_3_pos=12;
       const unsigned int item_4_pos=24;
       const unsigned int item_5_pos=32;
    };
    

    选项2

    namespace hdr_pos {
       enum e {
          item_1_pos=4,
          item_2_pos=8,
          item_3_pos=12,
          item_4_pos=24,
          item_5_pos=32
       };
    };
    

    是否有任何方法可以防止重复,如果由于将来对文件头的更新而更改位置,但忘记更改其中的一个位置,则需要进行捕获?

    请保持这一事实和非主观。如果你不知道有什么好处,请随时回答。

    注: 当然,在我的实际实现中,我会使用更具描述性的名称;我只是将其称为item_uult;>u…为了举例…

    5 回复  |  直到 14 年前
        1
  •  2
  •   bta    14 年前

    我可以看到使用枚举有两个好处。首先,一些调试器可以将常量转换回枚举变量名(这在某些情况下可以使调试更容易)。此外,还可以声明枚举类型的变量,该变量只能保存该枚举中的值。这可以为您提供一种额外的类型检查形式,而不仅仅是使用常量。

    检查值是否重复可能取决于特定的编译器。最简单的方法可能是编写一个外部脚本,该脚本将解析枚举定义并报告值是否重复(如果愿意,可以将其作为构建过程的一部分运行)。

        2
  •  1
  •   Matthieu M.    14 年前

    我以前处理过这种情况,因为有错误代码。

    我见过有人使用Enums作为错误代码,这带来了一些问题:

    1. 可以将int赋给与任何值都不对应的枚举(太差)
    2. 值本身在头中声明,这意味着错误代码重新分配(发生这种情况…)会破坏代码兼容性,在添加元素时还必须注意…
    3. 您必须在同一个头中定义所有代码,即使某些代码经常被自然地限制在应用程序的一小部分,因为枚举不能“扩展”。
    4. 不检查同一代码是否分配了两次
    5. 不能在 enum

    在设计我的错误代码解决方案时,我选择了另一条路:在源文件中定义的名称空间中的常量,地址点2和3。但是,为了获得类型安全性,常量不是 int 但具体来说 Code 班级:

    namespace error { class Code; }
    

    然后我可以定义几个错误文件:

    // error/common.hpp
    
    namespace error
    {
      extern Code const Unknown;
      extern Code const LostDatabaseConnection;
      extern Code const LostNASConnection;
    }
    
    // error/service1.hpp
    // error/service2.hpp
    

    不过,我没有解决任意的强制转换问题(构造函数是显式的,但是公共的),因为在我的案例中,需要转发其他服务器返回的错误代码,我当然不想知道所有这些错误代码(这太脆弱了)。

    然而,我确实考虑过,通过将所需的构造函数设为私有并强制使用构建器,我们甚至可以得到4个。5。猛扑:

    // error/code.hpp
    
    namespace error
    {
      class Code;
    
      template <size_t constant> Code const& Make(); // not defined here
    
      class Code: boost::totally_ordered<Code>
      {
      public:
        Code(): m(0) {} // Default Construction is useful, 0 is therefore invalid
    
        bool operator<(Code const& rhs) const { return m < rhs.m; }
        bool operator==(Code const& rhs) const { return m == rhs.m; }
    
      private:
        template <size_t> friend Code const& Make();
        explicit Code(size_t c): m(c) { assert(c && "Code - 0 means invalid"); }
    
        size_t m;
      };
    
      std::set<Code> const& Codes();
    }
    
    
    // error/privateheader.hpp (inaccessible to clients)
    
    namespace error
    {
      std::set<Code>& PrivateCodes() { static std::set<Code> Set; return Set; }
    
      std::set<Code> const& Codes() { return PrivateCodes(); }
    
      template <size_t constant>
      Code const& Make()
      {
        static std::pair< std::set<Code>::iterator, bool > r
          = PrivateCodes().insert(Code(constant));
        assert(r.second && "Make - same code redeclared");
        return *(r.first);
      }
    }
    
    //
    // We use a macro trick to create a function whose name depends
    // on the code therefore, if the same value is assigned twice, the
    // linker should complain about two functions having the same name
    // at the condition that both are located into the same namespace
    //
    #define MAKE_NEW_ERROR_CODE(name, value)         \
      Make<value>(); void _make_new_code_##value ();
    
    
    // error/common.cpp
    
    #include "error/common.hpp"
    
    #include "privateheader.hpp"
    
    namespace error
    {
      Code const Unkown = MAKE_NEW_ERROR_CODE(1)
      /// ....
    }
    

    多做一点工作(对于框架),只检查同一分配检查的链接时间/运行时。尽管通过扫描模式很容易诊断重复数据 MAKE_NEW_ERROR_CODE

    玩得高兴!

        3
  •  0
  •   AnT stands with Russia    14 年前

    您问题的标题表明,您怀疑使用枚举的主要原因是您的常量是 非迭代 . 但是在C++中枚举类型已经是非迭代的。为了生成迭代的枚举类型,必须跳过相当多的循环。

    我想说的是,如果常量是自然相关的,那么枚举是一个很好的主意,不管常量是否是迭代的。不过,枚举的主要缺点是完全缺少类型控制。在许多情况下,您可能更喜欢对常量值的类型有严格的控制(比如让它们无符号),这是枚举无法帮助您的(至少目前还没有)。

        4
  •  0
  •   John Dibling    14 年前

    要记住的一件事是你不能记下 enum :

    const unsigned* my_arbitrary_item = &item_1_pos;

        5
  •  0
  •   Puppy    14 年前

    如果它们是纯粹的常量并且不需要运行时的东西(比如不能用非枚举值初始化枚举),那么它们应该只是无符号常量。当然,枚举的类型较少,但这不只是要点。