Question

Si je comprends bien, l’idiome de pimpl n’existe que parce que C ++ vous oblige à placer tous les membres de la classe privée dans l’en-tête. Si l'en-tête ne devait contenir que l'interface publique, en théorie, aucun changement dans l'implémentation de la classe n'aurait nécessité une recompilation pour le reste du programme.

Ce que je veux savoir, c'est pourquoi le C ++ n'est pas conçu pour permettre un tel confort. Pourquoi demande-t-il que les parties privées d'une classe soient affichées ouvertement dans l'en-tête (sans jeu de mots)?

Était-ce utile?

La solution

Cela concerne la taille de l'objet. Le fichier h est utilisé, entre autres, pour déterminer la taille de l'objet. Si les membres privés n'y sont pas indiqués, vous ne sauriez pas quelle est la taille d'un objet à nouveau.

Vous pouvez toutefois simuler votre comportement souhaité en procédant comme suit:

class MyClass
{
public:
   // public stuff

private:
#include "MyClassPrivate.h"
};

Ceci n’impose pas le comportement, mais extrait le contenu privé du fichier .h. En revanche, cela ajoute un autre fichier à gérer. De plus, dans Visual Studio, l’intellisense ne fonctionne pas pour les membres privés - cela pourrait être un avantage ou un inconvénient.

Autres conseils

Je pense qu'il y a une confusion ici. Le problème ne concerne pas les en-têtes. Les en-têtes ne font rien (ce sont juste des moyens d'inclure des morceaux de texte source communs parmi plusieurs fichiers de code source).

Le problème, même s’il en existe un, c’est que les déclarations de classe en C ++ doivent tout définir, qu’elles soient publiques ou privées, qu’une instance doit avoir pour fonctionner. (Il en va de même pour Java, mais la façon dont la référence aux classes compilées en externe fonctionne rend inutile l’utilisation de quelque chose comme les en-têtes partagés.)

C’est dans la nature des technologies orientées objet courantes (pas uniquement C ++) qu’une personne a besoin de connaître la classe concrète utilisée et comment utiliser son constructeur pour livrer une implémentation, même si vous utilisez uniquement la classe. parties publiques. L'appareil de (3, ci-dessous) le cache. La pratique de (1, ci-dessous) sépare les problèmes, que vous le fassiez (3) ou non.

  1. Utilisez des classes abstraites qui définissent uniquement les parties publiques, principalement des méthodes, et laissez la classe d'implémentation hériter de cette classe abstraite. Donc, en utilisant la convention habituelle pour les en-têtes, il y a un abstract.hpp qui est partagé autour. Il existe également un fichier implementation.hpp qui déclare la classe héritée et qui est uniquement transmis aux modules qui implémentent les méthodes de cette implémentation. Le fichier implementation.hpp sera #include " abstract.hpp " à utiliser dans la déclaration de classe qu'il fait, de sorte qu'il n'y ait qu'un seul point de maintenance pour la déclaration de l'interface abstraite.

  2. Maintenant, si vous voulez imposer le masquage de la déclaration de classe d'implémentation, vous devez avoir un moyen de demander la construction d'une instance concrète sans posséder la déclaration de classe complète spécifique: vous ne pouvez pas utiliser new et vous ne peut pas utiliser les instances locales. (Vous pouvez toutefois supprimer.) L’introduction de fonctions d’aide (y compris des méthodes sur d’autres classes qui fournissent des références à des instances de classe) est la solution de remplacement.

  3. Avec les fichiers d'en-tête utilisés comme définition partagée pour la classe / interface abstraite, incluez des signatures de fonction pour les fonctions d'assistance externes. Ces fonctions doivent être implémentées dans des modules faisant partie des implémentations de classes spécifiques (afin de voir la déclaration de classe complète et d’exercer le constructeur). La signature de la fonction d'assistance ressemble probablement beaucoup à celle du constructeur, mais elle renvoie une référence d'instance en conséquence (ce constructeur proxy peut renvoyer un pointeur NULL et peut même renvoyer des exceptions si vous aimez ce genre de chose). La fonction d'assistance construit une instance d'implémentation particulière et la renvoie comme référence à une instance de la classe abstraite.

Mission accomplie.

Oh, la recompilation et la reconnexion doivent fonctionner comme vous le souhaitez, en évitant la recompilation des modules appelants lorsque seule l'implémentation change (car le module appelant n'effectue plus aucune allocation de stockage pour les implémentations).

Vous ignorez tous le but de la question -

Pourquoi le développeur doit-il taper le code PIMPL?

Pour moi, la meilleure réponse que je puisse trouver est que nous n’avons pas le bon moyen d’exprimer du code C ++ qui vous permette de l’exploiter. Par exemple, une réflexion à la compilation (ou un pré-processeur, ou autre) ou un code DOM.

C ++ a absolument besoin que l'un ou les deux de ces logiciels soient disponibles pour un développeur pour la méta-programmation.

Ensuite, vous pourriez écrire quelque chose comme ceci dans votre MyClass.h publique:

#pragma pimpl(MyClass_private.hpp)

Et écrivez ensuite votre propre générateur de wrapper vraiment trivial.

Quelqu'un aura une réponse beaucoup plus verbeuse que moi, mais la réponse rapide est double: le compilateur doit connaître tous les membres d'une structure pour déterminer les besoins en espace de stockage, et le compilateur doit connaître l'ordre de ces membres à générer des compensations de manière déterministe.

Le langage est déjà assez compliqué; Je pense qu'un mécanisme permettant de scinder les définitions de données structurées dans le code serait un peu une calamité.

En règle générale, j'ai toujours vu les classes de règles utilisées pour définir le comportement de la mise en œuvre dans une Manière Pimpl. Je pense que l'utilisation d'un modèle de politique présente certains avantages supplémentaires: il est plus facile d'échanger des implémentations, de combiner facilement plusieurs implémentations partielles en une seule unité, ce qui vous permet de diviser le code d'implémentation en unités fonctionnelles, réutilisables, etc.

Peut-être parce que la taille de la classe est requise pour passer son instance par valeurs, l'agréger dans d'autres classes, etc.?

Si C ++ ne supportait pas la sémantique des valeurs, cela aurait été bien, mais c'est le cas.

Oui, mais ...

Vous devez lire le document "Conception et évolution de C ++" de Stroustrup. livre. Il aurait inhibé l'absorption de C ++.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top