代码之家  ›  专栏  ›  技术社区  ›  John Channing

您是否应该使用受保护的成员变量?

  •  90
  • John Channing  · 技术社区  · 17 年前

    您是否应该使用受保护的成员变量?这有什么好处,会导致什么问题?

    10 回复  |  直到 8 年前
        1
  •  68
  •   Allain Lalonde    14 年前

    您是否应该使用受保护的成员变量?

    取决于你对隐藏状态的挑剔程度。

    • 如果不希望任何内部状态泄漏,那么将所有成员变量声明为private是一种可行的方法。
    • 如果您不关心子类是否可以访问内部状态,那么protected就足够了。

    如果一个开发人员出现在你的类的子类中,他们可能会把它弄乱,因为他们不完全理解它。对于私有成员(而不是公共接口),他们看不到具体的实现细节,也看不到如何进行操作,这就给了您以后更改它的灵活性。

        2
  •  29
  •   Will Dean    17 年前

    现在的普遍感觉是,它们导致派生类与其基类之间的过度耦合。

    与受保护的方法/属性相比,它们没有什么特别的优势(以前它们可能具有轻微的性能优势),而且它们也更多地用于深度继承流行的时代,而现在还不是。

        3
  •  27
  •   Orion Edwards    17 年前

    一般来说,如果某件事不是有意地被认为是公开的,我会把它变成私人的。

    如果出现需要从派生类访问该私有变量或方法的情况,我会将其从private更改为protected。

    这几乎从来没有发生过——我真的不是继承的粉丝,因为它不是模拟大多数情况的特别好的方法。无论如何,继续下去,不要担心。

    对于大多数开发人员来说,这是很好的(可能也是最好的方法)。

    事情的简单事实是 如果一年后其他开发人员发现他们需要访问您的私有成员变量,那么他们只需编辑代码,将其更改为protected,然后继续他们的业务。

    唯一真正的例外是,如果您从事的业务是将黑盒形式的二进制dll发送给第三方。这基本上由微软、那些“自定义数据报控件”供应商,以及其他一些与可扩展性库一起提供的大型应用程序组成。除非你属于这类人,否则花时间/精力去担心这类事情是不值得的。

        4
  •  6
  •   Michael Bishop    16 年前

    一般来说,我会将受保护的成员变量保留在少数情况下,在这种情况下,您也可以完全控制使用它们的代码。如果您正在创建公共API,我会说永远不会。下面,我们将成员变量称为对象的“属性”。

    这是你的超级市场 不能 将成员变量设为受保护的而不是专用的访问器后执行:

    1. 在读取属性时,惰性地动态创建一个值。如果您添加了一个受保护的getter方法,您可以惰性地创建该值并将其传递回去。

    2. 知道何时修改或删除属性。当超类对该变量的状态进行假设时,这可能会引入错误。为变量生成受保护的setter方法将保留该控件。

    3. 在读取或写入变量时设置断点或添加调试输出。

    4. 重命名该成员变量,而不搜索可能使用它的所有代码。

    一般来说,我认为很少有这样的情况,我建议您创建一个受保护的成员变量。您最好花几分钟时间通过getter/setter暴露属性,而不是几小时后在修改受保护变量的其他代码中跟踪错误。不仅如此,您还可以确保在不破坏依赖代码的情况下添加将来的功能(如延迟加载)。迟做比现在做要难。

        5
  •  6
  •   richj    15 年前

    在设计级别,使用受保护的属性可能是合适的,但对于实现,我在将其映射到受保护的成员变量(而不是访问器/赋值器方法)时没有看到任何优势。

    受保护的成员变量有很大的缺点,因为它们有效地允许客户机代码(子类)访问基类的内部状态。这会阻止基类有效地维护其不变量。

    出于同样的原因,受保护的成员变量也使得编写安全的多线程代码变得更加困难,除非保证常量或仅限于单个线程。

    访问器/转换器方法在维护时提供了相当多的API稳定性和实现灵活性。

    另外,如果您是OO纯粹主义者,对象通过发送消息而不是读取/设置状态进行协作/通信。

    作为回报,他们提供的优势很少。我不一定要从别人的代码中删除它们,但我自己不使用它们。

        6
  •  6
  •   deworde    10 年前

    对我来说关键的问题是,一旦你使一个变量受到保护,你就不能允许你的类中的任何方法 依靠 它的值在一个范围内,因为子类总是可以把它放在范围之外。

    例如,如果我有一个类定义了可渲染对象的宽度和高度,并且使这些变量受到保护,那么我就不能对纵横比(例如)做任何假设。

    关键是,我可以 从未 从代码作为库发布的那一刻起,在任何时候都要做这些假设,因为即使我更新了setter以保持纵横比,我也不能保证变量是通过setter设置的,或者是通过现有代码中的getter访问的。

    我类的任何子类也不能选择这样做,因为它们也不能强制变量值, 即使这是它们子类的全部点 .

    举个例子:

    • 我有一个矩形类,其宽度和高度存储为受保护变量。
    • 一个明显的子类(在我的上下文中)是一个“DisplayedRectangle”类,其中唯一的区别是我将宽度和高度限制为图形显示的有效值。
    • 但现在不可能了 ,因为我的DisplayedRectangle类 不能 真正约束这些值,因为它的任何子类都可以直接覆盖这些值,同时仍然被视为一个显示的矩形。

    通过将变量约束为私有的,我可以通过setter或getter来强制实现我想要的行为。

        7
  •  4
  •   paercebal    16 年前

    大多数情况下,使用protected是危险的,因为您打破了类的一些封装,而这些封装很可能被设计不好的派生类破坏。

    但我有一个很好的例子:假设您可以使用某种通用容器。它有一个内部实现和内部访问器。但您需要提供至少3个对其数据的公共访问:map、hash_map、vector-like。然后你会得到如下的东西:

    template <typename T, typename TContainer>
    class Base
    {
       // etc.
       protected
       TContainer container ;
    }
    
    template <typename Key, typename T>
    class DerivedMap     : public Base<T, std::map<Key, T> >      { /* etc. */ }
    
    template <typename Key, typename T>
    class DerivedHashMap : public Base<T, std::hash_map<Key, T> > { /* etc. */ }
    
    template <typename T>
    class DerivedVector  : public Base<T, std::vector<T> >        { /* etc. */ }
    

    我不到一个月前就用过这种代码(所以代码是从内存中提取的)。经过一些思考,我认为虽然泛型基容器应该是一个抽象类,但即使它可以很好地运行,因为直接使用基容器会非常痛苦,所以应该禁止使用它。

    总结 因此,您已经保护了派生类使用的数据。不过,我们必须考虑到基类应该是抽象的这一事实。

        8
  •  3
  •   Jay Stramel    17 年前

    简而言之,是的。

    受保护的成员变量允许从任何子类以及同一包中的任何类访问该变量。这可能非常有用,特别是对于只读数据。但是,我不认为有必要使用它们,因为任何对受保护成员变量的使用都可以使用私有成员变量和两个getter和setter进行复制。

        9
  •  2
  •   Community CDub    10 年前

    有关.NET访问修饰符的详细信息 go here

    受保护的成员变量没有真正的优点或缺点,这是您在特定情况下需要什么的问题。一般来说,将成员变量声明为私有变量并允许通过属性进行外部访问是公认的做法。另外,一些工具(例如一些O/R映射器)希望对象数据由属性表示,并且不识别公共或受保护的成员变量。但是,如果您知道您希望子类(并且只有子类)访问某个变量,那么没有理由不将其声明为受保护的。

        10
  •  2
  •   hAcKnRoCk    12 年前

    作为记录,在“例外C++”的第24项中,在一个脚注中,萨特走了。 “您永远不会编写具有公共或受保护成员变量的类。正确的?(不考虑某些库设置的糟糕示例。)