成员变量多态性和参数引用

member variable polymorphism & argument by reference

本文关键字:参数 引用 多态性 变量 成员      更新时间:2023-10-16

我是c++新手,有一个关于成员变量多态性的问题。我有以下类定义-

class Car
{
    public:
        Car();
        virtual int getNumberOfDoors() { return 4; }
};
class ThreeDoorCar : public Car
{
    public:
        ThreeDoorCar();
        int getNumberOfDoors() { return 3; }
};
class CarPrinter
{
    public:
        CarPrinter(const Car& car);
        void printNumberOfDoors();
    protected:
        Car car_;
};

和实现

#include "Car.h"
Car::Car()
{}
ThreeDoorCar::ThreeDoorCar()
{}
CarPrinter::CarPrinter(const Car& car)
: car_(car)
{}
void CarPrinter::printNumberOfDoors()
{
    std::cout << car_.getNumberOfDoors() << std::endl;
}

问题是当我运行以下代码时,调用父类的getNumberOfDoors。我可以通过使成员变量Car成为一个指针来解决这个问题,但是我更喜欢通过引用而不是指针来传递输入(我认为这是首选)。你能告诉我哪里做错了吗?谢谢!

ThreeDoorCar myThreeDoorCar;
std::cout << myThreeDoorCar.getNumberOfDoors() << std::endl;
CarPrinter carPrinter(myThreeDoorCar);
carPrinter.printNumberOfDoors();

通过复制该对象,你牺牲了它的多态能力。无论您传递的是什么类型的car,副本都将是类型为Car(基类)的,因为它是这样声明的。

如果想继续使用多态性,可以使用指针或引用。下面是使用引用的版本:

class CarPrinter
{
public:
    CarPrinter(const Car& car);
    void printNumberOfDoors();
protected:
    const Car &car_;     // <<= Using a reference here
};
如您所见,这样可以继续使用以引用作为实参的构造函数。(这些引用不一定是const,尽管const是有意义的,只要CarPrinter的目的只是打印。)

这样做的一个潜在的不良副作用是,在构造CarPrinter对象之后,您不能更改引用所引用的内容。如果需要打印不同对象的信息,则必须为此创建一个新的CarPrinter对象。这些对象实际上只是作为引用的包装(可能是短暂的)。

如果您不喜欢这样,您仍然可以继续向构造函数传递引用,但是通过在构造函数实现中获取其地址并存储该地址,将其转换为指针。

当你这样做的时候:

Car m_car;

它不会以多态方式处理m_car实例,即使Car有子类和虚函数。它将只使用Car函数。这被称为静态绑定——它根据静态类型 (Car)决定在编译时调用哪个函数。

您需要一个引用或指针,以便通过动态调度在运行时通过实例的动态类型(例如ThreeDoorCarTwoDoorCar等)的虚函数表查找正确的虚函数来多态处理。多态调用行为是通过指针或引用与虚函数声明相结合实现的。这或多或少是语法上使用值与指针/refs的直接结果(参见下面@kfmfe04的评论)。

Car* pCar;
Car& rCar = x_car;

通过指针或引用调用的虚成员(例如pCar->getNumberOfDoors()rCar.getNumberOfDoors())在运行时进行虚表查找(动态调度)。因为只有在运行时它才知道实例的动态类型

但是m_car.getNumberOfDoors()是直接调用的虚成员,编译器在编译时知道直接(static)类型和函数地址,在编译时静态绑定函数地址(Car::getNumberOfDoors)

问题在CarPrinter构造函数的这一行:

: car_(car)

调用编译器为Car类生成的默认复制构造函数,最终创建Car的实例,而不是ThreeDoorCar

不幸的是,您需要传递指针,或者通过引用传递,但存储指针。例如:

class CarPrinter
{
public:
    CarPrinter(const Car& car)
       :car_(&car) {};
...
protected:
    const Car* car_;
};