Apa yang terjadi ?
Saat Anda membuat Taxi, Anda juga membuat Carsubobjek. Dan ketika taksi dihancurkan, kedua benda hancur. Ketika Anda menelepon, test()Anda melewati nilai Cardengan. Jadi yang kedua Carakan dikopi dan akan hancur ketika test()dibiarkan. Jadi kami memiliki penjelasan untuk 3 destruktor: yang pertama dan yang kedua terakhir secara berurutan.
Destructor keempat (yang kedua dalam urutan) tidak terduga dan saya tidak bisa mereproduksi dengan kompiler lain.
Itu hanya bisa Cardibuat sementara sebagai sumber untuk Carargumen. Karena itu tidak terjadi ketika memberikan langsung Carnilai sebagai argumen, saya menduga itu untuk mengubah ke Taxidalam Car. Ini tidak terduga, karena sudah ada Carsub proyek di setiap Taxi. Oleh karena itu saya berpikir bahwa kompiler melakukan konversi yang tidak perlu menjadi temp dan tidak melakukan copy elision yang bisa menghindari temp ini.
Klarifikasi yang diberikan dalam komentar:
Di sini klarifikasi dengan mengacu pada standar pengacara bahasa untuk memverifikasi klaim saya:
- Konversi yang saya maksudkan di sini, adalah konversi oleh konstruktor
[class.conv.ctor], yaitu membangun objek dari satu kelas (di sini Mobil) berdasarkan argumen dari tipe lain (di sini Taksi).
- Konversi ini menggunakan objek sementara untuk mengembalikan
Carnilainya. Kompiler akan diizinkan untuk membuat salinan salinan sesuai [class.copy.elision]/1.1, karena alih-alih membangun sementara, itu dapat membangun nilai yang akan dikembalikan langsung ke parameter.
- Jadi jika temp ini memberikan efek samping, itu karena kompiler tampaknya tidak menggunakan kemungkinan copy-elision ini. Itu tidak salah, karena salinan elision tidak wajib.
Konfirmasi eksperimental anaysis
Saya sekarang dapat mereproduksi kasing Anda dengan menggunakan kompiler yang sama dan menggambar percobaan untuk mengonfirmasi apa yang sedang terjadi.
Asumsi saya di atas adalah bahwa kompiler memilih proses melewati parameter suboptimal, menggunakan konversi konstruktor Car(const &Taxi)alih-alih menyalin konstruksi langsung dari Carsubobjek dari Taxi.
Jadi saya mencoba menelepon test()tetapi secara eksplisit melemparkan Taxike dalam Car.
Upaya pertama saya tidak berhasil memperbaiki situasi. Kompiler masih menggunakan konversi konstruktor suboptimal:
test(static_cast<Car>(taxi)); // produces the same result with 4 destructor messages
Upaya kedua saya berhasil. Itu melakukan casting juga, tetapi menggunakan pointer casting agar sangat menyarankan kompiler untuk menggunakan Carsubobjek dari Taxidan tanpa membuat objek sementara konyol ini:
test(*static_cast<Car*>(&taxi)); // :-)
Dan mengejutkan: berfungsi seperti yang diharapkan, hanya menghasilkan 3 pesan penghancuran :-)
Eksperimen penutup:
Dalam percobaan terakhir, saya memberikan konstruktor khusus dengan konversi:
class Car {
...
Car(const Taxi& t); // not necessary but for experimental purpose
};
dan mengimplementasikannya dengan *this = *static_cast<Car*>(&taxi);. Kedengarannya konyol, tetapi ini juga menghasilkan kode yang hanya akan menampilkan 3 pesan destruktor, sehingga menghindari objek sementara yang tidak perlu.
Ini membuat kami berpikir bahwa mungkin ada bug di kompiler yang menyebabkan perilaku ini. Ini adalah kemungkinan salinan-konstruksi langsung dari kelas dasar akan terjawab dalam beberapa keadaan.
Taxiobjek ke fungsi yang mengambilCarobjek dengan nilai?