構成よりもプライベート継承を使用する方が望ましい具体例を教えてください。個人的には、プライベート継承よりもコンポジションを使用しますが、特定の問題に対してはプライベート継承を使用することが最善の解決策である場合があります。C++ faqを読むと、プライベート継承の使用例が得られますが、プライベート継承よりも構成 + 戦略パターンまたはパブリック継承を使用する方が簡単なようです。
3 に答える
「効果的なC ++」アイテム42のスコット・マイヤーズ 言います
「継承のみが保護されたメンバーへのアクセスを提供し、継承のみが仮想関数の再定義を可能にします。仮想関数と保護されたメンバーが存在するため、プライベート継承は、相互に実装された関係を表現する唯一の実用的な方法である場合があります。クラス。"
private
継承は通常、"implemented-in-term-of" を表すために使用されます。私が見た主な用途は、プライベート多重継承を使用して、さまざまな mixin の親から適切な機能を持つ子オブジェクトを構築する mixin です。これはコンポジションでも実行できます (私は少し好みます) が、継承メソッドを使用using
すると、一部の親メソッドをパブリックに公開することができ、mixin メソッドを使用するときに少し便利な表記が可能になります。
プライベートに継承するインターフェイス
多くの人が見落としている私的継承の典型的なアプリケーションは次のとおりです。
class InterfaceForComponent
{
public:
virtual ~InterfaceForComponent() {}
virtual doSomething() = 0;
};
class Component
{
public:
Component( InterfaceForComponent * bigOne ) : bigOne(bigOne) {}
/* ... more functions ... */
private:
InterfaceForComponent * bigOne;
};
class BigOne : private InterfaceForComponent
{
public:
BigOne() : component(this) {}
/* ... more functions ... */
private:
// implementation of InterfaceForComponent
virtual doSomething();
Component component;
};
通常BigOne
、多くの責任を持つクラスになります。コードをモジュール化するには、コードをコンポーネントに分割します。これは、小さな作業を行うのに役立ちます。これらのコンポーネントは友だちでBigOne
あってはなりませんが、実装の詳細であるため、公開したくないクラスへのアクセスが必要になる場合があります。したがって、この制限されたアクセスを提供するために、そのコンポーネントのインターフェースを作成します。これにより、アクセスの境界が明確になるため、コードの保守性と推論が向上します。
私はその技術を数人年のプロジェクトで頻繁に使用しましたが、それは成果を上げています。ここでは、構成は代替手段ではありません。
コンパイラーに部分的なコピーコンストラクターと割り当てを生成させる
場合によっては、多くの異なるデータメンバーを持つコピー可能/移動可能なクラスがあります。コンパイラが生成したコピーまたは移動コンストラクタと割り当ては、特別な処理が必要な1つまたは2つのデータメンバーを除いて、問題ありません。データメンバーが頻繁に追加、削除、または変更される場合、手書きのコピーおよび移動コンストラクターと割り当てを毎回更新する必要があるため、これは煩わしい場合があります。コードの膨張を引き起こし、クラスの保守を困難にします。
解決策は、データメンバーをカプセル化することです。データメンバーのコピーおよび移動操作は、コンパイラーで生成してエクストラに生成することも、struct
プライベートclass
に継承することもできます。
struct MyClassImpl
{
int i;
float f;
double d;
char c;
std::string s;
// lots of data members which can be copied/moved by the
// compiler-generated constructors and assignment operators.
};
class MyClass : private MyClassImpl
{
public:
MyClass( const MyClass & other ) : MyClassImpl( other )
{
initData()
}
MyClass( MyClass && other ) : MyClassImpl( std::move(other) )
{
initData()
}
// and so forth ...
private:
int * pi;
void initData()
{
pi = &p;
}
};
次に、コンパイラによって生成されたMyClassImpl
クラスの操作を、関心のあるクラスのそれぞれの操作の実装で使用できます。compositionでも同じことができますが、これにより、クラスの残りのコードが醜くなります。コンポジションを使用した場合、コピーおよび移動操作のこの実装の詳細のために、実装の残りの部分が苦しむ必要があります。プライベート継承はこれを回避し、多くのコードの繰り返しを回避します。