Quand utiliser l’inheritance privé C ++ sur la composition?

Pouvez-vous me donner un exemple concret lorsqu’il est préférable d’utiliser l’inheritance privé plutôt que la composition? Personnellement, j’utiliserai la composition plutôt que l’inheritance privé, mais il se peut que l’utilisation de l’inheritance privé soit la meilleure solution à un problème particulier. La lecture de la FAQ C ++ vous donne un exemple d’utilisation de l’inheritance privé, mais il semble plus facile d’utiliser un modèle de composition + stratégie ou même un inheritance public que l’inheritance privé.

private inheritance private est généralement utilisé pour représenter “implémenté-en-terme-de”. L’utilisation principale que j’ai vue concerne les mixins utilisant l’inheritance multiple privé pour construire un object enfant avec les fonctionnalités appropriées des différents parents mixin. Cela peut également être fait avec la composition (ce que je préfère légèrement), mais la méthode d’inheritance NE vous permet pas d’utiliser une using pour exposer publiquement certaines méthodes mères et permet une notation légèrement plus pratique lorsque vous utilisez les méthodes mixin.

Scott Meyers dans “Effective C ++” au paragraphe 42 dit

“Seul l’inheritance donne access aux membres protégés, et seul l’inheritance permet de redéfinir les fonctions virtuelles. En raison de l’existence de fonctions virtuelles et de membres protégés, l’inheritance privé est parfois le seul moyen pratique d’exprimer une relation est implémentée en termes de Des classes.”

Héritage privé d’interfaces

Une application typique de l’inheritance privé que beaucoup de gens oublient est la suivante.

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

Généralement, BigOne serait une classe avec beaucoup de responsabilités. Afin de modulariser votre code, vous le diviseriez en composants, ce qui vous aiderait à faire le petit travail. Ces composants ne doivent pas être des amis de BigOne , mais ils peuvent quand même avoir besoin d’un access à votre classe, que vous ne voulez pas donner au public, car ce sont des détails d’implémentation. Par conséquent, vous créez une interface pour ce composant afin de fournir cet access restreint. Cela rend votre code plus facile à gérer et à raisonner, car les choses ont des limites d’access claires.

J’ai beaucoup utilisé cette technique dans un projet de plusieurs années et cela a porté ses fruits. La composition n’est pas une alternative ici.

Laisser le compilateur générer un constructeur de copie partiel et une affectation

Parfois, il existe des classes copiables / mobiles qui ont beaucoup de données membres différentes. Le constructeur de copie ou de déplacement généré par le compilateur et l’affectation conviendraient, sauf pour un ou deux membres de données nécessitant un traitement spécial. Cela peut être gênant si des membres de données sont ajoutés, supprimés ou modifiés fréquemment, car les constructeurs et les affectations de copie et de déplacement manuscrits doivent être mis à jour à chaque fois. Il produit du code-ballonnement et rend la classe plus difficile à maintenir.

La solution consiste à encapsuler les données membres, dont les opérations de copie et de déplacement peuvent être générées par le compilateur dans une struct ou une class supplémentaire à partir de laquelle vous héritez de manière privée.

 struct MyClassImpl { int i; float f; double d; char c; std::ssortingng s; // lots of data members which can be copied/moved by the // comstackr-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; } }; 

Vous pouvez ensuite utiliser les opérations générées par le compilateur de la classe MyClassImpl dans la mise en oeuvre des opérations respectives de la classe qui vous intéresse. Vous pouvez faire de même avec la composition, mais cela nuirait à votre code dans le rest de votre classe. Si vous utilisiez la composition, le rest de la mise en œuvre aurait à souffrir de ce détail de mise en œuvre des opérations de copie et de déplacement. L’inheritance privé évite cela et évite beaucoup de répétition de code.