保護されたメンバー変数を使用する必要がありますか?


97

保護されたメンバー変数を使用する必要がありますか?利点は何ですか?これによりどのような問題が発生する可能性がありますか?

回答:


73

保護されたメンバー変数を使用する必要がありますか?

状態を隠すことについてどれほどうるさいかによります。

  • 内部状態のリークが必要ない場合は、すべてのメンバー変数をプライベートとして宣言するのがよいでしょう。
  • サブクラスが内部状態にアクセスできることを本当に気にしない場合は、protectedで十分です。

開発者がやって来てあなたのクラスをサブクラス化するなら、彼らはそれを完全に理解していないので彼らはそれを台無しにするかもしれません。パブリックインターフェース以外のプライベートメンバーを使用すると、実装の具体的な詳細を確認できないため、後で変更する柔軟性が得られます。


1
get / setメソッドを使用して、保護された変数とプライベート変数のパフォーマンスについてコメントできますか?
ジェイク

3
プロファイリングでボトルネックが最終的にアクセサーになることを発見しない限り、これは心配する価値のあることではないと思います(ほとんどありません)。JITが問題になる場合、JITをよりスマートにするために実行できるトリックがあります。たとえば、Javaでは、アクセサをfinalとしてマークすることで、アクセサをインライン化できることを示唆できます。正直なところ、ゲッターとセッターのパフォーマンスは、システム構成やプロファイラーによって決定される実際のパフォーマンスの問題を処理することよりもはるかに重要ではありません。
Allain Lalonde、2010年

23
@ジェイク:パフォーマンスの仮定に基づいて設計を決定することはできません。最良の設計であると考えるものに基づいて設計の決定を行い、実際のプロファイリングが設計にボトルネックを示した場合にのみ、それを修正します。通常、デザインがサウンドであれば、パフォーマンスも良好です。
Mecki

パブリックインターフェース以外のプライベートメンバーでは、実装固有の詳細見ることができません。クラスを開いてそれを調べるだけなので、意味がありません。
ブラック

2
@Black明らかにAllainは、これらのメンバーに「アクセスできない」ため、コードをビルドできないため、クラスの作成者が保護されたメンバーを後で削除/変更できるようにしました。(もちろん、pimplイディオムを使用すると、視覚的に、およびヘッダーを含む翻訳単位からも非表示にできます。)
underscore_d

31

今日の一般的な感覚は、それらが派生クラスとそのベースの間の過度の結合を引き起こすということです。

それらは保護されたメソッド/プロパティよりも特別な利点はありません(かつて、わずかなパフォーマンス上の利点がある可能性があります)。また、非常に深い継承が流行していた時代には、現時点では使用されていませんでした。


2
すべきではno particular advantage over protected methods/propertiesないno particular advantage over *private* methods/properties
Penghe Geng 2014

いいえ、私は派生クラスとそのベースの間のさまざまな通信方法の利点/欠点について話していたので、これらのテクニックはすべて「保護」されています-違いは、それらがメンバー変数(フィールド)であるか、プロパティ/メソッド(つまり、ある種のサブルーチン)。
ウィルディーン

1
迅速な説明ありがとうございます。6年前の投稿への質問に対して、元の投稿者から1時間以内に返信してくれてうれしいです。あなたはそれが他のほとんどのオンラインフォーラムで起こり得るとは思わない:)
Penghe Geng

9
さらに注目すべきは、私が実際にその期間にわたって自分自身に同意することです...
ウィルディーン

コンストラクターのビジネスの1つの順序は、すべての状態変数が明示的に初期化されることを確認することです。この規則に準拠している場合は、super構成を使用して親コンストラクタを呼び出すことができます。次に、親クラスのプライベート状態変数を初期化します。
ncmathsadist 2014

31

一般に、意図的にパブリックとして考えられていないものは、プライベートにします。

派生クラスからプライベート変数またはメソッドにアクセスする必要がある状況が発生した場合は、プライベートから保護に変更します。

これはめったに起こりません-ほとんどの状況をモデル化するのに特に良い方法ではないので、私は継承のすべてで本当にファンではありません。とにかく、心配する必要はありません。

これは、大多数の開発者にとっては問題ない(そしておそらくそれを行うための最良の方法でもある)と思います。

