Я изучаю 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
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
Абстрактные классы не могут быть созданы как полный объект, поэтому это приведет к ошибке времени компиляции, если вы попытаетесь.
Эта строка Animal ani = anim;
создает родовое животное от конкретного животного. Он назывался slicing, когда вы потеряли исходный производный класс.
Вам нужно изменить его на ссылку или указатель:
Animal& ani = anim;
Animal* pAni = &anim;
Это все потому, что у вас есть фактический объект класса Animal вместо указателя или ссылки на класс животных. Итак, если вы хотите, чтобы метод speak() собаки и Cat назывался ими, ссылайтесь на указатель родительского класса Animal. В противном случае вы получите метод выражения() животных, называемый как методы, называемые в соответствии с фактическим объектом, а не ссылками в полиморфизме.
Animal ani = anim;
означают две очень разные вещи в Java и C ++ (ссылочная семантика против семантики значений).