effective C++ノートの条項41、42:継承とテンプレートを区別し、プライベート継承を賢明に使用する


条項41:継承とテンプレートの区分
lテンプレートクラスの特徴:動作はタイプに依存しない
lオブジェクトのタイプがクラス内の関数の動作に影響しない場合、テンプレートを使用してクラスのセットを生成します.(スタックタイプ)
スタックタイプでは、スタック内のメンバータイプTについて何も知らなくても、各メンバー関数を書くことができるので、stackの動作はどこでもTに依存しません.
lオブジェクトのタイプがクラス内の動作に影響する場合、継承を使用してこのようなクラスのセットを得る.(猫のタイプ)
猫ごとに食べる方法と寝る方法があるからです.これは、異なる猫ごとに異なる行為を実現しなければならないことを意味します.すべての猫を処理するために関数を書くことはできません.できることは関数インタフェースを作成することだけで、すべての猫がそれを実現しなければなりません.したがって、ベースクラスに純粋な虚関数を宣言してインタフェースを作成します.
 
条項42:プライベート継承を賢明に使用
lプライベート継承動作:第1、公有継承とは逆に、2つのクラス間の継承関係がプライベートである場合、コンパイラは派生クラスオブジェクトをベースクラスオブジェクトに変換しません.第二に、プライベート・ベース・クラスから継承されたメンバーは、ベース・クラスで保護または共有メンバーであっても派生クラスのプライベート・メンバーになります.
lプライベート継承は「...で実現する」ことを意味する.Dプライベート継承をBに使用する場合は、タイプBのオブジェクトとタイプDのオブジェクトの間に概念的な関係があるためではなく、クラスBにすでに存在するいくつかのコードを利用したいからです.従って、プライベート継承は実装技術である.プライベート継承は、継承実装のみを意味し、インタフェースは無視されます.DプライベートがBに継承されると,Dオブジェクトが実装にBオブジェクトを用いたというだけである.プライベート継承はソフトウェア設計の過程で意味がなく、ソフトウェア実装時に役立つだけです.
lテンプレートによる「コード膨張」は良いことではありません.1つのテンプレートを100回インスタンス化すると、そのテンプレートのコードが100回インスタンス化される可能性があります.クラスによっては、汎用ポインタ(void*)を使用して回避できます.この方法を用いたクラスは,オブジェクトではなくポインタを格納する.以下に示すStackクラス:
class GenericStack
{
public:
         GenericStack();
         ~GenericStack();
         void push(void *object);
         void *pop();
         boolempty() const;
private:
         structStackNode{
                   void*data;
                   StackNode*next;
                   StackNode(void*newData, StackNode *nextNode)
		: data(newData),next(nextNode) {}
                  };
	  StackNode *top;
};

タイプセキュリティを取得するには、次のようにGenericStackのインタフェースクラスを作成します.
         class IniStack
         {
         public:
                   void push(int *intPtr) { s.push(intPtr); }
                   int  *pop() { return static_cast<int*>(s.pop()); }
                   bool empty() const { return s.empty(); }
         private:
                   GenericStack s; //  
         };

しかし、一部のユーザーは、GenericStackの使用がより効率的であるか、タイプセキュリティが重要ではないと考え、直接GenericStackを使用すると、タイプエラーを簡単に犯すことができます.したがって、これを防止するために、プライベート継承クラスによって実現される.GenericStackは安全ではなく、他のクラスを実現するためにしか使用できないことを他の人に伝えることができます.具体的には、次のようにGenericStackのメンバー関数を保護タイプとして宣言します.
class GenericStack
{
protected::
         GenericStack();
         ~GenericStack();
         void push(void *object);
         void *pop();
         bool empty() const;
private:
         struct StackNode{
                   void*data;
                   StackNode*next;
                   StackNode(void *newData, StackNode *nextNode)
		: data(newData),next(nextNode) {}
                  };
                   StackNode *top;
};

この場合GenericStackを直接使用したい場合:
         GenericStack s;  //  !       
         class IntStack : private GenericStack
         {
         public:
                   void push(int *intPtr) {GenericStack::push(intPtr); }
                   int *pop() { return static_cast<int*>(GenericStack::pop()); }
                   bool empty() const { return GenericStack::empty();}
         };

階層化された方法と同様に、プライベート継承に基づく実装
コード重複を回避
ああ、このタイプの安全なインタフェースクラスにはペアしか含まれていないからです.
GenericStack
の双曲線コサインを返します.
lクラスメンバー関数がprotectedと宣言された場合、保護メンバーにアクセスできるのは継承のみであり、階層化は同じ効果を達成できません.虚関数(条項43に詳細が記載されている)と保護メンバーが存在するため、プライベート継承はクラス間の「...で実現される」関係を表現する唯一の有効な方法である場合がある.
l私有継承は「...で実現する」ことを意味し,階層化も同様の意味を持つ.プライベート継承は、階層を可能な限り使用するように選択し、必要に応じて使用します(保護メンバー/ダミー関数がある場合が多い).