Ich habe dies ursprünglich nur als Frage zu Destruktoren gepostet, aber jetzt füge ich die Berücksichtigung des Standardkonstruktors hinzu. Hier ist die ursprüngliche Frage:
Wenn ich meiner Klasse einen Destruktor geben möchte, der virtuell ist, aber ansonsten dem entspricht, was der Compiler generieren würde, kann ich Folgendes verwenden
=default
:class Widget { public: virtual ~Widget() = default; };
Aber es scheint, dass ich mit weniger Eingabe mit einer leeren Definition den gleichen Effekt erzielen kann:
class Widget { public: virtual ~Widget() {} };
Gibt es eine Art und Weise, wie sich diese beiden Definitionen unterschiedlich verhalten?
Basierend auf den Antworten auf diese Frage scheint die Situation für den Standardkonstruktor ähnlich zu sein. Gibt es angesichts der Tatsache, dass es für Destruktoren fast keinen Bedeutungsunterschied zwischen " =default
" und " {}
" gibt, in ähnlicher Weise fast keinen Bedeutungsunterschied zwischen diesen Optionen für Standardkonstruktoren? Angenommen, ich möchte einen Typ erstellen, in dem die Objekte dieses Typs sowohl erstellt als auch zerstört werden, warum sollte ich dann sagen?
Widget() = default;
anstatt
Widget() {}
?
Ich entschuldige mich, wenn die Erweiterung dieser Frage nach der ursprünglichen Veröffentlichung gegen einige SO-Regeln verstößt. Das Posten einer fast identischen Frage für Standardkonstruktoren schien mir die weniger wünschenswerte Option zu sein.
quelle
= default
ist expliziter imo und stimmt mit der Unterstützung durch Konstruktoren überein.std::has_trivial_destructor<Widget>::value
ist estrue
für den ersten, aberfalse
für den zweiten. Was die Auswirkungen davon sind, weiß ich auch nicht. :)Antworten:
Dies ist eine völlig andere Frage, wenn nach Konstruktoren gefragt wird als nach Destruktoren.
Wenn Ihr Destruktor ist
virtual
, dann ist der Unterschied vernachlässigbar, wie Howard betonte . Wenn Ihr Destruktor jedoch nicht virtuell war , ist dies eine ganz andere Geschichte. Gleiches gilt für Konstruktoren.Die Verwendung der
= default
Syntax für spezielle Elementfunktionen (Standardkonstruktor, Kopieren / Verschieben von Konstruktoren / Zuweisung, Destruktoren usw.) bedeutet etwas ganz anderes als das einfache Ausführen{}
. Mit letzterem wird die Funktion "vom Benutzer bereitgestellt". Und das ändert alles.Dies ist eine triviale Klasse nach der Definition von C ++ 11:
Wenn Sie versuchen, ein Standardkonstrukt zu erstellen, generiert der Compiler automatisch einen Standardkonstruktor. Gleiches gilt für Kopieren / Verschieben und Zerstören. Da der Benutzer keine dieser Elementfunktionen bereitgestellt hat, wird dies in der C ++ 11-Spezifikation als "triviale" Klasse betrachtet. Es ist daher legal, dies zu tun, wie ihren Inhalt zu merken, um sie zu initialisieren und so weiter.
Dies:
Wie der Name schon sagt, ist dies nicht mehr trivial. Es verfügt über einen vom Benutzer bereitgestellten Standardkonstruktor. Es ist egal, ob es leer ist; Für die Regeln von C ++ 11 kann dies kein trivialer Typ sein.
Dies:
Wie der Name schon sagt, handelt es sich um einen trivialen Typ. Warum? Weil Sie den Compiler angewiesen haben, den Standardkonstruktor automatisch zu generieren. Der Konstruktor wird daher nicht "vom Benutzer bereitgestellt". Daher gilt der Typ als trivial, da er keinen vom Benutzer bereitgestellten Standardkonstruktor enthält.
Die
= default
Syntax dient hauptsächlich dazu, beispielsweise Konstruktoren / Zuweisungen zu kopieren, wenn Sie Elementfunktionen hinzufügen, die die Erstellung solcher Funktionen verhindern. Es löst aber auch ein spezielles Verhalten des Compilers aus, sodass es auch in Standardkonstruktoren / -destruktoren nützlich ist.quelle
=default
Funktionen der Fall ist) und vom Benutzer bereitgestellten (was für{}
Funktionen der Fall ist ) Funktionen zugrunde. Sowohl vom Benutzer deklarierte als auch vom Benutzer bereitgestellte Funktionen können die Generierung anderer spezieller Elementfunktionen verhindern (z. B. verhindert ein vom Benutzer deklarierter Destruktor die Generierung der Verschiebungsoperationen), aber nur eine vom Benutzer bereitgestellte spezielle Funktion macht eine Klasse nicht trivial. Richtig?= default
scheint nützlich zu sein, um den Compiler zu zwingen, trotz der Anwesenheit anderer Konstruktoren einen Standardkonstruktor zu generieren; Der Standardkonstruktor wird nicht implizit deklariert, wenn andere vom Benutzer deklarierte Konstruktoren bereitgestellt werden.Sie sind beide nicht trivial.
Sie haben beide die gleiche Noexcept-Spezifikation, abhängig von der Noexcept-Spezifikation der Basen und Mitglieder.
Der einzige Unterschied, den ich bisher feststelle, besteht darin, dass, wenn er
Widget
eine Basis oder ein Mitglied mit einem unzugänglichen oder gelöschten Destruktor enthält:Dann wird die
=default
Lösung kompiliert, ist aberWidget
kein zerstörbarer Typ. Wenn Sie also versuchen, a zu zerstörenWidget
, wird ein Fehler beim Kompilieren angezeigt. Wenn Sie dies nicht tun, haben Sie ein Arbeitsprogramm.Otoh, wenn Sie den vom Benutzer bereitgestellten Destruktor bereitstellen, werden die Dinge nicht kompiliert, unabhängig davon, ob Sie Folgendes zerstören
Widget
:quelle
=default;
dem Compiler wird der Destruktor nur generiert, wenn er verwendet wird, und daher wird kein Fehler ausgelöst. Das kommt mir komisch vor, auch wenn es nicht unbedingt ein Fehler ist. Ich kann mir nicht vorstellen , dass dieses Verhalten wird beauftragt , in der Norm.Der wichtige Unterschied zwischen
und
ist, dass der mit definierte Standardkonstruktor
B() = default;
als nicht benutzerdefiniert betrachtet wird . Dies bedeutet, dass im Falle einer Wertinitialisierung wie inEine spezielle Art der Initialisierung, bei der überhaupt kein Konstruktor verwendet wird, findet statt. Bei integrierten Typen führt dies zu einer Nullinitialisierung . In diesem Fall
B(){}
findet dies nicht statt. Der C ++ Standard n3337 § 8.5 / 7 sagtBeispielsweise:
mögliches Ergebnis:
http://ideone.com/k8mBrd
quelle