你应该使用受保护的成员变量吗?

时间:2008-08-31 18:22:46

标签: oop protected

您是否应该使用受保护的成员变量?这有什么优势和问题?

10 个答案:

答案 0 :(得分:70)

  

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

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

  • 如果您不希望任何内部状态泄漏,那么将所有成员变量声明为私有是可行的方法。
  • 如果你真的不在乎子类可以访问内部状态,那么受保护就足够了。

如果开发人员出现并将您的课程子类化,他们可能会搞砸,因为他们不完全理解它。对于除公共界面之外的私有成员,他们无法看到实现方式的实现具体细节,这使您可以灵活地在以后更改它。

答案 1 :(得分:30)

现在普遍的感觉是它们会导致衍生类和它们的基础之间的过度耦合。

它们与受保护的方法/属性相比没有特别的优势(曾几何时它们可能具有轻微的性能优势),并且它们也被用于更深层次的继承时尚的时代,它不在那一刻。

答案 2 :(得分:28)

一般来说,如果某些事情不是故意设想为公开的,我会把它变成私人的。

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

这几乎不会发生 - 我真的不是所有继承的粉丝,因为它不是模拟大多数情况的特别好方法。无论如何,继续,不用担心。

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

事情的简单事实是,如果其他一些开发人员在一年之后出现并决定他们需要访问您的私有成员变量,他们只是编辑代码,将其更改为保护,并继续他们的业务。

唯一真正的例外情况是,如果你的业务是以黑盒形式向第三方发送二进制dll。这基本上包括Microsoft,那些“Custom DataGrid Control”供应商,以及可能带有可扩展性库的其他一些大型应用程序。除非你属于那个类别,否则不值得花时间/精力去担心这类事情。

答案 3 :(得分:7)

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

以下是您的超类在使成员变量受保护而不是私有访问者后不能做的事情:

  1. 在阅读该属性时,懒惰地创建一个值。如果添加受保护的getter方法,则可以懒惰地创建该值并将其传回。

  2. 知道何时修改或删除了该属性。当超类对该变量的状态做出假设时,这可能会引入错误。为变量创建受保护的setter方法可以保持该控制。

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

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

  5. 一般来说,我认为我建议制作受保护的成员变量是极少数情况。最好花几分钟时间通过getter / setter暴露财产,而不是几小时后追踪修改受保护变量的其他代码中的错误。不仅如此,您还可以避免在不破坏相关代码的情况下添加未来的功能(例如延迟加载)。这样做比以后更难做到。

答案 4 :(得分:6)

在设计层面,使用受保护的属性可能是合适的,但是对于实现,我认为将其映射到受保护的成员变量而不是accessor / mutator方法没有任何优势。

受保护的成员变量具有明显的缺点,因为它们有效地允许客户端代码(子类)访问基类类的内部状态。这可以防止基类有效地维护其不变量。

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

Accessor / mutator方法在维护时提供了更多的API稳定性和实现灵活性。

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

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

答案 5 :(得分:6)

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

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

重要的是,从代码作为库发布的那一刻起,我就永远不会做出那些假设,因为即使我更新我的setter以保持宽高比,我也无法保证变量正在通过设置者设置或通过现有代码中的getter访问。

我班级的任何子类也不能选择提供这种保证,因为他们也不能强制执行变量值,即使这是他们子类的整个点

举个例子:

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

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

答案 6 :(得分:4)

大多数情况下,使用受保护是危险的,因为你打破了类的封装,这很可能被设计不良的派生类分解。

但我有一个很好的例子:假设您可以使用某种通用容器。它有一个内部实现和内部访问器。但是你需要提供至少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. */ }

我在不到一个月前使用过这种代码(因此代码来自内存)。经过一番思考后,我相信虽然通用的Base容器应该是一个抽象类,即使它可以很好地存活,因为直接使用Base会是一种痛苦,应该被禁止。

摘要因此,您拥有派生类使用的受保护数据。但是,我们必须考虑到Base类应该是抽象的事实。

答案 7 :(得分:3)

简而言之,是的。

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

答案 8 :(得分:2)

有关.Net访问修饰符go here

的详细信息

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

答案 9 :(得分:2)

仅仅为了记录,在“Exceptional C ++”的第24项下,在其中一个脚注中,Sutter去了 “你永远不会写一个有公共或受保护成员变量的类。对吧?(不管一些图书馆设置的不好的例子。)”