1

假设我有这个基类:

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 {};

你已经看到问题了,不是吗?我想强制卡车只接受卡车司机和强制飞机只接受飞行员,但这在当前设计中是不可能的。C++ 不允许被覆盖的虚拟变量的 diff 参数。

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

有什么出路吗?

4

5 回答 5

3

Vehiclevirtual void insert_op(Op op)这意味着“每辆车都可以接受任何Op”。

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

相关:里氏替换原理


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


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

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

但是, 的实现A并不知道Vehicles 的具体类型是什么。它只知道所有车辆都有一个方法insert_op(Op),因此insert_op(Pilot())即使车辆实际上是一个Truck.

结论:

  • 编译时检查甚至是不可能的

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

    • 但只会把问题扫到地毯下。s的用户Vehicle希望能够调用insert_op(Op)任何Vehicle.

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

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

并将其记录下来,以便调用者知道insert_op只能用Op满足can_insert_op给定的 s 来调用它Vehicle。或类似的东西(例如insert_op“此车辆的无效操作类型”中的记录异常) - 只要它是此接口的记录部分,任何东西都可以工作。


BTW 技术评论:您可能希望这些方法采用Opby 指针或引用而不是复制它,以避免不必要的复制和切片

于 2013-02-09T11:06:28.620 回答
0

Vehicle 子类应各自创建其适当的 Operator。应该没有设置运算符的方法。Vehicle 可以有一个 get_operator 方法,但仅此而已。

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

于 2013-02-09T11:12:58.287 回答
0

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

于 2013-02-09T11:27:18.383 回答
0

关于什么:

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对它的一些验证的通用定义。

于 2013-02-09T11:37:57.017 回答
0

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

从 开始的类层次结构与 的层次结构Vehicle协变Op的。如果你有燃料的层次结构,你也会遇到同样的问题。或者 Ops 的国籍等。

其他人已经告诉您如何通过运行时检查来完成此操作,但当然,总有一些需要改进的地方。

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

于 2017-08-29T06:22:15.627 回答