在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);
}
答案 0 :(得分:8)
虚拟性是继承的,因为您不知道某人是否会从您的Derived
进一步推导出来。有人也可以制作一个MoreDerived
,可以传递给一个期待Derived&
的函数,当他们发现Derived
的所有版本都是MoreDerived
时,他们会很难过虚拟函数被调用而不是Derived
的。
如果你的意思是你不会继承{{1}}所以你不想为虚拟函数调用付费,那么你运气不好,因为C ++无法承诺你不会从一个类继承,这是必要的,你可以做你想要的。
答案 1 :(得分:5)
在C ++ 03中,没有。
正如其他人所说,每当它可以评估对象的运行时类型时,它就是一个编译器优化(以及一个经常使用的编译器优化)来调用它。
但是,在C ++ 0x中,我们得到两个新的关键字:override
和final
,两者都可以应用于成员函数(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
调度。