問題の簡単な事実は 1年後に他の開発者が来て、プライベートメンバー変数にアクセスする必要があると判断した場合、彼らは単にコードを編集し、コードを保護に変更し、ビジネスを続行することです。

これに対する唯一の本当の例外は、バイナリdllをブラックボックス形式でサードパーティに出荷する場合です。これは基本的に、Microsoft、それらの「カスタムDataGridコントロール」ベンダー、および拡張ライブラリが同梱されている他のいくつかの大きなアプリで構成されています。あなたがそのカテゴリーに属していない限り、この種のことを心配する時間/労力を費やす価値はありません。


8

私にとって重要な問題は、変数を保護すると、サブクラスが常に範囲外に配置できるため、クラス内のメソッドがその値が範囲内にあることに依存することを許可できないことです。

たとえば、レンダリング可能なオブジェクトの幅と高さを定義するクラスがあり、それらの変数を保護する場合、(たとえば)アスペクト比を仮定することはできません。

批判的に、私は決してできませんコードがライブラリとしてリリースされた瞬間から、これらの仮定を行うアスペクト比を維持するようにセッターを更新しても、変数がセッターを介して設定されているか、または既存のコードのゲッター。

また、私のクラスのどのサブクラスもその保証を選択することはできません。サブクラスの全体的なポイントであっても、変数の値を強制できないためです。

例として:

  • 幅と高さが保護された変数として保存されている長方形のクラスがあります。
  • (私のコンテキスト内の)明らかなサブクラスは "DisplayedRectangle"クラスです。唯一の違いは、幅と高さをグラフィック表示の有効な値に制限していることです。
  • しかし、現在不可能です。DisplayedRectangleクラスこれらの値を実際に制約できないためです。そのサブクラスは値を直接オーバーライドでき、DisplayedRectangleとして扱われます。

変数をプライベートに制限することで、セッターまたはゲッターを介して必要な動作を強制できます。


7

一般に、保護されたメンバー変数は、それらを使用するコードを完全に制御できるというまれなケースに留めておきます。パブリックAPIを作成している場合、私は絶対に言わないでしょう。以下では、メンバー変数をオブジェクトの「プロパティ」と呼びます。

メンバー変数をprivate-with-accessorsではなく保護した後、スーパークラス実行できないことは次のとおりです。

  1. プロパティが読み取られているときにオンザフライで遅延して値を作成します。保護されたゲッターメソッドを追加すると、遅延して値を作成し、それを返すことができます。

  2. プロパティがいつ変更または削除されたかがわかります。これは、スーパークラスがその変数の状態を仮定しているときにバグを引き起こす可能性があります。変数のプロテクトセッターメソッドを作成すると、その制御が維持されます。

  3. 変数の読み取りまたは書き込み時に、ブレークポイントを設定するか、デバッグ出力を追加します。

  4. 使用する可能性のあるすべてのコードを検索せずに、そのメンバー変数の名前を変更します。

一般に、保護されたメンバー変数を作成することをお勧めするのはまれなケースだと思います。保護された変数を変更した他のいくつかのコードのバグを追跡するのに数時間かかるよりも、ゲッター/セッターを介してプロパティを公開するのに数分費やすほうがよいでしょう。それだけでなく、依存するコードを壊すことなく、将来の機能(遅延読み込みなど)を追加しないようにすることができます。今よりも後で行う方が難しいです。


7

設計レベルでは、保護されたプロパティを使用するのが適切かもしれませんが、実装では、これをアクセサー/ミューテーターメソッドではなく保護されたメンバー変数にマッピングする利点はありません。

保護されたメンバー変数は、クライアントコード(サブクラス)が基本クラスクラスの内部状態に効果的にアクセスできるため、重大な欠点があります。これにより、基本クラスがその不変条件を効果的に維持できなくなります。

同じ理由で、保護されたメンバー変数も、定数が保証されているか、単一のスレッドに限定されていない限り、安全なマルチスレッドコードの記述を大幅に難しくします。

アクセサー/ミューテーターメソッドは、メンテナンス中のAPIの安定性と実装の柔軟性を大幅に向上させます。

また、オブジェクト指向の純粋主義者の場合、オブジェクトは、状態の読み取り/設定ではなく、メッセージを送信することで、コラボレーション/通信を行います。

