Подтвердить что ты не робот

Изучение С++: полиморфизм и нарезка

Рассмотрим следующий пример:

#include <iostream>
using namespace std;

class Animal
{
public:
    virtual void makeSound() {cout << "rawr" << endl;}
};

class Dog : public Animal
{
public:
    virtual void makeSound() {cout << "bark" << endl;}
};

int main()
{
    Animal animal;
    animal.makeSound();

    Dog dog;
    dog.makeSound();

    Animal badDog = Dog();
    badDog.makeSound();

    Animal* goodDog = new Dog();
    goodDog->makeSound();
}

Вывод:

rawr
bark
rawr
bark

Но я думал, что, конечно, выход должен быть "rawr bark кора. Что с badDog?


Обновление: вас может заинтересовать еще один мой вопрос.

4b9b3361

Ответ 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, но они более похожи).

Ответ 2

 Animal badDog = Dog();
    ad.makeSound();

Когда вы создаете экземпляр Dog и присваиваете его по значению переменной Animal, вы срез объекта. В основном это означает, что вы отключите все Dog -ness от badDog и перейдете в базовый класс.

Чтобы использовать полиморфизм с базовыми классами, вы должны использовать либо указатели, либо ссылки.

Ответ 4

Вы инициализировали badDog с помощью оператора присваивания. Таким образом, Dog() был скопирован как Animal. Результат вашей программы верен.:)