【问题标题】:virtual methods C++虚拟方法 C++
【发布时间】:2015-01-15 20:40:54
【问题描述】:

我面临以下问题。

我实现了父类 - Vehicle,它有一些派生类,其中之一 - FastVehicle

在程序中,我需要存储一个 Vehicle* 指针向量。指针可能指向 Vehicle 对象或 FastVehicle 对象。

1) 我希望能够为向量中的每个对象调用 print() 方法。 问题是,在 FastVehicle 的情况下,我还想将参数传递给函数, 我需要调用一个带有签名的函数:

void print(int a)

我对虚函数机制略知一二,但据我所知,它只有在两个函数具有相同签名的情况下才有效。

我想听听关于如何解决的建议。

2) 另外在派生类FastVehicle 中还有一些独特的功能,它不与父类Vehicle 共享。 它执行的任务应该只对 FastVehicle 对象执行。 实现这一目标的最干净的方法是什么? 我想也许可以在父类 Vehicle 中实现“空”虚函数,并在 FastVehicle

的覆盖方法中实现“真实”任务

也许有人可以提出更好的解决方案。

谢谢

【问题讨论】:

  • 要么实现通用接口,要么使用dynamic_cast。
  • dynamic_cast 是如果您真的希望派生类具有不兼容的接口的答案。如果可以的话,定义一个通用接口会更简洁。
  • 不得不使用dynamic_cast 通常表明你考虑得不够充分。
  • 您应该首先考虑您的设计。 FastVehicle 能做哪些车辆不能做的事情?如果它只是跑得更快,那么 Vehicle 应该有一个 speed 成员,而更快的车辆只是将它设置得更高。
  • 一个类似的问题 - 也涉及汽车 :) - 可以在这里找到:stackoverflow.com/questions/7200446/… IMO,这是一个完全合法的向下转换用例。

标签: c++ inheritance virtual-functions


【解决方案1】:

您始终可以使用 dynamic_cast 将 Vehicle 转换为 FastVehicle。如果 Vehicle 不是 FastVehicle,则返回 NULL。是否真的应该这样做,取决于您的使用情况。

for(Vehicle* vehicle : vehicleVector)
{
    FastVehicle* fastVehicle = dynamic_cast<FastVehicle*>(vehicle);

    if(fastVehicle)
    {
        fastVehicle->print(1337);
        fastVehicle->somethingElse();
    }
    else
    {
        vehicle->print();
    }
}

此处提供完整示例:https://ideone.com/69n6Jb

【讨论】:

  • 感谢您的回答。父类是否需要至少有一个虚函数才能执行dynamic_cast?
  • @Day_Dreamer 是的。
  • 不要认为那行得通。我认为 dynamic_cast 横向投射(在兄弟姐妹之间)就好了,所以你永远不会碰到 else。
【解决方案2】:

您很可能必须重新考虑为什么需要 FastVehicle 的参数,而不是其他类型的车辆。对我来说,这表明设计不好。

只需在基类中声明 print(int),覆盖它,但在不需要 int 的类中,忽略它。

【讨论】:

    【解决方案3】:

    务实的解决方案是:

    1. int a 参数传递给虚拟print 方法,但在Vehicle 中忽略它,只在FastVehicle 中使用它

    2. 按照您的建议,只需向基类添加一个“空”虚函数,该函数在 Vehicle 中是无操作的,并且仅在 FastVehicle 中实现

    例如:

    struct Vehicle {
      virtual ~Vehicle(){}
      virtual void print(int /*a*/) const { std::cout << "Vehicle print\n"; }
      virtual void somethingElse() { /* no-op */ }
    };
    
    struct FastVehicle : Vehicle {
      void print(int a) const override {std::cout << "FastVehicle print " << a << "\n";}
      void somethingElse() override { std::cout << "Something else!\n"; }
    };
    
    for (auto vehicle : vehicles) {
      vehicle->print(512);
      vehicle->somethingElse();
    }
    

    Live demo

    【讨论】:

      【解决方案4】:

      也许你可以用抽象vehicleI重构:

      struct vehicleI {
          ....
          virtual void print(int) = 0;
      }
      

      然后是你的vehicle:

      struct vehicle : vehicleI {
          ....
          void print(int i = 0);
      }
      

      还有你的fastVehicle

      struct fastvehicle: vehicleI {
          ....
          void print(int);
      }
      

      【讨论】:

      • 请注意,如果您在 vehicleI 指针上调用 print,则不会使用 i=0 默认参数,因为默认参数基于静态类型。就个人而言,我会完全避免使用默认参数以避免混淆。
      • @ChrisDrew 好点,应该是 printf 并接受可变数量的参数。或者成为某种流......
      【解决方案5】:

      如果您想正确使用对 Vehicle 接口的动态调用,您需要定义一个通用接口。 如果您需要在 FastVehicle 的情况下指定参数,而在 FastVehicle 的情况下不需要指定参数,则不再是接口。

      您有两种解决方案:

      默认参数

      struct Vehicle
      {
        virtual void print(int a=0) {};
      };
      
      struct FastVehicle : public Vehicle
      {
        void print(int a=0) override {};
      };
      

      现在您可以使用或不使用参数来调用。

      第二个选项:

      struct Vehicle
      {
        virtual void print() {};
      };
      
      struct FastVehicle : public Vehicle
      {
        void print() override {};
        void setA(int a) { _a = a; }
        _a{};
      };
      

      现在您可以通过其他方法设置“a”变量,但不能通过 Vehicle 的接口访问对象时设置。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-09-09
        • 1970-01-01
        • 2012-11-30
        • 2016-09-28
        • 2011-12-19
        • 2010-11-17
        • 1970-01-01
        相关资源
        最近更新 更多