Что происходит ?
Когда вы создаете Taxi
, вы также создаете Car
подобъект. А когда такси уничтожается, оба объекта разрушаются. Когда вы звоните, test()
вы передаете Car
по значению. Итак, второйCar
становится копируемой и разрушается, когда test()
ее оставляют. Итак, у нас есть объяснение 3 деструкторам: первый и два последних в последовательности.
Четвертый деструктор (второй в последовательности) неожиданный, и я не смог воспроизвести его с другими компиляторами.
Это может быть только временное Car
создание в качестве источника для Car
аргумента. Так как это не происходит при непосредственном предоставлении Car
значения в качестве аргумента, я подозреваю, что это для преобразования Taxi
в Car
. Это неожиданно, поскольку Car
в каждом уже есть подобъект Taxi
. Поэтому я думаю, что компилятор делает ненужное преобразование в temp и не выполняет копирование, которое могло бы избежать этой температуры.
Разъяснение дано в комментариях:
Вот пояснение со ссылкой на стандарт языка-юриста для проверки моих претензий:
- Преобразование, на которое я здесь ссылаюсь, - это преобразование по конструктору
[class.conv.ctor]
, то есть создание объекта одного класса (здесь Car) на основе аргумента другого типа (здесь Taxi).
- Это преобразование использует затем временный объект для возврата его
Car
значения. Компилятору будет разрешено сделать копию в соответствии с[class.copy.elision]/1.1
с тем, что вместо создания временного он может создать значение, которое будет возвращено непосредственно в параметр.
- Так что, если этот темп дает побочные эффекты, то это потому, что компилятор явно не использует это возможное копирование. Это не так, так как копирование не обязательно.
Экспериментальное подтверждение анализа
Теперь я могу воспроизвести ваш случай, используя тот же компилятор, и провести эксперимент, чтобы подтвердить происходящее.
Мое предположение выше заключалось в том, что компилятор выбрал неоптимальный процесс передачи параметров, используя преобразование конструктора Car(const &Taxi)
вместо копирования непосредственно из Car
подобъекта Taxi
.
Поэтому я попытался позвонить, test()
но явно бросил Taxi
вCar
.
Моя первая попытка не смогла улучшить ситуацию. Компилятор все еще использовал неоптимальное преобразование конструктора:
test(static_cast<Car>(taxi)); // produces the same result with 4 destructor messages
Моя вторая попытка удалась. Он также выполняет приведение, но использует приведение указателей, чтобы настоятельно рекомендовать компилятору использовать Car
подобъект Taxi
и без создания этого глупого временного объекта:
test(*static_cast<Car*>(&taxi)); // :-)
И сюрприз: он работает как положено, выдает только 3 сообщения об уничтожении :-)
Завершающий эксперимент:
В последнем эксперименте я предоставил пользовательский конструктор путем преобразования:
class Car {
...
Car(const Taxi& t); // not necessary but for experimental purpose
};
и реализовать его с *this = *static_cast<Car*>(&taxi);
. Звучит глупо, но это также генерирует код, который будет отображать только 3 сообщения деструктора, таким образом избегая ненужного временного объекта.
Это приводит к мысли, что в компиляторе может быть ошибка, вызывающая такое поведение. Это означает, что при некоторых обстоятельствах возможность прямого конструирования копии из базового класса будет упущена.
Taxi
объекта в функцию, принимающуюCar
объект по значению?