有没有办法不继承子类中函数的“虚拟性”?

时间:2011-08-18 04:28:32

标签: c++ virtual

在C ++中是否有可能让一个类覆盖一个虚函数,但只有当通过超类调用该函数时才有虚拟调度(即,当它被静态类型化为子类时调用它)?我知道这不会发生什么,但有什么办法可以实现一些接近的目标吗?

想要这个的原因是我有两个类都暴露了flush()函数。在我的程序中绝大多数时间,我直接在我知道类型的子类对象上调用flush(),所以我不需要虚拟调度。但是我想在混合中添加一个超类,所以很少我可以将对其中一个类的实例的引用传递给doSomethingThenFlush()函数,这将虚拟地调用flush()

我知道我可以使用模板而不是虚函数,我知道我可以有两个不同的函数(例如。flushVirtual()刚刚调用flushNonVirtual(),并且在我不知道的地方调用flushNonVirtual()不需要虚拟调度。但这些似乎都有点像在一个主要是语法的问题上抛出代码。有没有更优雅的方法来实现这个目标?

也许更重要的是,有谁知道为什么虚拟化在C ++中被继承?

struct Base
{
  virtual ~Base(){}
  virtual void func();
};

struct Derived : public Base
{
  void func(){}
};

void callVirtually(Base &base)
{
  base.func();//this will use virtual dispatch
}

void callStatically(Derived &derived)
{
  derived.func();//I don't want/need this to use virtual dispatch
}

int main()
{
  Derived derived;
  callVirtually(derived);
  callStatically(derived);
}

4 个答案:

答案 0 :(得分:8)

虚拟性是继承的,因为您不知道某人是否会从您的Derived进一步推导出来。有人也可以制作一个MoreDerived,可以传递给一个期待Derived&的函数,当他们发现Derived的所有版本都是MoreDerived时,他们会很难过虚拟函数被调用而不是Derived的。

如果你的意思是你不会继承{{1}}所以你不想为虚拟函数调用付费,那么你运气不好,因为C ++无法承诺你不会从一个类继承,这是必要的,你可以做你想要的。

答案 1 :(得分:5)

在C ++ 03中,没有。

正如其他人所说,每当它可以评估对象的运行时类型时,它就是一个编译器优化(以及一个经常使用的编译器优化)来调用它。

但是,在C ++ 0x中,我们得到两个新的关键字:overridefinal,两者都可以应用于成员函数(final也可以应用于类)。

  • override:指定此函数覆盖基类中的虚函数,对于在不是这种情况时发出警告非常有用
  • final:指定在子类中不能覆盖此函数(虚拟)。

你的课程将成为:

struct Derived : public Base
{
  void func() final {}
};

注意:使用 final 并不要求编译器对函数调用进行虚拟化(从标准的角度来看),但任何值得盐的编译器都应该这样做。 < / p>

答案 2 :(得分:4)

在您的具体示例中,如果callStatically被内联,编译器可能会避免虚函数调度,因为它可以看到对象的实际类型(因为它是局部变量)。

对于这样的情况,您的编译器也可以避免虚拟调度:

class Foo {
public:
    callStatically() { d.func() }
private:
    Derived d;
};

无论callStatically是否内联,编译器都可能执行此优化,因为它可以看到变量成员的实际类型。

但据我所知,没有办法强制编译器绕过虚拟调用。

答案 3 :(得分:3)

答案在于你的问题。

derived.func();  // no virtual dispatch

使用object调用virtual函数时,没有virtual调度。它使用对象的静态类型调用函数。

仅当您尝试使用指针引用调用函数时,

virtual函数才会出现在图片中。

修改:在您更新的问题中,您正在使用Derived&来致电func()。通过引用调用将确保virtual调度发生。因此,没有语言功能(如Java中的final),这将停止virtual调度。

相关问题