#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()
的函数。
Car
和Bike
是Vehicle
的子类。它们会覆盖greet()
。
然而,我得到“你好,我是一辆车”。
我认为这是因为receiveVehicle
收到类型为Vehicle
的参数,而不是Car
或Bike
等特定子类。但这就是我想要的:我希望这个函数能够与Vehicle
的任何子类一起使用。
为什么我没有得到预期的输出?
答案 0 :(得分:4)
只有指针和引用可以是多态的。您正在经历切片,其中基类是从派生类构造的,并且作为派生类和所有额外数据成员失去其身份。
tl; dr :更改您的函数以接受Vehicle&
(并使参数成为非临时参数),它将正常工作。此外,默认情况下,函数是非虚函数,因此您需要在基类中的函数定义之前添加单词virtual
,例如virtual void greet() { ... }
(感谢Diego注意)。
长解释:
请记住,当您有值时,编译器必须知道要为其分配多少内存。派生类可以比基类大,因此当您从派生类构造基类时,它会丢失(切掉)派生类所携带的数据,并且只保留基类数据。
即使你的派生类没有成员,因此不比基类大,编译器也知道一个值不能是多态的,所以它不会费心从vtable中查找虚函数。实例。它只是直接调用函数,导致静态(非多态)行为,并调用基类函数。
想想如果编译器调用虚拟文件会发生什么:
this
指针会指向一个没有派生数据的对象,因为它被切掉了,当函数尝试时要访问派生成员变量,它们就不会存在!
答案 1 :(得分:4)
您的代码存在两个问题:
1)当您致电receiveVehicle(Car());
时,参数会被值接受。这意味着发生了与slicing相关的问题 - 调用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();
}
答案 2 :(得分:1)
void receiveVehicle(const Vehicle &vehicle) {
vehicle.greet();
}
// Make your `greet` method `const`
通过引用(或指针,如果你知道风险)来传递它让多态性起作用。否则,Car
将被切分为Vehicle
。