私有纯虚函数的意义何在?

我在一个头文件中看到了以下代码:

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;
};

对我来说,这意味着无论是 Engine类还是从它派生的类,都必须为这些纯虚函数提供实现。但是我不认为派生类可以访问这些私有函数来重新实现它们——那么为什么要让它们成为虚函数呢?

52989 次浏览

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

私有虚方法用于限制可以重写给定函数的派生类的数量。必须重写私有虚方法的派生类必须是基类的朋友。

可以找到 DevX.com的简要说明。


模板方法中有效地使用了一个私有的虚拟方法。派生类可以重写私有虚方法,但是派生类不能调用它的基类私有虚方法(在您的示例中为 SetStateBoolSetStateInt)。只有基类可以有效地调用其私有虚拟方法(只有当派生类需要调用虚函数的基实现时,才使虚函数受到保护)。

可以找到一篇关于 < strong > Virtuality 的有趣的文章。

私有的纯虚函数 非虚拟界面习惯用法的基础(好吧,它并不总是 纯洁虚拟的,但仍然是虚拟的)。当然,这也用于其他事情,但我发现这是最有用的(: 两个词: 在一个公共函数中,你可以把一些常见的事情(如日志记录、统计数据等)放在函数的开头和结尾,然后,“在中间”调用这个私有的虚函数,这将不同于特定的派生类。比如:

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()
{
// ..
}

纯虚拟 -只是强制派生类实现它。

编辑: 更多相关内容: 维基百科: : NVI-idiom

编辑: 关于重写能力和访问/调用能力的澄清语句。

它将能够覆盖这些私有函数:

#include <iostream>


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


void SetState( int var, int val )
{
SetStateInt( var, val );
}


private:


virtual void SetStateBool(int var, bool val ) = 0;
virtual void SetStateInt(int var, int val ) = 0;


};


class DerivedEngine : public Engine
{
private:
virtual void SetStateBool(int var, bool val )
{
std::cout << "DerivedEngine::SetStateBool() called" << std::endl;
}


virtual void SetStateInt(int var, int val )
{
std::cout << "DerivedEngine::SetStateInt() called" << std::endl;
}
};




int main()
{
DerivedEngine e;
Engine * be = &e;


be->SetState(4, true);
be->SetState(2, 1000);
}

类似于代码中的基类中的 Private virtual方法通常用于实现 模板方法设计模式。该设计模式允许在不更改基类中的代码的情况下更改基类中算法的行为。上面的代码是基类方法通过基类指针调用的一个简单模板方法。

这个主题中的问题表明了一个相当普遍的困惑。这种混淆是很常见的,C + + 常见问题解答长期以来一直主张反对使用私有虚拟,因为混淆似乎是一件坏事。

因此,首先要消除混淆: 是的,可以在派生类中重写私有虚函数。派生类的方法不能从基类调用虚函数,但可以为它们提供自己的实现。根据 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*/}
};

现在让我们假设您想要创建一个派生类,并且只需要为这个方法提供一个新的实现,该方法接受两个 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*/}
};

DR 回答:

您可以像对待另一个封装级别一样对待它——在 受到保护二等兵之间的某个位置: 您不能从子类调用它,但是您可以覆盖它。

它在实现 模板方法设计模式时非常有用。您可以使用 受到保护,但是 二等兵虚拟的一起可能被认为是更好的选择,因为它们具有更好的封装。