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.