为什么我不能对正态变量进行多态性?

Why can't I do polymorphism with normal variables?

本文关键字:多态性 变量 不能 为什么      更新时间:2023-10-16

我是一名Java程序员,最近开始学习C++。我被什么事弄糊涂了。

我知道在C++中,要实现多态行为,必须使用指针或引用。例如,考虑具有已实现方法getArea()的类Shape。它有几个子类,每个子类都以不同的方式覆盖getArea()。然后考虑以下功能:

void printArea(Shape* shape){
    cout << shape->getArea();
}

该函数根据指针指向的具体Shape调用正确的getArea()实现。

这是一样的:

void printArea(Shape& shape){
    cout << shape.getArea();
}

然而,以下方法在多态性上不起作用:

void printArea(Shape shape){
    cout << shape.getArea();
}

无论函数中传递了哪种具体类型的Shape,都会调用相同的getArea()实现:Shape中的默认实现。

我想了解这背后的技术原因。为什么多态性适用于指针和引用,而不适用于正常变量?(我想这不仅适用于函数参数,也适用于任何情况)。

请解释这种行为的技术原因,以帮助我理解。

答案是复制语义。

当你在C++中按值传递对象时,例如printArea(Shape shape),你传递的对象会有一个副本。如果你把一个派生类传递给这个函数,那么所有被复制的都是基类Shape。如果你仔细想想,编译器不可能做任何其他事情。

Shape shapeCopy = circle;

shapeCopy被声明为Shape,而不是Circle,所以编译器所能做的就是构造对象的Shape部分的副本。

调用void printArea(Shape shape)时,对象会复制到堆栈上的全新Shape中。子类部分不会被复制。这就是所谓的对象切片。如果基类对象是不合法的(例如,它有纯虚拟函数),你甚至不能声明或调用这个函数。这就是"传递值"语义;将提供传入对象的副本。

调用void printArea(Shape& shape)时,会传递对CircleRectangle对象的引用。(具体来说,是对该对象的Shape部分的引用。如果不强制转换,就无法访问特定于CircleSquare的成员。但虚拟函数当然可以正常工作。)这就是"通过引用传递"语义;传入对原始对象的引用。

您正在讨论运行时多态性。

这是一个对象可以是从*静态已知类派生的类的想法,因此对静态已知类中的虚拟成员函数的调用最终会出现在派生类实现中。

有了指针或引用,派生程度最高的类可以不同于(比)静态已知的类。但对于直接变量,静态已知的类派生最多的类。因此,除了最终导致基类成员函数调用的调用之外,没有运行时多态性的空间(在基类成员函数中,静态已知的类型是该基类,与大多数派生类不同)。


*静态已知类意味着编译器在没有任何分析的情况下已知,即声明的类

基本上,当您按值传递对象时,它会被复制到目标类型的对象-在这一点上,它是目标类型的对象,所以没有什么可以多态的(这甚至是一个词吗?)。

使用您的示例void printArea(Shape shape);,在printArea()函数内,参数shapeShape对象,而与调用站点使用的对象无关。