その見返りとして、それらにはほとんど利点がありません。私は必ずしも他の人のコードからそれらを削除するとは限りませんが、私はそれらを自分で使用しません。


4

ほとんどの場合、クラスのカプセル化をいくらか壊すので、プロテクトを使用するのは危険です。不適切に設計された派生クラスによって分解される可能性があります。

しかし、私には1つの良い例があります。たとえば、ある種の汎用コンテナーを使用できるとしましょう。内部実装と内部アクセサーがあります。ただし、そのデータへの少なくとも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. */ }

私はこの種のコードを1か月前以内に使用しました(そのため、コードはメモリからのものです)。考えた結果、汎用のBaseコンテナは抽象クラスである必要がありますが、たとえそれが十分に機能しても、直接Baseを使用するのは非常に面倒なので禁止する必要があります。

まとめこれで、派生クラスで使用されるデータが保護されました。それでも、Baseクラスが抽象である必要があるという事実を考慮に入れる必要があります。


パブリックメンバーよりもカプセル化が壊れることはありません。派生クラスは、クラスのユーザーに公開されていないクラスの状態を使用できると言う設定です。
gbjbaanb

@gbjbaanb:「カプセル化がパブリックメンバーよりも中断されない」というのは、「派生クラスのみがクラスの状態を使用できる」とは異なります。「保護された」とは、パブリックとプライベートの中間です。したがって、「保護された[...]は、カプセル化をいくらか壊します」は依然として当てはまります...
paercebal 2008年

実際、C ++言語では、std :: stackのようなコンテナーアダプターは、 "c"と呼ばれる保護された変数を使用して、基になるコンテナーオブジェクトを公開します。
Johannes Schaub-litb 2008

私はこの投稿がかなり古いことを知っていますが、私はそれに応える必要があると感じています。protectedはカプセル化されていませんpublic。私は間違って証明されても構わないと思っています。あなたがしなければならないのは、保護されたメンバーでクラスを書いて、私がそれを修正するのを禁止することだけです。明らかにクラスは非ファイナルでなければなりません、なぜならプロテクトを使用するすべてのポイントは継承のためです。何かがカプセル化されているか、カプセル化されていません。中間の状態はありません。
Taekahn

3

要するに、はい。

保護されたメンバー変数を使用すると、同じパッケージ内のすべてのクラスだけでなく、すべてのサブクラスから変数にアクセスできます。これは、特に読み取り専用データの場合に非常に役立ちます。ただし、保護されたメンバー変数の使用は、プライベートメンバー変数といくつかのゲッターとセッターを使用してレプリケートできるため、これらが必要になることはないと思います。


1
逆に、プライベートメンバー変数も必要ありません。どんな用途でもpublicで十分です。
アリス、

3

記録のために、「Exceptional C ++」の項目24の脚注の1つで、Sutterは「パブリックまたは保護されたメンバー変数を持つクラスを決して記述しないでしょう?(一部のライブラリーによって設定された悪い例に関係なく) 。)」


2

.Netアクセス修飾子の詳細については、こちらをご覧ください

保護されたメンバー変数には実際の利点や欠点はありません。特定の状況で何が必要かという問題です。一般に、メンバー変数をプライベートとして宣言し、プロパティを介して外部アクセスを可能にすることは慣例として受け入れられています。また、一部のツール(一部のO / Rマッパーなど)は、オブジェクトデータがプロパティによって表されることを期待し、パブリックまたは保護されたメンバー変数を認識しません。ただし、サブクラス(およびサブクラスのみ)が特定の変数にアクセスすることを知っている場合は、保護されていると宣言しない理由はありません。


サブクラスがしたいのアクセス変数と、それらを自由にすることができるようにしたいとは非常に異なっている変異させ、それを。これは、保護された変数に対する主な引数の1つです。派生クラスは保護されたメンバーに対して絶対に何でもできるので、基本クラスはその不変条件のいずれも保持することはできません。それが彼らに対する主な議論です。データにアクセスする必要があるだけなら、アクセサを作成します。:P(私はプロテクト変数を使用していますが、おそらく必要以上に多く、削減するつもりです!)
underscore_d
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.