【问题标题】:What is a good alternative to namespaces within classes?什么是类中命名空间的好选择?
【发布时间】:2012-11-07 16:24:15
【问题描述】:

问题是 C++ 不允许在类中声明命名空间。(我在网上搜索并找到了这个;如果不是真的,请说出来)那么,什么是欺骗这个问题的最佳方法?

上下文:我的班级中有一个枚举。

class GameCharacter {
public:
    enum MovingState {
        Running,
        Walking,
        Stopped
    };
    ...
};

OBS:这个例子不是真实的,完全是假设的。

C++ 定义枚举名称在类范围内,然后要使用这些状态,我必须直接从类名使用范围运算符(例如GameCharacter::Running;或using GameCharacter::Running)。

我认为这很糟糕,因为属于枚举的名称在类范围内;我想为 RunningState 枚举提供一个范围。 (以这种方式访问​​它:GameCharacter::MovingState::Running) 然后,我的第一个想法是创建一个命名空间来定义枚举的范围。

class GameCharacter {
public:
    // does not compile
    namespace MovingState {
        enum State {
            Running,
            Walking,
            Stopped
        };
    };
    ...
};

但是 C++ 禁止它。此代码无法编译。 (main.cpp:3:5: error: expected unqualified-id before ‘namespace’)

我之所以尝试以这种方式做事,是因为有可能创建具有相同范围内名称的第二个枚举。这可能会导致名称冲突。

class GameCharacter {
public:
    enum MovingState {
        Running,
        Walking,
        Stopped
    };
    enum DrivingState {
        Accelerating,
        Breaking,
        Stopped        // Compilation error: conflicts with previous declaration ‘GameCharacter::MovingState GameCharacter::Stopped’
    };
    ...
};

(我的想法是,在这种情况下,状态应该被称为GameCharacter::MovingState::StoppedGameCharacter::DrivingState::Stopped) 那我该怎么办?

【问题讨论】:

  • Breaking 表示停止工作。你的意思是Braking
  • 当然,对不起我的英语不好......
  • 很多以英语为母语的人都搞错了——事实上,我最近在几个 SO 问题上看到了同样的拼写错误!

标签: c++ enums namespaces scope


【解决方案1】:

尝试使用新的 C++11 功能 - strongly typed enumerations。多亏了它,您将不会发生名称冲突,并且您最终会得到非常强大的类型系统。

【讨论】:

    【解决方案2】:

    您可以为此使用枚举类

    enum class State
    {
        Accelerating,
        Breaking,
        Stopped
    };
    

    然后您可以根据需要将其称为 State::Breaking。 这当然需要c++11,见http://en.wikipedia.org/wiki/C%2B%2B11#Strongly_typed_enumerations

    【讨论】:

      【解决方案3】:

      在 C++1x(去年标准化的新版 C++)中,您可以使用 strongly typed enumerations,它将标识符置于枚举类本身的范围内(以及其他改进)。这些是用enum class声明的:

      class GameCharacter {
      public:
          enum class State {
              Running,
              Walking,
              Stopped    // Referred to as GameCharacter::MovingState::Stopped.
          };
          ...
      };
      

      同时,如果您坚持使用 C++03 编译器或想要保持兼容性,只需使用 class 而不是 namespace 并按照您在自己的答案中的建议将构造函数声明为私有。如果外部世界不需要看到它,您可以将整个 class 设为私有。

      【讨论】:

        【解决方案4】:

        我发现的另一种方法是使用类而不是命名空间,但问题是这样项目不够清晰,并且可以实例化类。 (虽然我可以用私有构造函数来欺骗这个问题)

        class GameCharacter {
        public:
            class MovingState {
                MovingState();
                    /* or, in C++11:
                     * MovingState() = delete;
                     */
            public:
                enum State {
                    Running,
                    Walking,
                    Stopped    // Referred as GameCharacter::MovingState::Stopped.
                };
            };
            class DrivingState {
                DrivingState();
                    /* or, in C++11:
                     * DrivingState() = delete;
                     */
            public:
                enum State {
                    Accelerating,
                    Breaking,
                    Stopped    // Referred as GameCharacter::DrivingState::Stopped.
                };
            };
            ...
        };
        

        这可行,但我不太清楚这是否是最佳解决方案。我认为应该有另一种方式。我要意见。你怎么看?

        【讨论】:

        • +1 这是 C++03 中的常见解决方案,在 C++11 中强类型枚举之前
        【解决方案5】:

        这听起来像是 C++11 的 Strongly Typed Enumerations 的工作。

        class GameCharacter {
        public:
            enum class MovingState {
                Running,
                Walking,
                Stopped
            };
            ...
            void f(Movingstate m) { if (m == MovingState;:Running) ... }
        };
        void f() { if (mm == GameCharacter::MovingState::Running) ... }
        

        【讨论】:

          【解决方案6】:

          如果你使用C++11,你可以使用

          class GameCharacter {
           public:
            enum class DrivingState { Accelerating, Breaking, Stopped };
            enum class WalkingState { Stopped, Running, Walking };
          
            DrivingState driving_state() { return ds_; }
            void set_driving_state(DrivingState ds) { ds_ = ds; }
          
            WalkingState walking_state() { return ws_; }
            void set_walking_state(WalkingState ws) { ws_ = ws; }
          
            DrivingState ds_;
            WalkingState ws_;
          };
          
          int main() {
            GameCharacter g;
            g.set_walking_state(GameCharacter::WalkingState::Stopped);
            g.set_driving_state(GameCharacter::DrivingState::Stopped);
            return 0;
          }   
          

          【讨论】:

            【解决方案7】:

            如果不能使用 C++11 strongly typed enums,可以使用结构体代替命名空间:

            class GameCharacter {
            public:
                struct MovingState {
                    enum Enum {
                        Running,
                        Walking,
                        Stopped
                    };
                }
                ...
            };
            

            这是我们采用了一段时间(直到 C++11 支持)的东西,因为它可以在任何地方使用,不像命名空间技巧。

            【讨论】:

              【解决方案8】:

              如果你的类中有太多东西以至于你觉得需要将它细分到命名空间中,那么你的类有太多东西

              你应该把东西移出来。特别是所有这些枚举不需要是类的成员。为什么不将它们保存在类所在的同一个命名空间中?

              【讨论】:

              • 掌握模式。如果仅在该类中使用枚举,则将其移开是没有意义的。编辑:另外,仅仅因为一个类有太多东西并不一定意味着它设计得不好。类可以有很多职责。
              • @LeonardoRaele:不一定。听起来像是因为某些设计模式告诉您将所有内容集中到一个对象中的情况。实际上,通过拥有所有这些类型,您给您的对象太多责任。它们属于一个命名空间。你可以让它成为一个只包含这些枚举和那个类的命名空间,如果它让你感觉更好的话......
              • Also, just because a class have too much things does not necessarily means that it was bad designed. 是的,根据“太多”的定义,准确来说就是这个意思。
              猜你喜欢
              • 2020-10-28
              • 2023-04-07
              • 1970-01-01
              • 2011-03-23
              • 2011-07-20
              • 2013-11-18
              相关资源
              最近更新 更多