重要な点は、他のオブジェクトprotected
のメンバーではなく、メンバーの独自のコピーへのアクセスを許可することです。これはよくある誤解です。多くの場合、派生型へのメンバーへのアクセスを一般化し、状態を付与します (自身のベースに対してのみ明示的に述べずに...)。protected
これには理由があります。他のオブジェクトが依存している不変条件を壊す可能性があるため、通常、階層の別の分岐にあるメンバーにアクセスしないでください。いくつかの大規模なデータ メンバー (保護されている) に対してコストのかかる計算を実行する型と、異なる戦略に従って結果をキャッシュする 2 つの派生型を考えてみましょう。
class base {
protected:
LargeData data;
// ...
public:
virtual int result() const; // expensive calculation
virtual void modify(); // modifies data
};
class cache_on_read : base {
private:
mutable bool cached;
mutable int cache_value;
// ...
virtual int result() const {
if (cached) return cache_value;
cache_value = base::result();
cached = true;
}
virtual void modify() {
cached = false;
base::modify();
}
};
class cache_on_write : base {
int result_value;
virtual int result() const {
return result_value;
}
virtual void modify() {
base::modify();
result_value = base::result();
}
};
型はデータへのcache_on_read
変更をキャプチャし、結果を無効としてマークするため、値の次の読み取りで再計算されます。これは、要求に応じて計算を実行するだけなので、書き込み数が比較的多い場合に適したアプローチです (つまり、複数の変更によって再計算がトリガーされることはありません)。これcache_on_write
は、書き込みの数が少なく、読み取りの決定論的なコストが必要な場合に適した戦略となる可能性があります (読み取りの低レイテンシーを考えてください)。
さて、元の問題に戻ります。どちらのキャッシュ戦略も、ベースよりも厳密な一連の不変条件を維持します。最初のケースでは、追加の不変条件はcached
、最後の読み取り後に変更されていないtrue
場合のみです。data
2 番目のケースでは、余分な不変条件は、result_value
常に操作の値です。
3 番目の派生型が a への参照を取得し、(許可されている場合) 書き込みbase
にアクセスすると、派生型の不変条件が壊れます。data
protected
そうは言っても、その特定の結果を達成するためにバックドアを残すため、言語の仕様は壊れています(個人的な意見)。特に、派生型のベースからメンバーのメンバーへのポインターを作成する場合、アクセスは にチェックインされますderived
が、返されるポインターは のメンバーへのポインターであり、任意のオブジェクトbase
に適用できます。 base
class base {
protected:
int x;
};
struct derived : base {
static void modify( base& b ) {
// b.x = 5; // error!
b.*(&derived::x) = 5; // allowed ?!?!?!
}
}