【问题标题】:Does a public static const variable break the encapsulation ideology?公共静态 const 变量是否破坏了封装思想?
【发布时间】:2011-05-07 16:31:43
【问题描述】:

我似乎总是在决定一个类中的值应该是 staticconst 应该是公共的还是私有的,并使用静态公共方法进行访问。

class DeepThought
{
public:
    static const int TheAnswer = 42;
};

对比:

class DeepThought
{
public:
    static int GetTheAnswer() { return TheAnswer; }
private:
    static const int TheAnswer = 42;
};

想要以第一种方式进行,但在我内心深处的某个地方感觉它破坏了封装,即使它是一个恒定值。第二种方式似乎并没有真正向表中添加任何内容,并且不必要地使代码混乱。

所以我问,这两种选择有什么根本错误吗?如果有,是什么?

【问题讨论】:

  • 我实际上从来没有,曾经需要在实际代码中做任何事情。你有这方面的真实用例吗?
  • @unapersson:你从未有过与类关联的常量吗?
  • 想想 C# 是如何做到的,任何常量原始类型也都是隐式静态的。它们总是出现,让您可以轻松地将可能是全局常量的内容关联起来,但不是全局的,而是将其与逻辑上属于的类关联。
  • @oli 是的,但不是静态的。
  • 真正的问题是你为什么要公开变量(即使通过吸气剂)。是否破坏封装的答案真的取决于值的含义。

标签: c++ encapsulation


【解决方案1】:

在纯理论意义上,第二种选择更正确。在实际意义上,我同意你的看法 - 用 getter 函数包装常量值是没有用的,无论如何都会被编译器删除。

根据我的经验,有时更简单的方法会更好,即使它在某种程度上违反了 OOP。

最后一点 - 我们曾经为此使用枚举:

enum CONSTS
{
    TheAnswer = 42,
};

【讨论】:

  • 如果你不得不改变它,编译器肯定会给你很多错误。
  • @Omnifarous:您客户的链接器也是如此,因为您破坏了二进制兼容性。当然,如果您不提供库二进制文件,那么您就不会关心这些。
【解决方案2】:

它们在语义上并不等价。首选第一个,因为它产生一个整数常量表达式,可用于数组边界、非类型模板参数、案例表达式等。

int a[DeepThought::TheAnswer];      // ok
int b[DeepThought::GetTheAnswer()]; // broken

【讨论】:

  • +1:没错!静态整型 const 在 C++ 中具有明确定义的重要作用。
  • 我同意。但是在 C++0x 中,函数也可以被标记为返回一个 const 表达式,因此在编译时用于数组边界。这并没有改变我更喜欢第一种方法的想法。
  • 每当有人兴高采烈地假设 C++0x 时,我都会感到困惑。我处理的代码,我们甚至不能假设 C++98。 (例如:我上次听说,Android NDK 不包含大部分 C++ 标准库。)
【解决方案3】:

您应该考虑为什么OOP“意识形态”说不要公开变量,只公开getter。通常的论点是因为您可能在将来的某个时候需要访问该变量来做一些更复杂的事情。但是一开始这种可能性很小,当您谈论常数时,这种可能性会变得更小。

我会继续将常量公开为常量。但是,当 current 不需要 getter 时,我通常也会继续公开变量。 Python 团队将其称为You Aren't Gonna Need It 原则的应用。

【讨论】:

    【解决方案4】:

    这是显而易见的:

    class DeepThought
    {
    public:
        static int GetTheAnswer() { return 42; }
    };
    

    【讨论】:

    • 跟我想的一样。
    【解决方案5】:

    你也可以使用第一个。考虑一下-您将在GetTheAnswer() 中输入什么逻辑?你不能改变它的签名或者它是静态的事实而不破坏它的接口考虑。这意味着,除非您要开始创建非常量全局变量,这将是 Extremely Bad™,否则您可以放入 GetTheAnswer() 中的任何内容都不能放入分配给 TheAnswer 的 constexpr 中。

    此外,使用GetTheAnswer() 可以做的事情是有限制的,例如,首先你可以获取常量的地址,而使用GetTheAnswer(),你不能,尽管你很合理应该可以的。

    【讨论】:

      【解决方案6】:

      像这样的这些常量基本上是隐藏的单例模式。只有一个可用的常量实例。这将是此类代码的正常演变:

      class DeepThought {
      public:
         static const int i=10;
         void f() { std::cout << i; }
      };
      

      然后你要改变变量以在运行时改变,它会变成这样:

         class Singleton {
            static Singleton &get() { static Singleton s; return s; }
            int get_i() const { return i; }
         };
         class DeepThought {
         public:
           void f() { std::cout << Singleton::get().get_i(); }
         };
      

      然后在某些时候你会想要不止一个常量实例,它会变成这样:

        struct Data
        {
           int i;
        };
        class DeepThought {
        public:
          DeepThought(Data &d) : d(d) { }
          void f() { std::cout << d.i; }
        private:
          Data &d;
        };
        int main() { Data d; DeepThought dt(d); dt.f(); }
      

      在代码的演变过程中,它会发生很大的变化,如果您有大量这样的代码,进行更改可能需要大量的工作。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-23
        • 1970-01-01
        • 2010-10-19
        相关资源
        最近更新 更多