学习 C++:多态性和切片
Posted
技术标签:
【中文标题】学习 C++:多态性和切片【英文标题】:Learning C++: polymorphism and slicing 【发布时间】:2010-12-09 22:19:49 【问题描述】:考虑以下示例:
#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
但我认为输出肯定应该是“rawr bark bark bark”。坏狗怎么了?
更新:你可能对another question of mine感兴趣。
【问题讨论】:
我认为你的变量有一些命名错误。 代码甚至无法按原样编译。还有void main()
???呃...
我不明白为什么有人会否决这个问题——这既不是“不清楚”也不是“没用”。 +1 否决反对票。
指南“使非叶类抽象”可以防止这种意外。
另一个 +1 用于发布一个完整的、最小的示例来说明问题。这正是我们一直要求的。
【参考方案1】:
这是一个称为“切片”的问题。
Dog()
创建一个 Dog
对象。如果你打电话给Dog().makeSound()
,它会像你期望的那样打印“bark”。
问题是您正在使用Dog
初始化badDog
,它是Animal
类型的对象。由于Animal
只能包含Animal
而不能包含从Animal
派生的任何内容,因此它采用Dog
的Animal
部分并用它初始化自身。
badDog
的类型始终为Animal
;它永远不会是其他任何东西。
在 C++ 中获得多态行为的唯一方法是使用指针(正如您在 goodDog
示例中所展示的那样)或使用引用。
引用(例如,Animal&
)可以引用从Animal
派生的任何类型的对象,而指针(例如,Animal*
)可以指向从Animal
派生的任何类型的对象。但是,普通的 Animal
始终是 Animal
,仅此而已。
Java 和 C# 等一些语言具有引用语义,其中变量(在大多数情况下)只是对对象的引用,因此给定 Animal rex;
、rex
实际上只是对某些 Animal
和 @987654344 的引用@ 使 rex
引用一个新的 Dog
对象。
C++ 不是这样工作的:变量不引用 C++ 中的对象,变量是对象。如果你在 C++ 中说rex = Dog()
,它会将一个新的Dog
对象复制到rex
,并且由于rex
实际上是Animal
类型,它会被切片,并且只复制Animal
部分。这些被称为值语义,这是 C++ 中的默认值。如果要在 C++ 中使用引用语义,则需要显式使用引用或指针(它们都与 C# 或 Java 中的引用不同,但它们更相似)。
【讨论】:
很好的解释。谢谢! 我要补充一点,我将 Java 与 C# 和 C++ 的类型系统之间的比较过分简化了。 为什么 c++ 对象赋值不复制 vptr 和其余成员,以获得变量级别的多态性?【参考方案2】: Animal badDog = Dog();
ad.makeSound();
当您实例化 Dog
并将其按值分配给 Animal
变量时,您将 slice 对象。这基本上意味着您从badDog
中剥离所有Dog
-ness 并将其放入基类中。
为了在基类中使用多态性,您必须使用指针或引用。
【讨论】:
【参考方案3】:您使用赋值运算符初始化了 badDog。因此 Dog() 被复制为 Animal。您的程序的输出是正确的。 :)
【讨论】:
以上是关于学习 C++:多态性和切片的主要内容,如果未能解决你的问题,请参考以下文章