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