L'opérateur new () se comporte différemment lorsque l'opérateur delete () est supprimé en fonction de l'existence du constructeur par défaut

17

La création d'un nouvel objet de classe C avec l'opérateur new () donne une erreur ici:

class C
{
public:
    C() {}
    virtual ~C() {}

    void operator delete(void*) = delete;
};


int main()
{
    C* c = new C;
}

avec C2280: 'void C::operator delete(void *)': function was explicitly deleted

Mais quand je remplacerai C() {} avec C() = default; ou supprimer la ligne de sorte que le compilateur insère un constructeur par défaut (qui je crois , a le même effet = default), le code compiler et exécuter.

Quelles sont les différences entre le constructeur par défaut généré par le compilateur et le constructeur par défaut défini par l'utilisateur qui rendent cela possible?

J'ai un indice dans cette publication , mais la classe C ici (sans constructeur fourni par l'utilisateur) n'est pas anodine car le destructeur est virtuel, non?

Compilé avec la dernière version de Visual Studio, c ++ 17.

yeshjho
la source
3
Je ne sais pas, mais je pense que la différence est que le constructeur par défaut estnoexcept
Sebastian Redl
1
Impossible de reproduire avec g ++. Diagnostic similaire indiquant operator delete()si le constructeur est écrit manuellement ou généré implicitement. Ce qui est conforme à mes attentes - puisqu'une exception peut être levée par l' newexpression, le compilateur doit y accéder operator delete().
Peter
@SebastianRedl vous avez raison, l'ajout noexceptfera compiler le code, mais comment ...?
yeshjho
1
@Peter L'exception ne peut être levée que par le constructeur, donc si c'est noexceptcomme SebastianRedl l'a mentionné, alors un appel à operator deleten'a pas besoin d'être inclus. De plus, g ++ ne se plaint que si le destructeur est virtuel. Sinon, il compile toujours, même si le constructeur lance.
noyer
@LeDYoM Votre lien concerne l'analyse des adresses IP, ce qui ne semble pas pertinent pour la question. Avez-vous publié un mauvais lien?
LF

Réponses:

17

Quelles sont les différences entre le constructeur par défaut généré par le compilateur et le constructeur par défaut défini par l'utilisateur qui rendent cela possible?

newexpression appelle le correspondant operator newpuis appelle le constructeur. Si le constructeur lève une newexpression d' exception , il faut annuler l'effet de operator new(pour éviter une fuite de mémoire) en appelant le correspondant operator delete. Si ce dernier est supprimé, l' newexpression ne peut pas l'appeler, ce qui entraîne le compilateur error: use of deleted function 'static void C::operator delete(void*)'.

Un noexceptconstructeur ne peut pas lever une exception, par conséquent, le correspondant operator deleten'est pas nécessaire car il ne sera pas appelé par une newexpression. Un defaultconstructeur d'une classe triviale est également un noexceptconstructeur. La présence d'un destructeur virtuel doit operator deleteêtre non supprimée car le destructeur de suppression scalaire spécial (un détail d'implémentation permettant d'activer l' deleteexpression via le pointeur de classe de base) invoque operator delete.

Il ne semble pas être spécifié par la norme C ++ si le compilateur doit exiger operator deleted'être non supprimé même s'il ne peut pas être appelé par newexpression. gcc, cependant, il ne semble pas du tout invoquer l' expression correspondante operator deletedans newsi c'est deleted (a posté un rapport de bogue ).

Maxim Egorushkin
la source