私人纯虚函数的意义何在?
我在头文件中遇到以下代码:
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一个简单的解释。
编辑在模板方法模式中有效地使用私有虚拟方法。 派生类可以重写私有虚拟方法,但派生类不能调用它的基类私有虚拟方法(在你的例子中, SetStateBool
和SetStateInt
)。 只有基类可以有效地调用它的私有虚方法( 只有派生类需要调用虚函数的基本实现,才能保护虚函数 )。
一个有趣的文章可以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模式的一个简单示例。