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

有帮助吗?

解决方案

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

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

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

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

其他提示

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

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

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

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

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

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

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

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

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

这是你的超类 不能 在将成员变量设置为 protected 而不是 private-with-accessors 后执行以下操作:

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

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

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

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

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

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

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

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

访问器/修改器方法在维护过程中提供了更高的 API 稳定性和实现灵活性。

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

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

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

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

关键的是,我可以 绝不 从代码作为库发布的那一刻起,随时做出这些假设,因为即使我更新设置器以保持纵横比,我也不能保证变量是通过现有代码中的设置器设置的或通过获取器访问的。

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

举个例子:

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

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

大多数时候,使用 protected 是危险的,因为您会在一定程度上破坏类的封装,而设计不当的派生类很可能会破坏这种封装。

但我有一个很好的例子:假设您可以使用某种通用容器。它有一个内部实现和内部访问器。但您需要提供至少 3 个对其数据的公共访问:映射、散列映射、类似向量。然后你会得到类似的东西:

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 会很痛苦,应该被禁止。

概括 因此,您已经保护了派生类使用的数据。尽管如此,我们必须考虑到基类应该是抽象的这一事实。

简而言之,是的。

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

仅根据记录,在“典型C ++”的第24项下,在其中一个脚注中,Sutter Goes“您永远不会写具有公共或受保护成员变量的类。正确的?(尽管有些图书馆树立了糟糕的榜样。)”

有关 .Net 访问修饰符的详细信息 到这里

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top