虚拟方法 C++

Posted

技术标签:

【中文标题】虚拟方法 C++【英文标题】:virtual methods 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 成员,而更快的车辆只是将它设置得更高。 一个类似的问题 - 也涉及汽车 :) - 可以在这里找到:***.com/questions/7200446/… IMO,这是一个完全合法的向下转换用例。 【参考方案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】:

务实的解决方案是:

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

    按照您的建议,只需向基类添加一个“空”虚函数,该函数在 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 的接口访问对象时设置。

【讨论】:

以上是关于虚拟方法 C++的主要内容,如果未能解决你的问题,请参考以下文章

防止 C++ 中的虚拟方法实现

调用非虚拟基方法时,C++ 中的虚拟继承是不是有任何惩罚/成本?

虚拟方法 C++

C++ 虚拟方法未按预期调用

C++ 虚拟方法:我必须在父类中为子级和父级不共享的每个方法创建一个虚拟方法吗?

在 C++ 中调用受保护的虚拟方法 [重复]