Почему подкласс вызывает метод своего родителя вместо своего собственного?

0

Я изучаю c++ самостоятельно и столкнулся с поведением, которого я не ожидал. Я не уверен, но думаю, что это не то, что Java будет делать в подобной ситуации. Почему Dog и Cat полагаются на реализацию родительского класса и создают общий звук?

#include <iostream>

class Animal{
    public:
        virtual void speak(){std::cout << "???" << std::endl; /* not implemented */}
};

class Dog : public Animal{
    public:
        virtual void speak(){
            std::cout << "Woof" << std::endl;
        }
};

class Cat : public Animal{
    public:
        virtual void speak(){
            std::cout << "Meow" << std::endl;
        }
};

class Desk{
    /* Not an animal */
};

template<class T>
void please_talk(){
    T anim;
    Animal ani = anim;
    anim.speak();
    ani.speak();
}

int main()
{
    please_talk<Dog>();
    please_talk<Cat>();
    /* Does not compile please_talk<Desk>(); */
    return 0;
}

Результат:

Woof
Generic animal sound
Meow
Generic animal sound
  • 0
    Потому что «ani» относится к типу Animal (это не указатель на базовый класс, а сам базовый класс).
  • 2
    Вы правы, это не то, что делает Java, в основном потому, что Animal ani = anim; означают две очень разные вещи в Java и C ++ (ссылочная семантика против семантики значений).
Теги:
class
methods
inheritance
virtual

3 ответа

5
Лучший ответ
Animal ani = anim;

Это создает новый объект типа Animal, независимо от типа anim. Поэтому ani.speak() вызовет Animal::speak, так как это переопределение для типа Animal.

Если вы должны были создать ссылку (или указатель)

Animal & ani = anim;

то динамический тип будет сохранен, и T::speak будет вызываться. Это похоже на то, что происходит в Java при копировании ссылки на объект; но вы не должны пытаться понять объектную модель C++ с точки зрения Java, поскольку они очень разные.

Вы можете предотвратить случайное создание объектов базового класса (иногда называемых "срезанием", поскольку оно отсекает части производного класса объекта), делая абстрактное абстрактное класс. То есть, объявить функцию чистой виртуальной, а не не чистой с реализацией по умолчанию:

virtual void speak() = 0; // Really not implemented

Абстрактные классы не могут быть созданы как полный объект, поэтому это приведет к ошибке времени компиляции, если вы попытаетесь.

  • 0
    Есть ли способ элегантно проверить anim's - это экземпляр подкласса Animal? Я хочу, чтобы компилятор выкрикнул, если вы попытаетесь сделать please_talk <Desk> ();
1

Эта строка Animal ani = anim; создает родовое животное от конкретного животного. Он назывался slicing, когда вы потеряли исходный производный класс.

Вам нужно изменить его на ссылку или указатель:

Animal& ani = anim;

Animal* pAni = &anim;
0

Это все потому, что у вас есть фактический объект класса Animal вместо указателя или ссылки на класс животных. Итак, если вы хотите, чтобы метод speak() собаки и Cat назывался ими, ссылайтесь на указатель родительского класса Animal. В противном случае вы получите метод выражения() животных, называемый как методы, называемые в соответствии с фактическим объектом, а не ссылками в полиморфизме.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню