C++ 私有纯虚函数的意义何在?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3970279/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is the point of a private pure virtual function?
提问by BeeBand
I came across the following code in a header file:
我在头文件中遇到了以下代码:
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;
};
To me, this implies that either the Engine
class or a class derived from it, has to provide the implementation for those pure virtual functions. But I didn't think derived classes could have access to those private functions in order to reimplement them - so why make them virtual?
对我来说,这意味着Engine
类或从它派生的类必须为那些纯虚函数提供实现。但我不认为派生类可以访问这些私有函数来重新实现它们——那么为什么要让它们成为虚拟的呢?
回答by Maciej Hehl
The question in the topic suggest a pretty common confusion. The confusion is common enough, that C++ FAQadvocated against using private virtuals, for a long time, because confusion seemed to be a bad thing.
该主题中的问题表明了一个非常常见的混淆。这种混淆很常见,C++ FAQ长期以来一直反对使用私有虚拟机,因为混淆似乎是一件坏事。
So to get rid of the confusion first: Yes, private virtual functions can be overridden in the derived classes. Methods of derived classes can't call virtual functions from the base class, but they can provide their own implementation for them. According to Herb Sutter, having public non-virtual interface in the base class and a private implementation that can be customized in the derived classes, allows for better "separation of the specification of interface from the specification of the implementation's customizable behavior". You can read more about it in his article "Virtuality".
所以首先要摆脱混乱:是的,私有虚函数可以在派生类中被覆盖。派生类的方法不能从基类调用虚函数,但可以为它们提供自己的实现。根据 Herb Sutter 的说法,在基类中具有公共非虚拟接口和可以在派生类中自定义的私有实现,允许更好地“将接口规范与实现的可定制行为规范分离”。你可以在他的文章“虚拟”中阅读更多关于它的信息。
There is however one more interesting thing in the code you presented, that deserves some more attention, in my opinion. The public interface consists of a set of overloaded non-virtual functions and those functions call non-public, non-overloaded virtual functions. As usual in the C++ world it is an idiom, it has a name and of course it is useful. The name is (surprise, surprise!)
然而,在我看来,您提供的代码中还有一件更有趣的事情,值得更多关注。公共接口由一组重载的非虚函数组成,这些函数调用非公共、非重载的虚函数。在 C++ 世界中,它是一个惯用语,它有一个名字,当然它很有用。名字是(惊喜,惊喜!)
"Public Overloaded Non-Virtuals Call Protected Non-Overloaded Virtuals"
“公共重载非虚拟设备调用受保护的非重载虚拟设备”
It helps to properly manage the hiding rule. You can read more about it here, but I'll try to explain it shortly.
它有助于正确管理隐藏规则。你可以在这里阅读更多关于它的信息,但我会尽快解释它。
Imagine, that virtual functions of the Engine
class are also its interface and it is a set of overloaded functions that is not pure virtual. If they were pure virtual, one could still encounter the same problem, as described below, but lower in the class hierarchy.
想象一下,Engine
类的虚函数也是它的接口,它是一组不是纯虚的重载函数。如果它们是纯虚拟的,仍然会遇到相同的问题,如下所述,但在类层次结构中较低。
class Engine
{
public:
virtual void SetState( int var, bool val ) {/*some implementation*/}
virtual void SetState( int var, int val ) {/*some implementation*/}
};
Now let's assume you want to create a derived class and you need to provide a new implementation only for the method, that takes two ints as arguments.
现在让我们假设您要创建一个派生类,并且您只需要为该方法提供一个新的实现,该实现将两个整数作为参数。
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*/}
};
If you forgot to put the using declaration in the derived class (or to redefine the second overload), you could get in trouble in the scenario below.
如果您忘记将 using 声明放在派生类中(或重新定义第二个重载),您可能会在下面的场景中遇到麻烦。
MyTurbochargedV8* myV8 = new MyTurbochargedV8();
myV8->SetState(5, true);
If you didn't prevent the hiding of the Engine
members, the statement:
如果你没有阻止隐藏Engine
成员,声明:
myV8->SetState(5, true);
would call void SetState( int var, int val )
from the derived class, converting true
to int
.
将从void SetState( int var, int val )
派生类调用,转换true
为int
.
If the interface is not virtual and the virtual implementation is non-public, like in your exmaple, the author of the derived class has one less problem to think about and can simply write
如果接口不是虚拟的,并且虚拟实现是非公共的,就像你的例子一样,派生类的作者少考虑一个问题,可以简单地写
class MyTurbochargedV8 : public Engine
{
private:
void SetStateInt(int var, int val ) {/*new implementation*/}
};
回答by Kiril Kirov
Private pure virtualfunction is the base of the Non-virtual interfaceidiom (OK, it's not absolutely always purevirtual, but still virtual there). Of course, this is used for other things, too, but I find this for most useful (: In two words: in a public function, you could put some common things (such as logging, statistics, etc.) in the beginning and in the end of the function and then, "in the middle" to call this private virtual function, that will be different for the specific derived class. Something like:
私有纯虚拟函数是非虚拟接口习惯用法的基础(好吧,它并不绝对总是纯虚拟的,但仍然是虚拟的)。当然,这也用于其他事情,但我觉得这最有用(: 两个字:在公共函数中,您可以将一些常见的东西(例如日志记录,统计等)放在开头和在函数的末尾,然后“在中间”调用这个私有虚函数,这对于特定的派生类会有所不同。比如:
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()
{
// ..
}
Pure virtual- just obligates the derived classes to implement it.
纯虚拟- 只需要派生类来实现它。
EDIT: More about this: Wikipedia::NVI-idiom
编辑:更多关于这个:Wikipedia::NVI-idiom
回答by Michael Goldshteyn
Well, for one, this would allow a derived class to implement a function that the base class (containing the pure virtual function declaration) can call.
嗯,一方面,这将允许派生类实现基类(包含纯虚函数声明)可以调用的函数。
回答by Void
EDIT: Clarified statements about ability to override and ability to access/invoke.
编辑:澄清了关于覆盖能力和访问/调用能力的声明。
It will be able to override those private functions. For example, the following contrived example works (EDIT: made derived class method private, and drop the derived class method invocation in main()
to better demonstrate the intent of design pattern in use.):
它将能够覆盖这些私有函数。例如,以下人为示例有效(编辑:将派生类方法main()
设为私有,并删除派生类方法调用以更好地展示使用中的设计模式的意图。):
#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
methods in a base class like the ones in your code are typically used to implement the Template Method design pattern. That design pattern allows one to change the behavior of an algorithm in the base class without changing the code in the base class. The above code where the base class methods are invoked through a base class pointer is a simple example of the Template Method pattern.
Private
virtual
基类中的方法(如代码中的方法)通常用于实现模板方法设计模式。该设计模式允许人们在不更改基类代码的情况下更改基类中算法的行为。上面通过基类指针调用基类方法的代码是模板方法模式的一个简单示例。
回答by Buhake Sindi
Private virtual method is used for limiting the number of derived classes that can override the given function. The derived classes that has to override the private virtual method will have to be a friend of the base class.
私有虚拟方法用于限制可以覆盖给定函数的派生类的数量。必须覆盖私有虚拟方法的派生类必须是基类的朋友。
A brief explanation can be found of DevX.com.
可以在DevX.com上找到简要说明。
EDITA private virtual method is effectively used in Template Method Pattern. The derived classes can override the private virtual method but the derived classes cannot call it's base class private virtual method (in your example, SetStateBool
and SetStateInt
). Only the base class can effectively call its private virtual method (Only if derived classes need to invoke the base implementation of a virtual function, make the virtual function protected).
编辑模板方法模式中有效地使用了私有虚拟方法。派生类可以覆盖私有虚拟方法,但派生类不能调用它的基类私有虚拟方法(在您的示例中,SetStateBool
和SetStateInt
)。只有基类才能有效调用其私有虚方法(只有派生类需要调用虚函数的基实现时,才使虚函数受保护)。
An interesting article can be found about Virtuality.
可以找到一篇关于Virtuality的有趣文章。
回答by jaskmar
TL;DR answer:
TL;博士回答:
You can treat it like another level of encapsulation - somewhere between protectedand private: you can't call it from child class, but you can override it.
你可以把它当作另一个级别的封装——介于protected和private之间:你不能从子类调用它,但你可以覆盖它。
It is useful when implementing Template Methoddesign pattern. You could use protected, but privatetogether with virtualmay be considered as better choice, because of better encapsulation.
它在实现模板方法设计模式时很有用。您可以使用protected,但由于更好的封装,private和virtual可能被认为是更好的选择。