Изучение C ++: полиморфизм и нарезка - PullRequest
51 голосов
/ 10 декабря 2010

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

#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?


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

Ответы [ 3 ]

70 голосов
/ 10 декабря 2010

Эта проблема называется "нарезка".

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

10 голосов
/ 10 декабря 2010
 Animal badDog = Dog();
    ad.makeSound();

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

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

0 голосов
/ 10 декабря 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...