ProgrammationDéveloppeur C++

Qu'est-ce qu'un constructeur virtuel (Virtual Constructor) en C++ ? Comment créer des objets de classes dérivées si leur type est inconnu au moment de la compilation ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

En C++, il n'existe pas de notion de 'constructeur virtuel' au sens strict, cependant, la nécessité de créer des instances d'objets de classes dérivées avec un type connu uniquement au moment de l'exécution se présente souvent. Historiquement, un analogue à cette tâche est résolu par le modèle "constructeur virtuel" via des fonctions virtuelles — généralement "clone()" ou "create()".

Historique de la question: En C++, depuis ses premières versions, il y a eu une limitation : un constructeur ne peut pas être déclaré comme virtuel. Cela dit, parfois, dans les hiérarchies de classes, il est nécessaire de créer de nouveaux objets à partir d'un existant (ou de connaître le type uniquement au moment de l'exécution).

Problème : Classiquement, les constructeurs ne respectent aucun mécanisme de fonctions virtuelles — l'appel est toujours résolu au moment de la compilation. Cela ne permet pas d'obtenir une "usine vivante" pour générer des objets avec leur véritable type d'exécution via le constructeur de la classe de base.

Solution : Il est recommandé de réaliser une fonction virtuelle dans la classe de base — généralement cela serait clone() (créer une copie de l'objet) ou create() (créer un objet du même type sans copier l'état).

Exemple de code :

class Base { public: virtual ~Base() {} virtual Base* clone() const = 0; }; class Derived : public Base { public: Derived(int v) : value(v) {} Base* clone() const override { return new Derived(*this); } private: int value; }; void process(const Base& b) { Base* b2 = b.clone(); // Créons la bonne copie via la méthode virtuelle delete b2; }

Caractéristiques clés :

  • Le constructeur virtuel n'est réalisé que via les modèles clone()/create().
  • Le constructeur lui-même ne peut pas être virtuel.
  • Nécessaire pour l'implémentation d'usines et de la copie des hiérarchies d'héritage.

Questions pièges.

Les constructeurs peuvent-ils être déclarés virtual en C++ ?

Non, la syntaxe C++ ne permet pas le spécificateur virtual pour le constructeur. Dans le cas contraire, le compilateur générera une erreur de compilation.

Si l'on déclare clone() soi-même, est-il obligatoire de le faire virtuel pur dans la classe de base ?

Non, ce n'est pas obligatoire. On peut donner une implémentation par défaut à clone(), par exemple, s'il est utile de copier seulement une partie de l'état ou de retourner nullptr, mais il est généralement fait en tant que fonction virtuel pure pour un meilleur contrôle.

Peut-on utiliser des méthodes statiques d'usine en remplacement de clone() ? Comment cela se rapporte-t-il à la virtualité ?

Les méthodes statiques d'usine ne sont pas virtuelles et ne sont pas redéfinies dans les dérivés par le mécanisme classique. Pour un véritable "constructeur virtuel", une fonction virtuelle d'instance est nécessaire ou un autre moyen de résolution dynamique de type.

Erreurs typiques et anti-modèles

  • Tentative de déclarer un constructeur comme virtual.
  • Inattention aux exceptions et à la mémoire lors de l'utilisation de clone() (des fuites peuvent survenir).
  • Absence de destructeur virtuel lors de la copie via clone().

Exemple de la vie réelle

Cas négatif

Un développeur a implémenté le modèle via une méthode statique :

class Base { public: static Base* create() { return new Base; } }; class Derived : public Base {}; // ... appelle Base::create(), retourne Base, perte d'information sur le type réel

Avantages :

  • Facile à réaliser

Inconvénients :

  • Perte de polymorphisme, Base::create() retourne toujours uniquement Base, impossible de créer Derived via l'interface

Cas positif

Le code utilise clone() :

class Base { public: virtual ~Base() {} virtual Base* clone() const = 0; }; class Derived : public Base { int x; public: Derived(int x) : x(x) {} Base* clone() const override { return new Derived(*this); } };

Avantages :

  • Le type est préservé, aucune information n'est perdue lors de la copie, le polymorphisme est maintenu

Inconvénients :

  • Nécessite une gestion prudente de la mémoire