【问题标题】:Should I use public or private variables?我应该使用公共变量还是私有变量?
【发布时间】:2013-01-02 05:20:46
【问题描述】:

我是第一次做一个大型项目。我有很多类,其中一些有公共变量,一些有带有 setter 和 getter 方法的私有变量,同样有两种类型。

我决定重写这段代码,主要只使用一种类型。但我不知道我应该使用哪个(仅用于同一对象中的方法的变量始终是私有的,不是这个问题的主题)。

我知道公共和私人意味着什么的理论,但在现实世界中使用的是什么,为什么?

【问题讨论】:

  • OO 的答案是使用私有变量。但是 C++ 中的可见性模型非常糟糕(在 C++11 中稍微少一点),如果你不给它们起丑化的名字,私有成员可能会导致非常令人惊讶的问题。
  • @MarcGlisse Wut?
  • 几乎完全是一个骗局:stackoverflow.com/questions/1596432/…
  • 在 C++11 之前,如果您在具有私有 x 成员的类型上调用另一个重载,则在其签名中使用 T::x 进行重载会导致硬错误。但即使在 C++11 中,您仍然会遇到以下问题:gcc.gnu.org/bugzilla/show_bug.cgi?id=55713 编译器坚持给出错误,而不是忽略私有成员(或基)。当然还有更糟糕的例子。听说有几个委员会成员称 C++ 访问控制被破坏,尽管我认为这可能是出于不同的原因。

标签: c++ oop abstraction getter-setter data-access


【解决方案1】:

从 OOP 的角度来看,getter/setter 有助于封装,因此应始终使用。当您调用 getter/setter 时,该类可以在幕后为所欲为,并且该类的内部不会暴露在外部。

另一方面,从 C++ 的角度来看,如果类在您只想获取/设置一个值时做了很多意想不到的事情,这也可能是一个劣势。人们想知道某些访问是否会导致巨大的开销或是否简单高效。当您访问公共变量时,您确切地知道您得到了什么,当您使用 getter/setter 时,您一无所知。

特别是如果您只做一个小项目,花时间编写 getter/setter 并在您决定更改变量名称/类型/时相应地调整它们...会产生大量忙碌的工作而收效甚微。你最好把时间花在编写有用的代码上。

当 C++ 代码不能提供真正的收益时,它们通常不会使用 getter/setter。如果您设计一个 1,000,000 行的项目,其中包含许多必须尽可能独立的模块,这可能是有道理的,但对于您每天编写的大多数正常大小的代码来说,它们是多余的。

