Contrairement à l'héritage protégé, l'héritage privé C++ a trouvé sa place dans le courant dominant du développement C++. Cependant, je n'ai toujours pas trouvé une bonne utilisation pour lui.
Quand l'utilisez-vous ?
Contrairement à l'héritage protégé, l'héritage privé C++ a trouvé sa place dans le courant dominant du développement C++. Cependant, je n'ai toujours pas trouvé une bonne utilisation pour lui.
Quand l'utilisez-vous ?
Je l'utilise tout le temps. Voici quelques exemples qui me viennent à l'esprit :
Un exemple typique est la dérivation privée d'un conteneur STL :
class MyVector : private vector<int>
{
public:
// Using declarations expose the few functions my clients need
// without a load of forwarding functions.
using vector<int>::push_back;
// etc...
};
@Krsna : En fait, je ne le pense pas. Il n'y a qu'une seule raison ici : la paresse, à part la dernière, qui serait plus délicate à contourner.
Pas vraiment de la paresse (à moins que vous ne le vouliez dans le bon sens). Cela permet de créer de nouvelles surcharges de fonctions qui ont été exposées sans travail supplémentaire. Si en C++1x on ajoute 3 nouvelles surcharges à push_back
, MyVector
les reçoit gratuitement.
Note après l'acceptation de la réponse : Il ne s'agit PAS d'une réponse complète. Lisez d'autres réponses comme aquí (sur le plan conceptuel) et aquí (à la fois théoriques et pratiques) si la question vous intéresse. Il s'agit simplement d'un tour de passe-passe qui peut être réalisé avec l'héritage privé. Bien que ce soit fantaisie ce n'est pas la réponse à la question.
Outre l'utilisation de base de l'héritage privé présenté dans la FAQ C++ (dont le lien figure dans les commentaires d'autres personnes), vous pouvez utiliser une combinaison d'héritage privé et virtuel pour joint une classe (dans la terminologie .NET) ou pour faire d'une classe final (dans la terminologie Java). Ce n'est pas une utilisation courante, mais j'ai quand même trouvé cela intéressant :
class ClassSealer {
private:
friend class Sealed;
ClassSealer() {}
};
class Sealed : private virtual ClassSealer
{
// ...
};
class FailsToDerive : public Sealed
{
// Cannot be instantiated
};
Scellé peut être instancié. Il dérive de ClassSealer et peut appeler le constructeur privé directement puisqu'il s'agit d'un ami.
FailsToDerive ne compilera pas car il doit appeler la fonction ClassSealer directement (exigence de l'héritage virtuel), mais il ne peut pas le faire car il est privé dans la classe Scellé et dans ce cas FailsToDerive n'est pas un ami de ClassSealer .
EDIT
Il a été mentionné dans les commentaires que cela ne pouvait pas être rendu générique à ce moment-là en utilisant le CRTP. La norme C++11 supprime cette limitation en fournissant une syntaxe différente pour les arguments de template :
template <typename T>
class Seal {
friend T; // not: friend class T!!!
Seal() {}
};
class Sealed : private virtual Seal<Sealed> // ...
Bien sûr, tout cela est discutable, puisque C++11 fournit une fonction final
mot-clé contextuel dans ce but précis :
class Sealed final // ...
Question : si nous n'utilisions pas l'héritage virtuel, alors FailsToDerive compilerait. Est-ce exact ?
+1. @Sasha : Correct, l'héritage virtuel est nécessaire car la classe la plus dérivée appelle toujours directement les constructeurs de toutes les classes virtuellement héritées, ce qui n'est pas le cas avec l'héritage ordinaire.
L'utilisation canonique de l'héritage privé est la relation "implémenté en termes de" (merci à Scott Meyers's 'Effective C++' pour cette formulation). En d'autres termes, l'interface externe de la classe héritière n'a aucune relation (visible) avec la classe héritée, mais elle l'utilise en interne pour implémenter sa fonctionnalité.
Il peut être utile de mentionner l'une des raisons pour lesquelles elle est utilisée dans ce cas : Cela permet d'effectuer l'optimisation de la classe de base vide, ce qui ne se produira pas si la classe avait été un membre au lieu d'une classe de base.
Son utilisation principale est de réduire la consommation d'espace là où cela est vraiment important, par exemple dans les classes de chaînes contrôlées par des politiques ou dans les paires compressées. en fait, boost::compressed_pair utilisait l'héritage protégé.
Jalf : Hé, je n'avais pas réalisé ça. Je pensais que l'héritage non public était principalement utilisé comme un hack lorsque vous avez besoin d'accéder aux membres protégés d'une classe. Je me demande pourquoi un objet vide prendrait de la place quand on utilise la composition. Probablement pour l'adressabilité universelle...
Une utilisation utile de l'héritage privé est lorsque vous avez une classe qui implémente une interface, qui est ensuite enregistrée avec un autre objet. Vous rendez cette interface privée afin que la classe elle-même doive s'enregistrer et que seul l'objet spécifique avec lequel elle est enregistrée puisse utiliser ces fonctions.
Par exemple :
class FooInterface
{
public:
virtual void DoSomething() = 0;
};
class FooUser
{
public:
bool RegisterFooInterface(FooInterface* aInterface);
};
class FooImplementer : private FooInterface
{
public:
explicit FooImplementer(FooUser& aUser)
{
aUser.RegisterFooInterface(this);
}
private:
virtual void DoSomething() { ... }
};
Par conséquent, la classe FooUser peut appeler les méthodes privées de FooImplementer par le biais de l'interface FooInterface, alors que les autres classes externes ne le peuvent pas. Il s'agit d'un excellent modèle pour gérer les rappels spécifiques qui sont définis comme des interfaces.
@Daemin, pourquoi personne ne marque FooInterface comme abstraite ? Les interfaces sont généralement abstraites n'est-ce pas.
@SouravKannanthaB Je ne sais pas ce que vous voulez dire par là. Il n'y a pas de mot-clé abstrait en C++.
Je pense que la section critique de la FAQ C++ Lite es:
Une utilisation légitime et à long terme de l'héritage privé est lorsque vous voulez construire une classe Fred qui utilise du code dans une classe Wilma, et que le code de la classe Wilma doit invoquer des fonctions membres de votre nouvelle classe Fred. Dans ce cas, Fred appelle des fonctions non virtuelles dans Wilma, et Wilma appelle (généralement des fonctions virtuelles pures) dans elle-même, qui sont surchargées par Fred. Cela serait beaucoup plus difficile à faire avec la composition.
En cas de doute, il faut préférer la composition à l'héritage privé.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.