Der Operator new () verhält sich je nach Existenz des Standardkonstruktors unterschiedlich, wenn der Operator delete () gelöscht wird

17

Das Erstellen eines neuen Objekts der Klasse C mit dem Operator new () führt hier zu einem Fehler:

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

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


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

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

Aber wenn ich ersetzen C() {} mit C() = default; oder die Zeile entfernen , so dass Compiler einen Standardkonstruktor fügt (was ich glaube die gleiche Wirkung mit hat = default), wird der Code kompiliert und ausgeführt.

Was sind die Unterschiede zwischen dem vom Compiler generierten Standardkonstruktor und dem benutzerdefinierten Standardkonstruktor, die dies ermöglichen?

Ich habe einen Hinweis in diesem Beitrag erhalten , aber Klasse C hier (ohne vom Benutzer bereitgestellten Konstruktor) ist nicht trivial, da der Destruktor virtuell ist, oder?

Kompiliert mit dem neuesten Visual Studio, c ++ 17.

Yeshjho
quelle
3
Ich bin mir nicht sicher, aber ich denke, der Unterschied ist, dass der voreingestellte Konstruktornoexcept
Sebastian Redl
1
Kann nicht mit g ++ reproduzieren. Ähnliche Diagnose, operator delete()ob der Konstruktor manuell geschrieben oder implizit generiert wird. Dies entspricht meinen Erwartungen - da der newAusdruck möglicherweise eine Ausnahme auslöst, muss der Compiler darauf zugreifen operator delete().
Peter
@SebastianRedl Sie haben Recht, durch Hinzufügen noexceptwird der Code kompiliert, aber wie ...?
Yeshjho
1
@Peter Die Ausnahme kann nur vom Konstruktor ausgelöst werden. Wenn es sich also noexceptum SebastianRedl handelt, muss ein Aufruf von operator deletenicht enthalten sein. Auch g ++ beschwert sich nur, wenn der Destruktor virtuell ist. Andernfalls wird immer kompiliert, auch wenn der Konstruktor wirft.
Walnuss
@LeDYoM In Ihrem Link geht es um das Parsen von IP-Adressen, was für die Frage irrelevant zu sein scheint. Hast du einen falschen Link gepostet?
LF

Antworten:

17

Was sind die Unterschiede zwischen dem vom Compiler generierten Standardkonstruktor und dem benutzerdefinierten Standardkonstruktor, die dies ermöglichen?

newAusdruck ruft den entsprechenden auf operator newund ruft dann den Konstruktor auf. Wenn der Konstruktor einen Ausnahmeausdruck newauslöst, muss der Effekt von operator new(um Speicherverluste zu vermeiden) durch Aufrufen des entsprechenden Ausdrucks rückgängig gemacht werden operator delete. Wenn letzteres gelöscht wird new, kann der Ausdruck ihn nicht aufrufen, was zum Compiler führt error: use of deleted function 'static void C::operator delete(void*)'.

Ein noexceptKonstruktor kann möglicherweise keine Ausnahme auslösen, daher ist die entsprechende operator deletenicht erforderlich, da sie nicht von einem newAusdruck aufgerufen wird . Ein defaultKonstruktor einer trivialen Klasse ist auch ein noexceptKonstruktor. Das Vorhandensein eines virtuellen Destruktors muss operator deletenicht gelöscht werden, da der spezielle skalare Löschdestruktor (ein Implementierungsdetail zum Aktivieren des deleteAusdrucks über den Basisklassenzeiger) aufgerufen wird operator delete.

Der C ++ - Standard scheint nicht spezifiziert zu sein, ob der Compiler operator deletenicht gelöscht werden muss, auch wenn er möglicherweise nicht durch newAusdruck aufgerufen werden kann. gccJedoch scheint nicht zu dem entsprechenden zu Aufrufen operator deletein newAusdruck überhaupt , wenn es deleted (schrieb einen Bug - Report ).

Maxim Egorushkin
quelle