101

你应该使用受保护的成员变量吗?这有什么优势以及会导致什么问题?

4

10 回答 10

79

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

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

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

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

于 2008-08-31T18:34:54.003 回答
35

一般来说,如果某事不是故意被认为是公开的,我会将其设为私有。

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

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

我会说这对大多数开发人员来说很好(并且可能是最好的方法)。

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

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

于 2008-09-01T03:09:28.180 回答
32

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

与受保护的方法/属性相比,它们没有特别的优势(曾几何时,它们可能具有轻微的性能优势),并且它们在非常深的继承流行的时代也被更多地使用,而目前还没有。

于 2008-08-31T18:42:08.337 回答
9

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

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

至关重要的是,从代码作为库发布的那一刻起,我永远无法做出这些假设,因为即使我更新我的设置器以保持纵横比,我也不能保证变量是通过设置器设置或通过现有代码中的吸气剂。

我的班级的任何子类也不能选择做出这种保证,因为他们也不能强制执行变量值,即使这是他们的子类的全部意义

举个例子:

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

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

于 2013-05-15T16:10:14.413 回答
8

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

在使成员变量受保护而不是带访问器的私有变量后,这是您的超类不能做的事情:

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

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

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

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

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

于 2008-12-10T20:15:13.963 回答
7

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

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

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

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

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

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

于 2010-02-09T23:44:46.467 回答
4

大多数时候,使用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. */ }

我在不到一个月前使用了这种代码(所以代码来自记忆)。经过一番思考,我认为虽然通用 Base 容器应该是一个抽象类,但即使它可以活得很好,因为直接使用 Base 会很痛苦,所以应该禁止它。

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

于 2008-10-11T21:14:30.017 回答
3

简而言之,是的。

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

于 2008-09-01T02:25:59.160 回答
3

只是为了记录,在“Exceptional C++”的第 24 项下,在一个脚注中,Sutter 说“你永远不会编写一个具有公共或受保护成员变量的类。对吧?(不管某些库设置的糟糕示例.)”

于 2013-04-15T09:50:35.507 回答
2

有关 .Net 访问修饰符的详细信息,请访问此处

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

于 2008-08-31T18:40:04.800 回答