关于在强制转换为子类的超类时调用其重写方法

About calling an subclass' overriding method when casted to its superclass

本文关键字:超类 调用 方法 重写 子类 转换      更新时间:2023-10-16
#include <iostream>
class Vehicle { 
public:
    void greet() {
        std::cout << "Hello, I'm a vehicle";
    }
};
class Car : public Vehicle { 
public:
    void greet() {
        std::cout << "Hello, I'm a car";
    }
};
class Bike : public Vehicle { 
public:
    void greet() {
        std::cout << "Hello, I'm a bike";
    }
};
void receiveVehicle(Vehicle vehicle) {
    vehicle.greet();
}
int main() {
    receiveVehicle(Car());
    return 0;
}

如您所见,我正在尝试将 Vehicle 类型的参数发送到调用 greet() 的函数。

CarBikeVehicle 的子类。他们覆盖greet().

但是,我得到"你好,我是一辆车"。

我想这是因为receiveVehicle接收Vehicle类型的参数,而不是像 CarBike 这样的特定子类。但这就是我想要的:我希望这个函数适用于Vehicle的任何子类。

为什么我没有得到预期的输出?

您的代码有两个问题:

1) 调用 receiveVehicle(Car()); 参数时被值接受。这意味着发生了与切片相关的问题 - 调用Vehicle的默认复制构造函数,该构造函数从您的汽车构造Vehicle。更改为指针或引用,使其在缩进时工作。

例如:

void receiveVehicle(Vehicle& vehicle) {
    vehicle.greet();
}
int main() {
    Car aCar;
    receiveVehicle(aCar);
    return 0;
}

2) 仅当基方法标有 virtual 关键字时,才会进行多态调用。所以你需要greet虚拟化:

class Vehicle { 
public:
    virtual void greet() {
        std::cout << "Hello, I'm a vehicle";
    }
};

为了严格起见,您还可以根据需要使用const

class Vehicle { 
public:
    virtual void greet() const {   //change it also in subclasses
        std::cout << "Hello, I'm a vehicle";
    }
};
void receiveVehicle(const Vehicle& vehicle) {
    vehicle.greet();
}

只有指针和引用可以是多态的。您正在经历切片,其中基类是从派生类构造的,并失去其作为派生和所有额外数据成员的标识。

tl;dr:更改您的函数以接受Vehicle&(并使参数成为非临时参数),它将正常工作。此外,默认情况下函数是非虚拟的,因此您需要在基类中的函数定义之前添加单词 virtual,例如 virtual void greet() { ... }(感谢 Diego 注意到)。

详细解释

请记住,当您有一个值时,编译器必须知道要为其分配多少内存。派生类可以大于基类,因此当您从派生类构造基类时,它会丢失(切片)派生类携带的数据,并且仅保留基类数据。

即使派生类没有成员,因此不大于基类,编译器也知道值不能是多态的,因此它不必费心从实例的 vtable 中查找虚函数。它只会直接调用函数,导致静态(非多态)行为,并调用基类函数。

想想如果编译器确实调用了虚拟的,会发生什么:this指针将指向一个没有派生数据的对象,因为它被切掉了,当函数尝试访问派生成员变量时,它们将不存在!

void receiveVehicle(const Vehicle &vehicle) {
    vehicle.greet();
}
// Make your `greet` method `const`

通过引用(或指针,如果您知道风险)传递它,让多态性起作用。否则Car将被切片成Vehicle