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

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

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类还是从它派生的类,都必须为这些纯虚函数提供实现。 但是我不认为派生类可以访问这些私有函数来重新实现它们 – 那么为什么它们是虚拟的呢?

专题中的问题暗示了一个相当普遍的混乱。 这种混淆是很普遍的, C ++ FAQ主张反对使用私有虚拟很长一段时间,因为混淆似乎是一件坏事。

所以为了摆脱困惑:是的,私人虚拟function可以在派生类中重写。 派生类的方法不能从基类调用虚函数,但可以为它们提供自己的实现。 根据Herb Sutter的说法,在基类中具有公共的非虚拟接口和可以在派生类中定制的私有实现,可以更好地将接口规范与实现的可定制行为的规范分离开来。 你可以在他的文章“虚拟性”中阅读更多。

在你提供的代码中有一个更有趣的事情,在我看来,值得更多的关注。 公共接口由一组重载的非虚函数组成,这些函数调用非公共的,非重载的虚函数。 像往常一样在C ++的世界里,这是一个成语,它有一个名字,当然它是有用的。 这个名字是(惊喜,惊喜!)

“公共重载的非虚拟呼叫保护非重载虚拟”

这有助于正确pipe理隐藏规则 。 你可以在这里阅读更多关于它的信息 ,但我会尽快解释它。

想象一下, Engine类的虚函数也是它的接口,它是一组重载函数,不是纯虚函数。 如果它们是纯虚拟的,那么仍然会遇到同样的问题,如下所述,但在类层次结构中较低。

 class Engine { public: virtual void SetState( int var, bool val ) {/*some implementation*/} virtual void SetState( int var, int val ) {/*some implementation*/} }; 

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

如果您忘记将使用声明放在派生类中(或重新定义第二个重载),则可能会在下面的情况中遇到麻烦。

 MyTurbochargedV8* myV8 = new MyTurbochargedV8(); myV8->SetState(5, true); 

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

 myV8->SetState(5, true); 

会从派生类中调用void SetState( int var, int val ) ,将true转换为int

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

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

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

 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的成语

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

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

DevX.com可以find一个简单的解释。


编辑在模板方法模式中有效地使用私有虚拟方法。 派生类可以重写私有虚拟方法,但派生类不能调用它的基类私有虚拟方法(在你的例子中, SetStateBoolSetStateInt )。 只有基类可以有效地调用它的私有虚方法( 只有派生类需要调用虚函数的基本实现,才能保护虚函数 )。

一个有趣的文章可以find关于虚拟

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

它将能够覆盖这些私人function。 例如,下面的devise模式的作品( 编辑:使派生类方法私人,并放弃派生类方法调用在main()以更好地展示devise模式的使用意图。 ):

 #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方法(如代码中的Private virtual方法)通常用于实现“ 模板方法”devise模式 。 该devise模式允许改变基类中algorithm的行为而不改变基类中的代码。 上面的代码通过基类指针调用基类方法是Template Method模式的一个简单示例。

Interesting Posts