Chuyện gì đang xảy ra vậy?
Khi bạn tạo một Taxi
, bạn cũng tạo một tiểu dự án Car
. Và khi chiếc taxi bị phá hủy, cả hai đối tượng đều bị phá hủy. Khi bạn gọi test()
bạn vượt qua Car
giá trị. Vì vậy, một giây Car
được xây dựng sao chép và sẽ bị phá hủy khi test()
còn lại. Vì vậy, chúng tôi có một lời giải thích cho 3 hàm hủy: đầu tiên và hai cuối cùng trong chuỗi.
Hàm hủy thứ tư (đó là thứ hai trong chuỗi) là bất ngờ và tôi không thể sao chép với các trình biên dịch khác.
Nó chỉ có thể là tạm thời Car
được tạo như là nguồn cho Car
đối số. Vì nó không xảy ra khi cung cấp trực tiếp một Car
giá trị làm đối số, tôi nghi ngờ đó là để chuyển đổi Taxi
thành Car
. Điều này là bất ngờ, vì đã có một tiểu dự án Car
trong mỗi Taxi
. Do đó, tôi nghĩ rằng trình biên dịch thực hiện một chuyển đổi không cần thiết thành một temp và không thực hiện các cuộc bầu chọn sao chép có thể tránh được temp này.
Làm rõ trong các ý kiến:
Ở đây làm rõ với tham chiếu đến tiêu chuẩn cho luật sư ngôn ngữ để xác minh khiếu nại của tôi:
- Chuyển đổi mà tôi đang đề cập ở đây, là một chuyển đổi của nhà xây dựng
[class.conv.ctor]
, tức là xây dựng một đối tượng của một lớp (ở đây là Xe hơi) dựa trên một đối số của loại khác (ở đây là Taxi).
- Chuyển đổi này sử dụng sau đó một đối tượng tạm thời để trả về
Car
giá trị của nó . Trình biên dịch sẽ được phép thực hiện một cuộc bầu chọn sao chép [class.copy.elision]/1.1
, vì thay vì xây dựng tạm thời, nó có thể xây dựng giá trị được trả trực tiếp vào tham số.
- Vì vậy, nếu temp này mang lại hiệu ứng phụ, thì đó là do trình biên dịch dường như không sử dụng phương pháp sao chép có thể này. Điều đó không sai, vì việc sao chép bản sao là không bắt buộc.
Xác nhận thí nghiệm của anaysis
Bây giờ tôi có thể tái tạo trường hợp của bạn bằng cách sử dụng cùng một trình biên dịch và vẽ một thử nghiệm để xác nhận những gì đang xảy ra.
Giả định của tôi ở trên là trình biên dịch đã chọn một quá trình truyền tham số dưới mức tối ưu, sử dụng chuyển đổi hàm tạo Car(const &Taxi)
thay vì sao chép cấu trúc trực tiếp từ tiểu dự án Car
của Taxi
.
Vì vậy, tôi đã cố gắng gọi test()
nhưng rõ ràng đúc Taxi
thành một Car
.
Nỗ lực đầu tiên của tôi đã không thành công để cải thiện tình hình. Trình biên dịch vẫn sử dụng chuyển đổi hàm tạo tối ưu:
test(static_cast<Car>(taxi)); // produces the same result with 4 destructor messages
Nỗ lực thứ hai của tôi đã thành công. Nó cũng thực hiện quá trình truyền, nhưng sử dụng đúc con trỏ để đề xuất mạnh mẽ trình biên dịch sử dụng tiểu dự án Car
của Taxi
và không tạo đối tượng tạm thời ngớ ngẩn này:
test(*static_cast<Car*>(&taxi)); // :-)
Và thật bất ngờ: nó hoạt động như mong đợi, chỉ tạo ra 3 thông báo hủy :-)
Kết thúc thí nghiệm:
Trong một thử nghiệm cuối cùng, tôi đã cung cấp một hàm tạo tùy chỉnh bằng cách chuyển đổi:
class Car {
...
Car(const Taxi& t); // not necessary but for experimental purpose
};
và thực hiện nó với *this = *static_cast<Car*>(&taxi);
. Nghe có vẻ ngớ ngẩn, nhưng điều này cũng tạo ra mã sẽ chỉ hiển thị 3 thông báo hủy, do đó tránh các đối tượng tạm thời không cần thiết.
Điều này dẫn đến việc nghĩ rằng có thể có một lỗi trong trình biên dịch gây ra hành vi này. Đó là khả năng sao chép trực tiếp - việc xây dựng từ lớp cơ sở sẽ bị bỏ qua trong một số trường hợp.
Taxi
đối tượng đến một hàm lấy mộtCar
đối tượng theo giá trị?