这个话题中的问题表明了一个相当普遍的困惑。这种困惑是足够常见的,以至于
C++ FAQ 长期以来都反对使用私有虚函数,因为困惑似乎是一件坏事。
首先要消除这种困惑:是的,在派生类中可以重写私有虚函数。派生类的方法不能调用基类中的虚函数,但是它们可以为其提供自己的实现。根据Herb Sutter的说法,在基类中具有公共非虚接口和可以在派生类中自定义的私有实现,可以更好地"将接口规范与可定制行为的实现规范分离"。你可以在他的文章
《Virtuality》中了解更多信息。
然而,在你提供的代码中还有一件更有趣的事情,我认为它值得更多的关注。公共接口由一组重载的非虚函数组成,这些函数调用非公开、非重载的虚函数。通常在 C++ 的世界中,这是一种惯用语法,它有一个名字,当然也很有用。这个名字是(惊喜!)
"Public Overloaded Non-Virtuals Call Protected Non-Overloaded Virtuals"
它有助于
正确管理隐藏规则。你可以在
这里阅读更多内容,但我会简要解释一下。
想象一下,
Engine
类的虚函数也是其接口,它是一组不是纯虚的重载函数。如果它们是纯虚的,那么仍然可能遇到下面描述的同样问题,只不过在类层次结构中更低的位置。
class Engine
{
public:
virtual void SetState( int var, bool val ) {}
virtual void SetState( int var, int val ) {}
};
现在假设您想创建一个派生类,并且您需要仅为接受两个整数参数的方法提供新实现。
class MyTurbochargedV8 : public Engine
{
public:
using Engine::SetState;
void SetState( int var, int val ) {}
};
如果你忘记在派生类中加入using声明(或重新定义第二个重载函数),那么在下面的情况下可能会出现问题。
MyTurbochargedV8* myV8 = new MyTurbochargedV8()
myV8->SetState(5, true)
如果您没有防止隐藏 Engine
成员,那么该语句会执行:
myV8->SetState(5, true);
从派生类调用void SetState( int var, int val )
,将true
转换为int
。
如果接口不是虚拟的且虚拟实现是非公共的(就像您的示例一样),那么派生类的作者可以更简单地编写代码,不必考虑一个问题:
class MyTurbochargedV8 : public Engine
{
private:
void SetStateInt(int var, int val ) {}
};