学习C ++:多态和切片

时间:2010-12-09 22:19:50

标签: c++ polymorphism

考虑以下示例:

#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感兴趣。

3 个答案:

答案 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#)具有引用语义,其中变量(在大多数情况下)只是对对象的引用,因此给定AnimalAnimal 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。程序的输出是正确的。 :)