Je me demandais ce qui inciterait un programmeur à choisir l'idiome Pimpl ou la classe virtuelle pure et l'héritage.
Je comprends que l'idiome pimpl est livré avec une indirection supplémentaire explicite pour chaque méthode publique et la surcharge de création d'objet.
La classe virtuelle Pure en revanche est livrée avec une indirection implicite (vtable) pour l'implémentation héréditaire et je comprends qu'aucune surcharge de création d'objet.
EDIT : Mais vous auriez besoin d'une usine si vous créez l'objet de l'extérieur
Qu'est-ce qui rend la classe virtuelle pure moins désirable que l'idiome pimpl?
c++
abstract-class
pimpl-idiom
Arkaitz Jimenez
la source
la source
Réponses:
Lors de l'écriture d'une classe C ++, il convient de se demander si elle va être
Un type de valeur
Copie par valeur, l'identité n'a jamais d'importance. Il est approprié que ce soit une clé dans un std :: map. Exemple, une classe "chaîne", ou une classe "date", ou une classe "nombre complexe". "Copier" des instances d'une telle classe a du sens.
Un type d'entité
L'identité est importante. Toujours passé par référence, jamais par "valeur". Souvent, cela n'a aucun sens de "copier" des instances de la classe. Quand cela a du sens, une méthode polymorphe "Clone" est généralement plus appropriée. Exemples: une classe Socket, une classe Database, une classe "policy", tout ce qui serait une "fermeture" dans un langage fonctionnel.
PImpl et la classe de base abstraite pure sont des techniques pour réduire les dépendances de temps de compilation.
Cependant, je n'utilise pImpl que pour implémenter des types Value (type 1), et seulement parfois lorsque je veux vraiment minimiser les dépendances de couplage et de compilation. Souvent, cela ne vaut pas la peine. Comme vous le faites remarquer à juste titre, il y a plus de surcharge syntaxique car vous devez écrire des méthodes de transfert pour toutes les méthodes publiques. Pour les classes de type 2, j'utilise toujours une classe de base abstraite pure avec des méthodes de fabrique associées.
la source
Pointer to implementation
consiste généralement à masquer les détails de mise en œuvre structurelle.Interfaces
concernent l'instanciation de différentes implémentations. Ils servent vraiment deux objectifs différents.la source
L'idiome pimpl vous aide à réduire les dépendances et les temps de construction, en particulier dans les grandes applications, et minimise l'exposition de l'en-tête des détails d'implémentation de votre classe à une unité de compilation. Les utilisateurs de votre classe ne devraient même pas avoir besoin d'être conscients de l'existence d'un bouton (sauf en tant que pointeur cryptique dont ils ne sont pas au courant!).
Les classes abstraites (virtuels purs) sont quelque chose dont vos clients doivent être conscients: si vous essayez de les utiliser pour réduire le couplage et les références circulaires, vous devez ajouter un moyen de leur permettre de créer vos objets (par exemple via des méthodes d'usine ou des classes, injection de dépendance ou autres mécanismes).
la source
Je cherchais une réponse pour la même question. Après avoir lu quelques articles et un peu de pratique, je préfère utiliser des "interfaces de classe virtuelle pures" .
Le seul inconvénient ( j'essaie d'enquêter à ce sujet ) est que l'idiome pimpl pourrait être plus rapide
la source
Je déteste les boutons! Ils font la classe moche et illisible. Toutes les méthodes sont redirigées vers Pimple. Vous ne voyez jamais dans les en-têtes, quelles fonctionnalités a la classe, donc vous ne pouvez pas la refactoriser (par exemple changer simplement la visibilité d'une méthode). La classe se sent comme "enceinte". Je pense que l'utilisation d'iterfaces est meilleure et vraiment suffisante pour cacher l'implémentation au client. Vous pouvez éventuellement laisser une classe implémenter plusieurs interfaces pour les maintenir minces. Il faut préférer les interfaces! Remarque: vous n'avez pas nécessairement besoin de la classe d'usine. Il est important que les clients de classe communiquent avec ses instances via l'interface appropriée. La dissimulation de méthodes privées me semble une étrange paranoïa et je ne vois pas de raison à cela puisque nous avons des interfaces.
la source
Il y a un problème très réel avec les bibliothèques partagées que l'idiome pimpl contourne parfaitement que les virtuels purs ne peuvent pas: vous ne pouvez pas modifier / supprimer en toute sécurité les membres de données d'une classe sans forcer les utilisateurs de la classe à recompiler leur code. Cela peut être acceptable dans certaines circonstances, mais pas par exemple pour les bibliothèques système.
Pour expliquer le problème en détail, considérez le code suivant dans votre bibliothèque / en-tête partagé:
Le compilateur émet du code dans la bibliothèque partagée qui calcule l'adresse de l'entier à initialiser pour être un certain décalage (probablement zéro dans ce cas, car c'est le seul membre) du pointeur vers l'objet A qu'il sait être
this
.Du côté utilisateur du code, a
new A
allouera d'abord dessizeof(A)
octets de mémoire, puis remettra un pointeur vers cette mémoire auA::A()
constructeur sous la formethis
.Si dans une révision ultérieure de votre bibliothèque vous décidez de supprimer l'entier, de le rendre plus grand, plus petit ou d'ajouter des membres, il y aura un décalage entre la quantité de mémoire allouée par le code de l'utilisateur et les décalages attendus par le code du constructeur. Le résultat probable est un crash, si vous avez de la chance - si vous êtes moins chanceux, votre logiciel se comporte bizarrement.
En pimpl'ing, vous pouvez ajouter et supprimer en toute sécurité des membres de données à la classe interne, car l'allocation de mémoire et l'appel du constructeur se produisent dans la bibliothèque partagée:
Tout ce que vous avez à faire maintenant est de garder votre interface publique exempte de membres de données autres que le pointeur vers l'objet d'implémentation, et vous êtes à l'abri de cette classe d'erreurs.
Edit: Je devrais peut-être ajouter que la seule raison pour laquelle je parle du constructeur ici est que je ne voulais pas fournir plus de code - la même argumentation s'applique à toutes les fonctions qui accèdent aux données membres.
la source
class A_impl *impl_;
Nous ne devons pas oublier que l'héritage est un couplage plus fort et plus étroit que la délégation. Je prendrais également en compte toutes les questions soulevées dans les réponses données au moment de décider quels idiomes de conception utiliser pour résoudre un problème particulier.
la source
Bien que largement couvert dans les autres réponses, je peux peut-être être un peu plus explicite sur un avantage de pimpl par rapport aux classes de base virtuelles:
Une approche pimpl est transparente du point de vue de l'utilisateur, ce qui signifie que vous pouvez par exemple créer des objets de la classe sur la pile et les utiliser directement dans des conteneurs. Si vous essayez de masquer l'implémentation à l'aide d'une classe de base virtuelle abstraite, vous devrez renvoyer un pointeur partagé vers la classe de base à partir d'une fabrique, ce qui complique son utilisation. Considérez le code client équivalent suivant:
la source
À ma connaissance, ces deux choses servent des objectifs complètement différents. Le but de l'idiome Pimple est essentiellement de vous donner une idée de votre implémentation afin que vous puissiez faire des choses comme des échanges rapides pour un tri.
Le but des classes virtuelles est plutôt de permettre le polymorphisme, c'est-à-dire que vous avez un pointeur inconnu vers un objet d'un type dérivé et lorsque vous appelez la fonction x, vous obtenez toujours la bonne fonction pour la classe sur laquelle le pointeur de base pointe réellement.
Des pommes et des oranges vraiment.
la source
Le problème le plus ennuyeux de l'idiome pimpl est qu'il rend extrêmement difficile la maintenance et l'analyse du code existant. Donc, en utilisant pimpl, vous payez avec le temps et la frustration du développeur uniquement pour "réduire les dépendances et les temps de construction et minimiser l'exposition de l'en-tête des détails d'implémentation". Décidez-vous si cela en vaut vraiment la peine.
En particulier, les "temps de construction" sont un problème que vous pouvez résoudre par un meilleur matériel ou en utilisant des outils comme Incredibuild (www.incredibuild.com, également déjà inclus dans Visual Studio 2017), n'affectant ainsi pas la conception de votre logiciel. La conception du logiciel doit être généralement indépendante de la façon dont le logiciel est construit.
la source