0
#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而不是一个特定的子类,比如Caror Bike。但这就是我想要的:我希望这个函数可以与Vehicle.

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

4

3 回答 3

4

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

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

长解释

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

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

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

于 2013-10-20T21:47:05.797 回答
4

您的代码有两个问题:

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

例如:

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();
}
于 2013-10-20T21:47:46.860 回答
1
void receiveVehicle(const Vehicle &vehicle) {
    vehicle.greet();
}
// Make your `greet` method `const`

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

于 2013-10-20T21:48:45.833 回答