该主题中的问题提出了一个非常常见的混淆。这种混淆很常见,C++ FAQ长期以来一直反对使用私有虚拟,因为混淆似乎是一件坏事。
所以首先要摆脱混淆:是的,私有虚函数可以在派生类中被覆盖。派生类的方法不能从基类调用虚函数,但可以为它们提供自己的实现。根据 Herb Sutter 的说法,在基类中具有公共的非虚拟接口和可以在派生类中定制的私有实现,可以更好地“将接口规范与实现的可定制行为规范分离”。您可以在他的文章“虚拟性”中阅读更多相关信息。
然而,在我看来,您提供的代码中还有一件更有趣的事情值得更多关注。公共接口由一组重载的非虚函数组成,这些函数调用非公共、非重载的虚函数。像往常一样,在 C++ 世界中,它是一个习语,它有一个名字,当然它也很有用。名字是(惊喜,惊喜!)
“公共重载非虚拟调用受保护非重载虚拟”
它有助于正确管理隐藏规则。你可以在这里阅读更多关于它的信息,但我会尽快解释它。
想象一下,Engine
类的虚函数也是它的接口,它是一组非纯虚的重载函数。如果它们是纯虚拟的,仍然会遇到同样的问题,如下所述,但在类层次结构中较低。
class Engine
{
public:
virtual void SetState( int var, bool val ) {/*some implementation*/}
virtual void SetState( int var, int val ) {/*some implementation*/}
};
现在让我们假设您要创建一个派生类,并且您只需要为该方法提供一个新的实现,它需要两个整数作为参数。
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*/}
};