主题中的问题表明了一个非常普遍的困惑。混乱很常见,C ++ FAQ长期以来一直提倡不使用私有虚拟机,因为混乱似乎是一件坏事。
因此,首先要消除混乱:是的,可以在派生类中重写私有虚拟函数。派生类的方法不能从基类调用虚函数,但是它们可以为其提供自己的实现。根据Herb Sutter的说法,在基类中具有公共非虚拟接口,并可以在派生类中自定义私有实现,可以更好地“将接口的规范与实现的可定制行为的规范分开”。您可以在他的文章“ Virtuality”中了解更多信息。
但是,在我看来,您提供的代码中还有一件有趣的事情,值得更多注意。公用接口由一组重载的非虚拟函数组成,这些函数称为非公用,非重载的虚拟函数。像在C ++世界中一样,它是一个习惯用法,它有一个名称,并且当然有用。名字是(惊喜,惊喜!)
“公共重载的非虚拟机呼叫保护的非重载的虚拟机”
它有助于正确管理隐藏规则。您可以在此处阅读有关此内容的更多信息,但我会尽快对其进行解释。
想象一下,Engine
该类的虚函数也是其接口,并且它是一组重载函数,它们不是纯虚函数。如果它们是纯虚拟的,则仍然可能遇到相同的问题,如下所述,但在类层次结构中较低。
class Engine
{
public:
virtual void SetState( int var, bool val ) {/*some implementation*/}
virtual void SetState( int var, int val ) {/*some implementation*/}
};
现在,假设您要创建一个派生类,并且只需要为该方法提供一个新的实现,该方法需要两个int作为参数。
class MyTurbochargedV8 : public Engine
{
public:
// To prevent SetState( int var, bool val ) from the base class,
// from being hidden by the new implementation of the other overload (below),
// you have to put using declaration in the derived class
using Engine::SetState;
void SetState( int var, int val ) {/*new implementation*/}
};
如果忘记将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 ) {/*new implementation*/}
};