我是一名Java程序员,最近开始学习C ++。我对此感到困惑。
我理解在C ++中,要实现多态行为,必须使用指针或引用。例如,考虑具有已实现方法Shape
的类getArea()
。它有几个子类,每个子类以不同的方式覆盖getArea()。比考虑以下功能:
void printArea(Shape* shape){
cout << shape->getArea();
}
该函数根据指针指向的具体getArea()
调用正确的Shape
实现。
这也是一样的:
void printArea(Shape& shape){
cout << shape.getArea();
}
但是,以下方法不适用于多态:
void printArea(Shape shape){
cout << shape.getArea();
}
在函数中传递具体类型的Shape
无关紧要,调用相同的getArea()
实现:Shape
中的默认实现。
我想了解这背后的技术推理。为什么多态性适用于指针和引用,而不适用于普通变量? (我认为这不仅适用于函数参数,也适用于任何事情)。
请解释这种行为的技术原因,以帮助我理解。
答案 0 :(得分:4)
答案是复制语义。
在C ++中按值传递对象时,例如printArea(Shape shape)
复制您传递的对象。如果将派生类传递给此函数,则复制的所有内容都是基类Shape
。如果你考虑一下,那么编译器就无法做任何事情。
Shape shapeCopy = circle;
shapeCopy
被声明为Shape
,而不是Circle
,因此编译器所能做的就是构造对象Shape
部分的副本。
答案 1 :(得分:1)
调用void printArea(Shape shape)
时,您的对象将被复制到堆栈中的全新Shape
。子类部分不会被复制。这称为object slicing。如果基类对象不合法(例如,它中包含纯虚函数),则甚至无法声明或调用此函数。那就是“通过价值传递”的语义;提供了传入对象的副本。
调用void printArea(Shape& shape)
时,会传递对Circle
或Rectangle
对象的引用。 (具体来说,是对该对象的Shape
部分的引用。您无法在不进行转换的情况下访问Circle
- 或Square
特定成员。但虚拟函数当然可以正常工作。 )这是“通过引用传递”语义;传入对原始对象的引用。
答案 2 :(得分:1)
您正在谈论运行时多态性。
这是一个对象可以是从 * 静态已知类派生的类的想法,因此对静态已知类中的虚拟成员函数的调用最终会在派生类实现中完成。
使用指针或引用时,派生类最多的类可以与静态已知类不同(更具体)。但是对于直接变量,静态已知类是派生类最多的类。因此,除了最终导致来自基类成员函数的调用的调用之外,没有运行时多态性的空间(在基类成员函数中,静态已知类型是基类,与最派生类不同)
<小时/> * )静态已知的类意味着编译器在没有任何分析的情况下已知,声明的类。
答案 3 :(得分:0)
基本上,当你按值传递一个对象时,它被复制到一个目标类型的对象 - 那时它是一个目标类型的对象,所以没有什么可以变形(甚至是一句话?)。
要在void printArea(Shape shape);
函数中使用您的示例printArea()
,参数shape
是一个Shape
对象,无论对象是什么用于呼叫站点。