Avant la norme C++11, lorsqu'il était nécessaire d'écrire plusieurs constructeurs avec des paramètres différents, il fallait doubler le code d'initialisation dans chacun d'eux, car il n'était pas possible d'appeler un autre constructeur à l'intérieur des constructeurs de classe.
L'impossibilité d'utiliser déjà la logique d'initialisation existante dans d'autres constructeurs conduisait à une duplication de code et à des erreurs lors de la modification de la logique d'initialisation : en mettant à jour un constructeur, il était facile d'oublier les autres.
Avec la sortie de C++11, un mécanisme de "constructeurs délégués" a été introduit, permettant à un constructeur de classe d'appeler un autre constructeur de la même classe via la syntaxe de la liste d'initialisation.
Exemple de code :
class Widget { public: Widget() : Widget(0, "défaut") {} Widget(int n) : Widget(n, "utilisateur") {} Widget(int n, std::string name) : size(n), label(name) {} private: int size; std::string label; };
Maintenant, toute la logique commune est concentrée dans un "constructeur principal", tout le reste délègue l'appel.
Caractéristiques clés :
Peut-on déléguer l'appel à un autre constructeur en appelant une fonction à l'intérieur du corps du constructeur ?
Non. L'appel d'un constructeur à partir d'un autre constructeur est possible uniquement via la liste d'initialisation, et non à l'intérieur du corps du constructeur. Si une fonction est appelée à partir du corps du constructeur, les membres de l'objet seront déjà initialisés.
Que se passe-t-il si nous créons un "cercle" de délégation entre les constructeurs ?
La délégation en boucle (par exemple, A délègue à B, et B à A) entraîne une erreur de compilation C++ : il ne peut pas y avoir de récursion infinie entre les constructeurs.
Peut-on déléguer l'appel au constructeur de la classe de base à l'aide d'un constructeur délégué de la classe dérivée ?
Non. Les constructeurs délégués fonctionnent uniquement dans une seule classe. Pour appeler le constructeur de base, une syntaxe séparée est utilisée :
class Base { public: Base(int) {} }; class Derived : public Base { public: Derived() : Base(42) {} };
Trois constructeurs, chacun initialisant explicitement des champs, la logique n'est pas synchronisée — une modification est apportée uniquement à un constructeur, les deux autres sont incorrects.
Avantages :
Inconvénients :
Un constructeur principal, les autres lui délèguent, toute la logique d'initialisation est centralisée.
Avantages :
Inconvénients :