web-dev-qa-db-fra.com

Pourquoi les constructeurs ne peuvent-ils pas être appelés explicitement alors que les destructeurs le peuvent?

Dans le code C++ suivant, je suis autorisé à appeler explicitement le destructeur mais pas le constructeur. Pourquoi donc? Ne serait-ce pas un appel ctor explicite plus expressif et nifié avec le cas dtor?

class X { };

int main() {
  X* x = (X*)::operator new(sizeof(X));
  new (x) X;  // option #1: OK
  x->X();     // option #2: ERROR

  x->~X();
  ::operator delete(x);
}
28
Daniel Langr

Parce qu'avant le démarrage du constructeur, il n'y a aucun objet de type X à cette adresse. En tant que tel, le déréférencement de x en tant que type X ou l'accès à ses membres/méthodes serait un comportement indéfini.

Ainsi, la principale différence entre x->X(); (syntaxe hypothétique) et x->~X() est que dans le 2ème cas, vous avez un objet sur lequel vous pouvez appeler un membre (spécial) tel que le destructeur, tandis que dans le premier cas, il n'y a pas encore d'objet sur lequel vous pouvez appeler des méthodes (même la méthode spéciale - constructeur).

Vous pourriez faire valoir qu'il pourrait y avoir une exception à cette règle, mais ce serait finalement une question de préférence de syntaxe, où vous avez des incohérences dans les deux cas. Avec la syntaxe actuelle, l'appel au constructeur ne ressemble pas à un appel au constructeur, dans la syntaxe que vous proposez, il y aurait une symétrie avec l'appel du destructeur, mais des incohérences dans les règles qui régissent quand vous pouvez déréférencer/accéder aux méthodes d'un objet. En fait, il devrait y avoir une exception permettant d'appeler une méthode sur quelque chose qui n'est pas encore un objet. Il faudrait alors définir strictement dans la lettre de la norme quelque chose qui n'est pas encore un objet .

37
bolov

Il s'agit d'une variante du problème du poulet et des œufs.

Vous pouvez appeler explicitement des destructeurs, comme s'il s'agissait de fonctions membres, car l'instance de l'objet existe déjà.

Vous ne pouvez pas faire de même avec un constructeur, car une instance sur laquelle vous l'appellerez doit exister et être entièrement initialisée par un constructeur.

La seule exception à cela est lorsque vous avez alloué de la mémoire à l'objet, mais que vous n'avez pas encore initialisé l'instance (c'est-à-dire que la mémoire de l'instance est là, mais qu'elle n'a pas été initialisée pour devenir une instance réelle). Par conséquent, vous devez appeler un constructeur. C'est le cas lorsque l'emplacement new, la syntaxe que vous affichez sous le commentaire "option 1", est utile. Toutefois, il ne s'agit pas d'un appel membre que vous effectuez sur une instance, car l'instance n'est pas disponible avant d'effectuer cet appel.

9
dasblinkenlight