我自己学习c ++并且遇到了一些我没想到的行为。我不确定,但确实认为这不是Java在类似情况下会做的事情。为什么Dog
和Cat
依赖于te父类实现并发出通用声音?
#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 :(得分:5)
Animal ani = anim;
无论Animal
的类型如何,都会创建anim
类型的新对象。因此,ani.speak()
会调用Animal::speak
,因为这是类型Animal
的覆盖。
如果要创建引用(或指针)
Animal & ani = anim;
然后将保留动态类型,并调用T::speak
。这类似于复制对象引用时Java中发生的情况;但是你不应该尝试用Java来理解C ++对象模型,因为它们非常不同。
通过使基类抽象,可以防止意外创建基类对象(有时称为“切片”,因为它会切掉对象的派生类部分)。也就是说,使用默认实现声明函数pure virtual,而不是非pure:
virtual void speak() = 0; // Really not implemented
抽象类无法实例化为完整对象,因此如果您尝试这将导致编译时错误。
答案 1 :(得分:1)
此行Animal ani = anim;
从特定动物创建一般动物。当你丢失原始派生类时,它被称为切片。
您需要将其更改为引用或指针:
Animal& ani = anim;
Animal* pAni = &anim;
答案 2 :(得分:0)
这都是因为你有Animal类的实际对象而不是指向动物类的指针或引用。因此,如果你想要调用dog和cat的speak()方法,那么就要做父类Animal的引用或指针。否则你会得到一个叫做方法的speak()方法,根据实际对象调用而不是多态中的引用。