Эта проблема называется "нарезка".
Dog()
создает объект Dog
. Если бы вы звонили Dog().makeSound()
, он напечатал бы «лай» так, как вы ожидаете.
Проблема в том, что вы инициализируете badDog
, который является объектом типа Animal
, с этим Dog
. Поскольку Animal
может содержать только Animal
, а не что-либо, полученное из Animal
, он берет Animal
часть Dog
и инициализирует себя этим.
Тип badDog
всегда Animal
; больше ничего не может быть.
Единственный способ получить полиморфное поведение в C ++ - использовать указатели (как вы продемонстрировали на примере goodDog
) или ссылки.
Ссылка (например, Animal&
) может ссылаться на объект любого типа, производного от Animal
, а указатель (например, Animal*
) может указывать на объект любого типа, производного от Animal
. Однако простая Animal
всегда является Animal
, ничем иным.
Некоторые языки, такие как Java и C #, имеют ссылочную семантику, где переменные (в большинстве случаев) являются просто ссылками на объекты, поэтому, учитывая Animal rex;
, rex
на самом деле является просто ссылкой на некоторые Animal
и rex = new Dog()
делает rex
ссылкой на новый Dog
объект.
C ++ не работает таким образом: переменные не ссылаются на объекты в C ++, переменные являются объектами. Если вы говорите rex = Dog()
в C ++, он копирует новый Dog
объект в rex
, а поскольку rex
на самом деле имеет тип Animal
, он разрезается и копируются только части Animal
. Это так называемая семантика значений, которая используется по умолчанию в C ++. Если вы хотите ссылочную семантику в C ++, вам нужно явно использовать ссылки или указатели (ни один из них не является тем же, что и ссылки в C # или Java, но они более похожи).