Was ist los ?
Wenn Sie ein erstellen Taxi
, erstellen Sie auch ein Car
Unterobjekt. Und wenn das Taxi zerstört wird, werden beide Objekte zerstört. Wenn Sie anrufen, übergeben test()
Sie den Car
by-Wert. Eine Sekunde Car
wird also kopiert und zerstört, wenn sie test()
übrig bleibt. Wir haben also eine Erklärung für 3 Destruktoren: den ersten und die beiden letzten in der Sequenz.
Der vierte Destruktor (das ist der zweite in der Sequenz) ist unerwartet und ich konnte nicht mit anderen Compilern reproduzieren.
Es kann nur eine temporäre Car
Quelle für das Car
Argument sein. Da dies nicht der Fall ist, wenn direkt ein Car
Wert als Argument angegeben wird, vermute ich, dass dies zur Umwandlung des Werts Taxi
in dient Car
. Dies ist unerwartet, da Car
in jedem bereits ein Unterobjekt vorhanden ist Taxi
. Daher denke ich, dass der Compiler eine unnötige Konvertierung in eine temporäre Version vornimmt und nicht die Kopierelision ausführt, die diese temporäre Version hätte vermeiden können.
Klarstellung in den Kommentaren:
Hier die Klarstellung unter Bezugnahme auf den Standard für Sprachanwälte zur Überprüfung meiner Ansprüche:
- Die Konvertierung, auf die ich mich hier beziehe, ist eine Konvertierung durch einen Konstruktor
[class.conv.ctor]
, dh das Konstruieren eines Objekts einer Klasse (hier Auto) basierend auf einem Argument eines anderen Typs (hier Taxi).
- Diese Konvertierung verwendet dann ein temporäres Objekt, um seinen
Car
Wert zurückzugeben. Der Compiler könnte eine Kopierelision gemäß vornehmen [class.copy.elision]/1.1
, da er anstelle einer temporären Konstruktion den Wert konstruieren könnte, der direkt in den Parameter zurückgegeben werden soll.
- Wenn diese Temperatur Nebenwirkungen hat, liegt dies daran, dass der Compiler diese mögliche Kopierentscheidung anscheinend nicht nutzt. Es ist nicht falsch, da die Kopierentscheidung nicht obligatorisch ist.
Experimentelle Bestätigung der Anaysis
Ich könnte jetzt Ihren Fall mit demselben Compiler reproduzieren und ein Experiment zeichnen, um zu bestätigen, was los ist.
Meine obige Annahme war, dass der Compiler einen suboptimalen Parameterübergabeprozess ausgewählt hat, der die Konstruktorkonvertierung verwendet, Car(const &Taxi)
anstatt die Konstruktion direkt aus dem Car
Unterobjekt von zu kopieren Taxi
.
Also habe ich versucht anzurufen, test()
aber das explizit Taxi
in a umgewandelt Car
.
Mein erster Versuch war nicht erfolgreich, die Situation zu verbessern. Der Compiler verwendete weiterhin die suboptimale Konstruktorkonvertierung:
test(static_cast<Car>(taxi)); // produces the same result with 4 destructor messages
Mein zweiter Versuch war erfolgreich. Es führt auch das Casting durch, verwendet jedoch das Zeiger-Casting, um dem Compiler dringend zu empfehlen, das Car
Unterobjekt des zu verwenden, Taxi
ohne dieses alberne temporäre Objekt zu erstellen:
test(*static_cast<Car*>(&taxi)); // :-)
Und Überraschung: Es funktioniert wie erwartet und produziert nur 3 Zerstörungsnachrichten :-)
Abschließendes Experiment:
In einem letzten Experiment habe ich einen benutzerdefinierten Konstruktor durch Konvertierung bereitgestellt:
class Car {
...
Car(const Taxi& t); // not necessary but for experimental purpose
};
und implementieren Sie es mit *this = *static_cast<Car*>(&taxi);
. Klingt albern, generiert aber auch Code, der nur 3 Destruktormeldungen anzeigt und so das unnötige temporäre Objekt vermeidet.
Dies lässt vermuten, dass im Compiler ein Fehler vorliegt, der dieses Verhalten verursacht. Es ist möglich, dass die Möglichkeit der direkten Erstellung von Kopien aus der Basisklasse unter bestimmten Umständen übersehen wird.
Taxi
Objekt an eine Funktion übergeben wird, die einCar
Objekt nach Wert nimmt?