4 votes

pointeur intelligent auto-instancié

Je cherche un moyen simple de réduire le couplage des en-têtes dans un projet C++, qui est principalement dû à la composition de classes (trop utilisée) qui nécessite bien sûr un type complet. Par exemple :

// header A
class A
{
  B b; // requires header B
};

J'ai également envisagé les interfaces et pimpl, mais tous deux impliquent un code de base que je ne veux pas écrire/supporter manuellement (ou existe-t-il un moyen de rendre cela automatique ?).

J'ai donc pensé à remplacer member par un pointeur et un forward comme class B* pB; mais cela nécessite de gérer la création et la suppression d'objets. Ok, je pourrais utiliser des pointeurs intelligents pour la suppression (pas des auto_ptr Cependant, comme il nécessite un type complet lors de la création, il faut dire quelque chose comme shared_ptr<class B> pB; ), mais comment faire avec la création d'objets maintenant ?

Je pourrais créer l'objet dans A comme le constructeur de pB = new B; mais c'est, encore une fois, manuel, et ce qui est pire, c'est qu'il pourrait y avoir plusieurs constructeurs... Je cherche donc un moyen de faire cela automatiquement, ce qui serait aussi simple que de changer B b; à autoobjptr<class B> pB; en A sans avoir à se préoccuper de la définition de pB l'instanciation.

Je suis presque sûr que cette idée n'est pas nouvelle, alors peut-être pourriez-vous me donner une référence à une solution commune ou à une discussion ?

UPDATE : Pour clarifier, je n'essaie pas de rompre la dépendance entre A y B mais je veux éviter l'inclusion de B lorsque l'on inclut A de l'autre. En pratique, B est utilisé dans la mise en œuvre de A Une solution typique consisterait donc à créer une interface ou un pimpl pour A mais je cherche quelque chose de plus facile pour le moment.

MISE À JOUR2 : J'ai soudain réalisé qu'un pointeur paresseux tel que celui qui est proposé aquí ferait l'affaire (dommage qu'il n'y ait pas d'implémentation standard de ceci dans boost par exemple), lorsqu'il est combiné avec un destructeur virtuel (pour permettre un type incomplet). Je ne comprends toujours pas pourquoi il n'y a pas de solution standard et j'ai l'impression de réinventer la roue...

UPDATE3 : Soudain, Sergey Tachenov est venu avec une solution très simple (la réponse acceptée), bien qu'il m'ait fallu une demi-heure pour comprendre pourquoi cela fonctionne vraiment... Si vous supprimez le constructeur A() ou si vous le définissez en ligne dans le fichier d'en-tête, la magie ne fonctionnera plus (erreur de compilation). Je suppose que lorsque vous définissez un constructeur explicite non-inline, la construction des membres (même ceux implicites) se fait dans la même unité de compilation (A.cpp) où le type B est terminée. En revanche, si votre A est en ligne, la création des membres doit se faire à l'intérieur d'autres unités de compilation et ne fonctionnera pas en tant que B est incomplète. C'est logique, mais je suis curieux de savoir si ce comportement est défini par la norme C++.

UPDATE4 : J'espère qu'il s'agira de la dernière mise à jour. Reportez-vous à la réponse acceptée et aux commentaires pour une discussion sur la question ci-dessus.

1voto

Didier Trosset Points 17949

Je suis presque sûr que cela peut être mis en œuvre de la même manière que unique_ptr est mis en œuvre. La différence réside dans le fait que le allocated_unique_ptr alloue par défaut l'objet B.

Notez toutefois que si vous souhaitez une construction automatique de l'objet B, il faut volonté est instancié avec la fonction constructeur par défaut .

0voto

rubenvb Points 27271

Vous avez vous-même donné la meilleure solution : utiliser des pointeurs, et new dans le constructeur... S'il y a plus d'un constructeur, répétez ce code à chaque fois. Vous pourriez créer une classe de base qui ferait cela pour vous, mais cela ne ferait que mystifier l'implémentation...

Avez-vous pensé à un modèle en class B ? Cela peut également résoudre les dépendances croisées des en-têtes, mais augmentera très probablement le temps de compilation... Ce qui nous amène à la raison pour laquelle vous essayez d'éviter ces #include s. Avez-vous mesuré le temps de compilation ? Est-il inquiétant ? Est-ce le problème ?

MISE À JOUR : exemple de modèle de manière :

// A.h
template<class T>
class A
{
public:
    A(): p_t( new T ) {}
    virtual ~A() { delete p_t }
private:
    T* p_t;
};

Encore une fois, cela n'augmentera probablement pas le temps de compilation ( B.h devra être introduit pour créer l'instance de modèle. A<B> ), il vous permet cependant de supprimer les inclusions dans l'en-tête A et le fichier source.

0voto

Puppy Points 90818

Vous pourriez écrire un pimpl_ptr<T> ce qui aurait pour effet d'ajouter, de supprimer et de copier automatiquement les T qu'il contient.

0voto

Le problème de votre approche est que si vous pouvez éviter d'inclure le fichier d'en-tête pour B de cette manière, cela ne réduit pas vraiment les dépendances.

Une meilleure façon de réduire les dépendances est de laisser B dériver d'une classe de base déclarée dans un fichier d'en-tête séparé, et d'utiliser un pointeur vers cette classe de base dans A. Vous devrez toujours créer manuellement le bon descendant (B) dans le constructeur de A, bien sûr.

Il est également très possible que la dépendance entre A et B soit réelle et dans ce cas, vous n'améliorez rien en évitant artificiellement d'inclure le fichier d'en-tête de B.

Prograide.com

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.

Powered by:

X