私有纯虚函数有什么意义?

Bee*_*and 133 c++ oop inheritance non-virtual-interface

我在头文件中遇到了以下代码:

class Engine
{
public:
    void SetState( int var, bool val );
    {   SetStateBool( int var, bool val ); }

    void SetState( int var, int val );
    {   SetStateInt( int var, int val ); }
private:
    virtual void SetStateBool(int var, bool val ) = 0;    
    virtual void SetStateInt(int var, int val ) = 0;    
};
Run Code Online (Sandbox Code Playgroud)

对我来说,这意味着Engine从它派生的类或类必须为那些纯虚函数提供实现.但我不认为派生类可以访问这些私有函数以重新实现它们 - 那么为什么要将它们变为虚拟?

Mac*_*ehl 203

该主题中的问题表明了一个非常常见的混淆.混淆是很常见的,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*/}
};
Run Code Online (Sandbox Code Playgroud)

现在让我们假设你想要创建一个派生类,你需要为该方法提供一个新的实现,它需要两个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*/}
};
Run Code Online (Sandbox Code Playgroud)

如果您忘记将using声明放在派生类中(或重新定义第二个重载),您可能会在下面的场景中遇到麻烦.

MyTurbochargedV8* myV8 = new MyTurbochargedV8();
myV8->SetState(5, true);
Run Code Online (Sandbox Code Playgroud)

如果你没有阻止隐藏Engine成员,声明如下:

myV8->SetState(5, true);
Run Code Online (Sandbox Code Playgroud)

将从void SetState( int var, int val )派生类调用,转换trueint.

如果接口不是虚拟的并且虚拟实现是非公共的,就像在你的例子中那样,派生类的作者只需要考虑一个问题,并且可以简单地编写

class MyTurbochargedV8 : public Engine
{
private:
    void SetStateInt(int var, int val )  {/*new implementation*/}
};
Run Code Online (Sandbox Code Playgroud)

  • +1"公共重载的非虚拟调用受保护的非重载虚拟"xD (10认同)
  • @Rich你可以,但是通过将它们设为非公开,你可以更清楚地传达他们的意图。首先,如果您坚持将接口公开而实现非公开,那么它会显示关注点分离。其次,如果你希望继承类能够调用基实现,你可以将它们声明为 protected;如果您只希望它们提供自己的实现而不调用基础实现,则可以将它们设为私有。 (2认同)

Kir*_*rov 42

私有纯虚函数是非虚拟接口习惯用法的基础(好吧,它不是绝对纯粹的虚拟虚拟,但仍然是虚拟的).当然,这也用于其他事情,但我觉得这最有用(:在两个词中:在公共函数中,你可以在开头放一些常见的东西(比如日志,统计等)在函数的最后,然后,"在中间"调用这个私有虚函数,对于特定的派生类将是不同的.例如:

class Base
{
    // ..
public:
    void f();
private:
    virtual void DerivedClassSpecific() = 0;
   // ..
};
void Base::f()
{
    //.. Do some common stuff
    DerivedClassSpecific();
    //.. Some other common stuff
}
// ..

class Derived: public Base
{
    // ..
private:
    virtual void DerivedClassSpecific();
    //..
};
void Derived::DerivedClassSpecific()
{
    // ..
}
Run Code Online (Sandbox Code Playgroud)

纯虚拟 - 只需要派生类来实现它.

编辑:更多关于此:维基百科:: NVI-idiom


Mic*_*eyn 17

好吧,首先,这将允许派生类实现基类(包含纯虚函数声明)可以调用的函数.

  • **只有**基类可以调用! (5认同)