开发者

What is the point of a private pure virtual function?

开发者 https://www.devze.com 2023-01-20 16:12 出处:网络
I came across the following code in a header file: class Engine { public: void SetState( int var, bool val );

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;    
  开发者_StackOverflow中文版  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?


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.

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.

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

If you didn't prevent the hiding of the Engine members, the statement:

myV8->SetState(5, true);

would call void SetState( int var, int val ) from the derived class, converting true to 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*/}
};


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:

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


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.


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.):

#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 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 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).

An interesting article can be found about Virtuality.


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消