Quando usar a inheritance particular de C ++ sobre composição?

Você pode me dar um exemplo concreto quando é preferível usar a inheritance privada sobre a composição? Pessoalmente, vou usar composição sobre inheritance privada, mas pode haver o caso de usar inheritance privada é a melhor solução para um problema específico. Lendo o C ++ faq , dá-lhe um exemplo sobre o uso de inheritance privada, mas parece mais fácil usar o padrão de composição + estratégia ou até mesmo a inheritance pública do que a inheritance privada.

private inheritance private é normalmente usada para representar “implementados em termos de”. O uso principal que tenho visto é para mixins usando inheritance múltipla privada para construir um object filho com a funcionalidade adequada dos vários pais mixin. Isso também pode ser feito com a composição (que eu prefiro um pouco), mas o método de inheritance permite que você use para expor alguns methods pai publicamente e permite uma notação um pouco mais conveniente ao usar os methods mixin.

Scott Meyers no item “Efetivo C ++” 42 diz

“Somente a inheritance dá access a membros protegidos, e somente a inheritance permite que funções virtuais sejam redefinidas. Como existem funções virtuais e membros protegidos, a inheritance privada é às vezes a única maneira prática de expressar uma relação de implementação em termos de termos entre aulas. ”

Interfaces de inheritance privada

Uma aplicação típica de inheritance privada que muitas pessoas ignoram é a seguinte.

 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; }; 

Normalmente BigOne seria uma class com muitas responsabilidades. Para modularizar seu código, você dividiria seu código em componentes, o que ajudaria a fazer as pequenas coisas. Esses componentes não devem ser amigos do BigOne , mas ainda assim eles podem precisar de algum access à sua class, que você não deseja fornecer ao público, porque são detalhes de implementação. Portanto, você cria uma interface para esse componente para fornecer esse access restrito. Isso torna seu código mais fácil de manter e de raciocinar, porque as coisas têm limites claros de access.

Eu usei essa técnica muito em um projeto de vários anos e isso valeu a pena. Composição não é uma alternativa aqui.

Deixando o compilador gerar um construtor de cópia parcial e atribuição

Às vezes, existem classs copiáveis ​​/ móveis que possuem muitos membros de dados diferentes. O construtor e a atribuição de cópia ou movimentação gerados pelo compilador ficariam bem, exceto por um ou dois membros de dados que precisam de tratamento especial. Isso pode ser chato, se os membros de dados forem adicionados, removidos ou alterados com frequência, já que os construtores e atribuições de cópia e movimentação manuais precisam ser atualizados sempre. Ele produz código-bloat e dificulta a manutenção da class.

A solução é encapsular os membros de dados, cujas operações de copiar e mover podem ser geradas pelo compilador em uma struct extra ou class da qual você herda em particular.

 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; } }; 

Em seguida, você pode usar as operações geradas pelo compilador da class MyClassImpl na implementação das respectivas operações da class em que está interessado. Você poderia fazer o mesmo com a composição, mas isso faria com que seu código se tornasse desagradável no restante de sua class. Se você usou composição, o restante da implementação teria que sofrer devido a esse detalhe de implementação das operações de cópia e movimentação. A inheritance privada evita isso e evita muita repetição de código.