考虑以下示例:
#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
但我认为输出应该是“粗树皮树皮树皮”。 badDog有什么用?
更新:您可能对another question of mine感兴趣。
答案 0 :(得分:70)
这是一个叫做“切片”的问题。
Dog()
会创建一个Dog
对象。如果您打电话给Dog().makeSound()
,它会按照您的预期打印“树皮”。
问题是您正在使用此badDog
初始化Animal
,这是Dog
类型的对象。由于Animal
只能包含Animal
,而不能包含Animal
的任何内容,因此它会占用Animal
的{{1}}部分并使用该值进行初始化。< / p>
Dog
的类型始终为badDog
;它永远不会是别的。
在C ++中获得多态行为的唯一方法是使用指针(正如您在Animal
示例中演示的那样)或使用引用。
引用(例如,goodDog
)可以引用从Animal&
派生的任何类型的对象,并且指针(例如,Animal
)可以指向任何类型的对象派生来自Animal*
。但是,普通Animal
始终是Animal
,而不是其他。
某些语言(如Java和C#)具有引用语义,其中变量(在大多数情况下)只是对对象的引用,因此给定Animal
,Animal rex;
实际上只是对某些{{1}的引用}}和rex
使Animal
引用新的rex = new Dog()
对象。
C ++不能这样工作:变量不引用C ++中的对象,变量是对象。如果您在C ++中说rex
,它会将新的Dog
对象复制到rex = Dog()
,并且由于Dog
实际上是rex
类型,它会被切片并且只是rex
部分被复制。这些被称为值语义,它是C ++中的默认值。如果你想在C ++中使用引用语义,你需要显式地使用引用或指针(这些引用或指针都不同于C#或Java中的引用,但它们更相似)。
答案 1 :(得分:10)
Animal badDog = Dog();
ad.makeSound();
当您实例化Dog
并将其按值分配给Animal
变量时,您slice该对象。这基本上意味着您从Dog
剥离所有badDog
- 并将其放入基类。
为了将多态性与基类一起使用,必须使用指针或引用。
答案 2 :(得分:-1)
您使用赋值运算符初始化了badDog。因此Dog()被复制为Animal。程序的输出是正确的。 :)