两个相关的类层次结构 - 覆盖虚函数

Two related class hierarchies - overriding virtual functions

本文关键字:层次结构 覆盖 函数 两个      更新时间:2023-10-16

假设我有这个基类:

struct Vehicle {
  //"op" stands for the operator of the vehicle
  //examples: pilot, truck driver, etc.
  virtual void insert_op(Op op) = 0;
  //other members...
};

而这两个子类

struct Truck : public Vehicle {
  void insert_op(Op op) override {
    //prepare Truck with truck driver
  }
};
struct Airplaine : public Vehicle {
  void insert_op(Op op) override {
    //prepare Airplaine with pilot
  }
};

正如您所猜测的,这是另一个层次结构:

struct Op {};
struct TruckDriver : public Op {};
struct Pilot : public Op {};

你已经看到了问题,不是吗?我想让FORCE Truck只接受TruckDrivers,让FORCE airplaines只接受飞行员,但这在当前的设计中是不可能的。C++ 不允许对被覆盖的虚拟使用差异参数。

我想我可以对insert_op的每个子类实现中的"Op"类型进行运行时类型检查,但这听起来像是一个非常丑陋的解决方案,而且它在编译时没有强制执行。

有什么出路吗?

您的Vehiclevirtual void insert_op(Op op),意思是"每辆车都可以接受任何Op"。

因此,根据您的设计,Truck不是Vehicle子类的有效候选项,因为它不能接受任何Op - 它只能接受TruckDriver s。

相关:利斯科夫替代原理


问题在于您的设计,而不是它的实现。我建议简化您的类层次结构。你真的需要这么多的类和继承吗?您可以简单地使用具有标识其类型的字段的VehicleOp吗?


让我进一步解释一下设计问题:

假设某个对象A与方法manVehicle(Vehicle&) . Truck 是 Vehicles 的子类,因此可以使用类型 Truck 的对象调用此方法。

但是,A的实现并不知道Vehicle的具体类型。它只知道所有车辆都有一个方法insert_op(Op),所以即使车辆实际上是Truck,它也可以尝试像insert_op(Pilot())这样的调用。

结论:

  • 甚至不可能进行编译时检查

  • 运行时检查可以工作...

    • 但只会把问题扫到地毯下。Vehicle 的用户希望能够在任何Vehicle上呼叫insert_op(Op)

解决方案是将Vehicle界面修改为如下所示:

struct Vehicle {
  virtual bool can_insert_op(Op op) = 0;
  virtual void insert_op(Op op) = 0;
};

并记录下来,以便调用方知道insert_op只能使用满足给定Vehicle can_insert_opOp调用。或类似的东西(例如来自"此车辆的无效操作类型"insert_op记录的异常( - 只要它是此接口的记录部分,就可以正常工作。


顺便说一句,技术评论:您可能希望这些方法通过指针或引用获取Op而不是复制它,以避免不必要的复制和切片。

每个载具子类都应创建其相应的运算符。不应有设置运算符的方法。车辆可以有一个get_operator的方法,但仅此而已。

我猜这不是你实际的层次结构(除非你正在创建一个游戏或其他东西(。如果您显示实际层次结构,则可能有助于建议更好的解决方案。

找不到

反对 OOD。 如果卡车司机是司机的孩子,那么它就是司机,但有其他功能。 在您的情况下,不允许司机,那么它不是副卡车司机。如果你想坚持当前的设计,你需要按照你的假设在fn的开头进行检查。多态性旨在@compile时间不出错,因此它在运行时是动态绑定,不依赖于编译时间。

怎么样:

struct Vehicle {
  //"op" stands for the operator of the vehicle
  //examples: pilot, truck driver, etc.
  virtual void insert_op(Op op) = 0;
  virtual bool validate_op(Op op);
  //other members...
};
struct Truck : public Vehicle {
  void insert_op(Op op) override {
    if (validate_op(op)) {
        //prepare Truck with truck driver
    }
  }
  bool validate_op(Op op) override {
    //check if this is a valid truck driver
    return ( typeid(op)==typeid(TruckDriver) );
  }
};

您将能够保留insert_op的通用定义,并对其进行一些验证。

你想要完成的是合法的,但是,没有对良好解决方案的支持。 这不是C++本身的问题,而是面向对象的问题:

Vehicle 开始的类层次结构相对于 Op 的层次结构是协变的。 如果你有一个燃料等级,你会遇到同样的问题。 或行动的国籍等。

其他人已经告诉您如何使用运行时检查完成此操作的解释,但是当然,总有一些需要的地方。

如果要完成完整的编译时检查,并且想要的多态性是在编译时而不是运行时,则可以使用泛型编程模板。