Was ist los ?
Wenn Sie ein erstellen Taxi, erstellen Sie auch ein CarUnterobjekt. Und wenn das Taxi zerstört wird, werden beide Objekte zerstört. Wenn Sie anrufen, übergeben test()Sie den Carby-Wert. Eine Sekunde Carwird 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 CarQuelle für das CarArgument sein. Da dies nicht der Fall ist, wenn direkt ein CarWert als Argument angegeben wird, vermute ich, dass dies zur Umwandlung des Werts Taxiin dient Car. Dies ist unerwartet, da Carin 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
CarWert 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 CarUnterobjekt von zu kopieren Taxi.
Also habe ich versucht anzurufen, test()aber das explizit Taxiin 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 CarUnterobjekt des zu verwenden, Taxiohne 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.
TaxiObjekt an eine Funktion übergeben wird, die einCarObjekt nach Wert nimmt?