【讨论】:

    【解决方案2】:

    私有成员变量优于公共成员变量,主要是出于上述原因(封装、明确指定的数据等)。它们还提供了一些数据保护,因为它保证任何外部实体都不能在需要时通过 setter 的正确通道来更改成员变量。

    getter 和 setter 的另一个好处是,如果您使用的是 IDE(如 Eclipse 或 Netbeans),您可以使用 IDE 的功能来搜索代码库中调用该函数的每个位置.它们提供了关于该特定类中的一条数据在何处被使用或修改的可见性。此外,您可以通过使用内部互斥锁轻松地使对成员变量的访问成为线程安全的。 getter/setter 函数会在访问或修改变量之前获取此互斥体。

    我支持抽象到它仍然有用的程度。为抽象而抽象通常会导致比它的价值更复杂的混乱。

    【讨论】:

      【解决方案3】:

      有些数据类型的唯一目的是保存明确指定的数据。这些通常可以编写为具有公共数据成员的结构。除此之外,一个类应该定义一个抽象。公共变量或琐碎的 setter 和 getter 表明设计没有经过充分考虑,导致聚集了不抽象任何东西的弱抽象。与其考虑数据,不如考虑行为:这个类应该做 X、Y 和 Z。从那里,决定需要哪些内部数据来支持所需的行为。一开始这并不容易,但要不断提醒自己重要的是行为,而不是数据。

      【讨论】:

        【解决方案4】:

        既然您说您知道理论,并且其他答案已经深入了解了公共/私有、getter 和 setter 的含义,我想专注于为什么使用访问器而不是创建公共属性(成员数据在 C++ 中)。

        假设您在物流项目中有一个卡车类:

        class Truck {
        public:
            double capacity;
        
            // lots of more things...
        };
        

        如果您是北美人,您可能会使用加仑来表示您的卡车的容量。想象一下,您的项目已经完成,它运行良好,尽管 Truck::capacity 的许多直接使用已经完成。实际上,您的项目取得了成功,因此一些欧洲公司要求您调整您的项目以适应他们;不幸的是,该项目现在应该使用公制系统,因此容量应该使用升而不是加仑。

        现在,这可能是一团糟。当然,一种可能性是只为北美准备一个代码库,只为欧洲准备一个代码库。但这意味着应该在两个不同的代码源中应用错误修复,这被认为是不可行的。

        解决方案是在您的项目中创建配置可能性。用户应该能够设置加仑或升,而不是固定的、硬连线的加仑选择。

        使用上面看到的方法,这将意味着大量工作,您必须追踪Truck::capacity 的所有用途,并决定如何处理它们。这可能意味着修改整个代码库中的文件。作为替代方案,我们假设您决定采用更多 theoretic 的方法。

        class Truck {
        public:
            double getCapacity() const
                { return capacity; }
        
            // lots of more things...
        private:
            double capacity;
        };
        

        一个可能的替代更改不涉及修改类的接口:

        class Truck {
        public:
            double getCapacity() const
                { if ( Configuration::Measure == Gallons ) {
                    return capacity;
                  } else {
                     return ( capacity * 3.78 );
                  }
                }
        
        
            // lots of more things...
        private:
            double capacity;
        };
        

        (请注意,有很多方法可以做到这一点,一种只是一种可能性,这只是一个例子)

        您必须创建全局实用程序类配置(但无论如何您都必须这样做),并在truck.h 中为configuration.h 添加一个包含,但这些都是本地更改,您的代码库的其余部分保持不变不变,从而避免潜在的错误。

        最后,您还声明您现在正在从事一个大项目,我认为在这个领域中,这些原因实际上更有意义。请记住,在大型项目中工作时要牢记的目标是创建可维护的代码,即可以更正和扩展新功能的代码。您可以忘记个人小型项目中的 getter 和 setter,尽管我会尽量让自己习惯它们。

        希望这会有所帮助。

        【讨论】:

        • 我知道这是一个玩具示例,我基本同意这些结论,但是如果您正在运行一个包含多个单元的项目,请不要这样做 .相反,在内部使用 SI 单位存储所有内容,并且仅在与用户交互时在单位之间进行转换(如果您正在远程执行 MVC-ish 操作,则在视图层中)。
        【解决方案5】:

        对于什么应该是私有/公共或受保护没有硬性规定。

        这取决于您的班级的角色及其提供的内容。

        • 构成内部运作的所有方法和成员 该课程应设为私有
        • 课程向外界提供的所有内容都应公开
        • 可能必须在此类的特化中扩展的成员和方法, 可以声明为protected

        【讨论】:

          【解决方案6】:

          通常不鼓励使用公共变量,更好的形式是将所有变量设为私有并使用 getter 和 setter 访问它们:

          private int var;
          
          public int getVar() {
            return var;
          }
          
          public void setVar(int _var) {
            var = _var;
          }
          

          Eclipse 等现代 IDE 提供“实现 Getter 和 Setter”和“封装字段”等功能(用相应的 getter 和 setter 调用替换所有对变量的直接访问),从而帮助您做到这一点。

          【讨论】:

          • 为什么最好将变量设为私有并且提供getter和setter?将该过程称为“封装字段”具有误导性;它只是用更详细的访问语法替换了一种访问语法。 (注意,问题标记为 C++。)
          • getter 和 setter 提供了一种错误的封装感。
          【解决方案7】:

          private 数据成员通常被认为是好的,因为它们提供了封装。

          为它们提供 getter 和 setter 打破了这种封装,但它仍然比 public 数据成员更好,因为只有一次访问该数据的点。

          您会在调试过程中注意到这一点。如果它是私有的,你知道你只能修改类中的变量。如果它是公开的,您将不得不搜索整个代码库以查找它可能被修改的位置。

          尽可能禁止 getter/setter 并设置属性 private。这遵循了信息隐藏的原则——你不应该关心一个类有什么属性。它应该是独立的。当然,在实践中这是不可行的,如果可行,那么遵循这一点的设计将比不遵循的设计更加混乱和难以维护。

          这当然是一个经验法则 - 例如,我只使用 struct(相当于具有公共访问权限的 class)作为简单的点类:

          struct Point2D
          {
             double x;
             double y;
          };
          

          【讨论】:

          • 我会说 setter/getter 除了直接访问变量之外还创建了一个额外的访问点。
          • @sth 但直接访问仅限于类,所以它的东西。 (如果这就是你的意思,那就是)
          • +1 表示“尽可能禁止 getter/setter”。我总是声称二传手是邪恶的(我的意思是功能。我不介意狗)。
          • @LuchianGrigore:无论如何都向公众提供了所有相同的访问权限,因此实际上您只创建了两种从类内部使用变量的方法......我真的不认为限制其中一种如果您同时重新创建一种从外部获得所有相同访问权限的方法,则对类的访问方法会有所帮助。
          • 您还应该提到private 成员和public 成员应该优先于public 成员,因为这是您控制成员如何更改的唯一方法。这可以(例如)用于支持向上兼容性。想象一下,新版本的 Point2D 只能处理 -100.0 和 +100.0 之间的值。没有(简单的)方法可以使用上面的结构来实现这一点,而不会导致使用此结构的客户端更改代码。
          猜你喜欢
          • 2011-05-25
          • 2011-12-03
          • 1970-01-01
          • 2012-01-25
          • 1970-01-01
          • 1970-01-01
          • 2011-05-25
          • 1970-01-01
          • 2010-10-21
          相关资源
          最近更新 更多