私有纯虚函数有什么意义?
我在头文件中遇到了以下代码:
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
类或从它派生的类必须提供这些纯虚函数的实现。但我不认为派生类可以访问这些私有函数来重新实现它们 - 那么为什么要使它们虚拟呢?
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
该主题中的问题表明了一个相当常见的困惑。这种混乱很常见,C++ FAQ 很长一段时间以来都提倡反对使用私有虚拟,因为混乱似乎是一件坏事。
因此,首先要摆脱混乱:是的,私有虚函数可以在派生类中被重写。派生类的方法不能从基类调用虚函数,但可以为它们提供自己的实现。根据 Herb Sutter 的说法,在基类中拥有公共非虚拟接口和可以在派生类中定制的私有实现,可以更好地“将接口规范与实现的可定制行为规范分开”。您可以在他的文章“虚拟性”中阅读更多相关信息。
然而,在我看来,您提供的代码中有一件更有趣的事情值得更多关注。公共接口由一组重载的非虚函数组成,这些函数调用非公共、非重载的虚函数。与 C++ 世界中的惯例一样,它是一个惯用语,它有一个名称,当然它也很有用。名称是(惊讶,惊讶!)
“公共重载非虚拟调用受保护的非重载虚拟”
它有助于 正确管理隐藏规则。您可以在此处阅读更多相关信息,但我会尝试简短地解释一下。
想象一下,
Engine
类的虚函数也是它的接口,它是一组非纯虚函数的重载函数。如果它们是纯虚拟的,人们仍然可能遇到相同的问题,如下所述,但在类层次结构中较低。现在假设您想要创建一个派生类,并且您需要仅为该方法提供一个新的实现,该实现采用两个 int 作为参数。
如果您忘记将 using 声明放入派生类中(或重新定义第二个重载),则可能会在下面的场景中遇到麻烦。
如果您没有阻止隐藏
Engine
成员,则语句:将从派生类调用
void SetState( int var, int val )
,转换true
到int
。如果接口不是虚拟的并且虚拟实现是非公共的,就像在您的示例中一样,派生类的作者就少了一个需要考虑的问题,并且可以简单地编写
The question in the topic suggest a pretty common confusion. The confusion is common enough, that C++ FAQ advocated against using private virtuals, for a long time, because confusion seemed to be a bad thing.
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".
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!)
"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.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.
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.
If you didn't prevent the hiding of the
Engine
members, the statement:would call
void SetState( int var, int val )
from the derived class, convertingtrue
toint
.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
私有纯虚拟函数是非虚拟接口习惯用法的基础(好吧,它并不绝对总是纯虚拟虚拟,但仍然是虚拟的) 。当然,这也用于其他事情,但我发现这是最有用的(:用两个词来说:在公共函数中,您可以将一些常见的东西(例如日志记录,统计数据等)放在开头和在函数的末尾,然后在“中间”调用此私有虚函数,这对于特定的派生类来说会有所不同,例如:
纯虚拟 - 只是要求派生类执行 以下操作: 编辑
:更多信息:维基百科::NVI-习语
Private pure virtual function is the base of the Non-virtual interface idiom (OK, it's not absolutely always pure virtual, 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:
Pure virtual - just obligates the derived classes to implement it.
EDIT: More about this: Wikipedia::NVI-idiom
嗯,一方面,这将允许派生类实现基类(包含纯虚函数声明)可以调用的函数。
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.
编辑:澄清了有关覆盖能力和访问/调用能力的声明。
它将能够覆盖这些私有函数。例如,以下人为的示例有效(编辑:将派生类方法设为私有,并在
main()
中删除派生类方法调用,以更好地演示使用中的设计模式的意图。< /strong>):基类中的
私有
虚拟
方法(如代码中的方法)通常用于实现模板方法设计模式。该设计模式允许人们在不更改基类中的代码的情况下更改基类中算法的行为。上面通过基类指针调用基类方法的代码是模板方法模式的一个简单示例。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.):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.私有虚方法用于限制可以重写给定函数的派生类的数量。必须重写私有虚拟方法的派生类必须是基类的友元。
可以在 DevX.com 找到简要说明。
编辑私有虚拟方法在模板方法模式中有效使用。派生类可以重写私有虚拟方法,但派生类无法调用其基类私有虚拟方法(在您的示例中为
SetStateBool
和SetStateInt
)。只有基类才能有效地调用其私有虚方法(仅当派生类需要调用虚函数的基实现时,才使虚函数受保护)。可以找到一篇关于虚拟性的有趣文章。
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.
EDIT A 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
andSetStateInt
). 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).An interesting article can be found about Virtuality.
TL;DR 答案:
您可以将其视为另一个封装级别 - 介于 protected 和 private 之间:您不能从子类中调用它,但可以覆盖它。
它在实现模板方法设计模式时非常有用。您可以使用protected,但private与virtual一起可能被认为是更好的选择,因为封装更好。
TL;DR answer:
You can treat it like another level of encapsulation - somewhere between protected and private: you can't call it from child class, but you can override it.
It is useful when implementing Template Method design pattern. You could use protected, but private together with virtual may be considered as better choice, because of better encapsulation.