成员变量多态性和参数引用
member variable polymorphism & argument by reference
我是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
)决定在编译时调用哪个函数。
您需要一个引用或指针,以便通过动态调度在运行时通过实例的动态类型(例如ThreeDoorCar
或TwoDoorCar
等)的虚函数表查找正确的虚函数来多态处理。多态调用行为是通过指针或引用与虚函数声明相结合实现的。这或多或少是语法上使用值与指针/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_;
};
- 将const引用参数初始化为默认参数会导致悬空引用吗
- 通过常量引用传递参数的矩阵模板类
- 非类型指针和引用模板参数,以及在编译时如何/为什么解析它们.c++
- 何时应通过引用传递矢量参数而不是按值传递矢量参数?
- 具有常量引用参数的函数模板专用化
- 作为参数引用
- 在 cpp 文件中隐藏采用模板参数引用的方法
- 如何传递带有通过引用传递的结构参数的函数?
- 将输入参数作为右值引用传递?
- 使用引用与指针将数组作为参数传递
- 根据模板参数引用不同基类的函数
- 默认情况下C++数组作为参数引用
- 对指针C++的参数引用
- 作为非类型模板参数引用
- 重用返回语句或按参数引用返回
- 将参数引用传递给函数C++
- 从另一个参数引用的模板可变参数
- 成员变量多态性和参数引用
- 按参数引用或按参数引用之间的差异
- 无法实例化抽象类:为什么模板参数(引用)导致这种情况