156

What is the advantage of making a private method virtual in C++?

I have noticed this in an open source C++ project:

class HTMLDocument : public Document, public CachedResourceClient {
private:
    virtual bool childAllowed(Node*);
    virtual PassRefPtr<Element> createElement(const AtomicString& tagName, ExceptionCode&);
};
silverburgh
  • 8,659
  • 10
  • 31
  • 24
  • 15
    I think the question is backwards. The reason for making something virtual is always the same: to allow derived classes to override it. So the question should be: what is the advantage of making a virtual method private? To which the answer is: make everything private by default. :-) – ShreevatsaR Apr 02 '14 at 16:58
  • 3
    @ShreevatsaR But you didn't even answer your own question...... – Spencer Mar 01 '18 at 23:35
  • @ShreevatsaR I thought you mean backwards in a different way: What is the advantage of making a virtual method *not* private? – Peter - Reinstate Monica Oct 22 '19 at 08:24

6 Answers6

152

Herb Sutter has very nicely explained it here.

Guideline #2: Prefer to make virtual functions private.

This lets the derived classes override the function to customize the behavior as needed, without further exposing the virtual functions directly by making them callable by derived classes (as would be possible if the functions were just protected). The point is that virtual functions exist to allow customization; unless they also need to be invoked directly from within derived classes' code, there's no need to ever make them anything but private

Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
Prasoon Saurav
  • 91,295
  • 49
  • 239
  • 345
  • As you might guess from my answer, I think Sutter's guideline #3 rather shoves guideline #2 out the window. – Spencer Nov 09 '16 at 14:52
  • 3
    What if a derived class needs to override the method but call the parent method from inside there? That's common enough that I can't imagine private virtuals being recommended if it blocks that. Does C++ have a mechanism like `super(...)` to call the parent method from within an overridden version, which works even if it's private? – flarn2006 Aug 14 '20 at 02:28
  • 1
    @flarn2006 Guideline #3: Only if derived classes need to invoke the base implementation of a virtual function, make the virtual function protected. – luizfls Apr 23 '21 at 07:06
  • But how do you call down to the base function? – user13947194 Jul 02 '22 at 03:33
  • 1
    @user13947194 NameOfBaseClass::NameOfMethod(whatever, arguments, you, need); – Marc Jul 04 '22 at 20:03
  • @Mar But you the method is private. – user13947194 Jul 06 '22 at 01:35
  • @user13947194 Sorry, I assumed you read the link. If you need to call the base method Guideline 3 applies and you make it protected. Or consider rearranging your code so that you don't using the non-virtual interface from Guideline 1. It depends on your situation. – Marc Jul 09 '22 at 16:06
78

If the method is virtual it can be overridden by derived classes, even if it's private. When the virtual method is called, the overridden version will be invoked.

(Opposed to Herb Sutter quoted by Prasoon Saurav in his answer, the C++ FAQ Lite recommends against private virtuals, mostly because it often confuses people.)

luk32
  • 15,812
  • 38
  • 62
sth
  • 222,467
  • 53
  • 283
  • 367
  • 46
    It appears that the C++ FAQ Lite has since changed its recommendation: "_the C++ FAQ formerly recommended using protected virtuals rather than private virtuals. However the private virtual approach is now common enough that confusion of novices is less of a concern._" – Zack The Human Apr 01 '12 at 05:37
  • 37
    Confusion of experts, however, remains a concern. None of the four C++ professionals sitting next to me were aware of private virtuals. – Newtonx Apr 20 '17 at 22:13
14

Despite all of the calls to declare a virtual member private, the argument simply doesn't hold water. Frequently, a derived class's override of a virtual function will have to call the base class version. It can't if it's declared private:

class Base
{
 private:

 int m_data;

 virtual void cleanup() { /*do something*/ }

 protected:
 Base(int idata): m_data (idata) {}

 public:

 int data() const { return m_data; }
 void set_data (int ndata) { m_data = ndata; cleanup(); }
};

class Derived: public Base
{
 private:
 void cleanup() override
 {
  // do other stuff
  Base::cleanup(); // nope, can't do it
 }
 public:
 Derived (int idata): base(idata) {}
};

You have to declare the base class method protected.

Then, you have to take the ugly expedient of indicating via a comment that the method should be overridden but not called.

class Base
{
 ...
 protected:
 // chained virtual function!
 // call in your derived version but nowhere else.
 // Use set_data instead
 virtual void cleanup() { /* do something */ }
 ...

Thus Herb Sutter's guideline #3...But the horse is out of the barn anyway.

When you declare something protected you're implicitly trusting the writer of any derived class to understand and properly use the protected internals, just the way a friend declaration implies a deeper trust for private members.

Users who get bad behavior from violating that trust (e.g. labeled 'clueless' by not bothering to read your documentation) have only themselves to blame.

Update: I've had some feedback that claims you can "chain" virtual function implementations this way using private virtual functions. If so, I'd sure like to see it.

The C++ compilers I use definitely won't let a derived class implementation call a private base class implementation.

If the C++ committee relaxed "private" to allow this specific access, I'd be all for private virtual functions. As it stands, we're still being advised to lock the barn door after the horse is stolen.

Spencer
  • 1,924
  • 15
  • 27
  • 4
    I find your argument invalid. You as a developer of an API should strive for an interface that is **hard** to use incorrectly and not set another developer up for your own mistakes in doing so. What you want to do in your example could be implemented with only private virtual methods. – sigy Jan 18 '17 at 09:18
  • @sigy Then you will just have to post an answer showing how a derived class function can call a private base class function. – Spencer Jan 18 '17 at 14:11
  • 2
    That's not what I was saying. But you can restructure your code to achieve the same effect without the need to call a private base class function – sigy Jan 18 '17 at 14:14
  • 2
    @sigy I.E with a chain of nonvirtual protected "implementation" functions and godawful wrapper functions. Those are contortions that only obfuscate your code. And the nonvirtual functions can be misused just the same. – Spencer Jan 18 '17 at 14:25
  • 5
    In your example you want to extend the behavior of `set_data`. Instructions `m_data = ndata;` and `cleanup();` could thus be considered an invariant that must hold for all implementations. Therefore make `cleanup()` non-virtual and private. Add a call to another private method that is virtual and the extension point of your class. Now there is no need for your derived classes to call base's `cleanup()` anymore, your code stays clean and your interface is hard to use incorrectly. – sigy Jan 18 '17 at 14:38
  • 4
    @sigy That just moves the goalposts. You need to look beyond the miminal example. When there are further descendants that need to call all of the `cleanup()`s in the chain, the argument falls apart. Or are you recommending an extra virtual function for each descendant in the chain? Ick. Even Herb Sutter allowed protected virtual functions as a loophole in his guideline #3. Anyway, without some actual code you'll never convince me. – Spencer Jan 18 '17 at 14:52
  • 3
    Then let's agree to disagree ;) – sigy Jan 18 '17 at 14:54
  • 3
    Both of your arguments fail my check. Cleanup behavior should be extracted from the class, leaving a concrete class with a polymorphic cleanup strategy. Composition should be preferred to inheritance. Even if we look beyond the minimal example, the same apples for any other behavior. A class should strive to have a single responsibility. Guideline - only override pure virtual functions. – Dragan May 20 '20 at 22:21
  • 1
    I would argue that for "chaining" the protected virtual method is a bad choice. I would prefer the base class to have a single public method which would call a bunch of callbacks and another protected method allowing derived classes to register callbacks. – Mojomoko Jan 30 '21 at 11:08
  • @Mojomoko You've just described a vtable. – Spencer Jan 30 '21 at 11:45
  • With a protected method every derived class would need to call the inherited version explicitly. (-1) With the callback you could enforce a certain sequence (call f before the callbacks g afterwards). (+1) – Mojomoko Jan 30 '21 at 14:40
12

I first came across this concept while reading Scott Meyers' 'Effective C++', Item 35: Consider alternatives to virtual functions. I wanted to reference Scott Mayers for others that may be interested.

It's part of the Template Method Pattern via the Non-Virtual Interface idiom: the public facing methods aren't virtual; rather, they wrap the virtual method calls which are private. The base class can then run logic before and after the private virtual function call:

public:
  void NonVirtualCalc(...)
  {
    // Setup
    PrivateVirtualCalcCall(...);
    // Clean up
  }

I think that this is a very interesting design pattern and I'm sure you can see how the added control is useful.

  • Why make the virtual function private? The best reason is that we've already provided a public facing method.
  • Why not simply make it protected so that I can use the method for other interesting things? I suppose it will always depend on your design and how you believe the base class fits in. I would argue that the derived class maker should focus on implementing the required logic; everything else is already taken care of. Also, there's the matter of encapsulation.

From a C++ perspective, it's completely legitimate to override a private virtual method even though you won't be able to call it from your class. This supports the design described above.

Pooven
  • 1,744
  • 1
  • 25
  • 44
5

I use them to allow derived classes to "fill in the blanks" for a base class without exposing such a hole to end users. For example, I have highly stateful objects deriving from a common base, which can only implement 2/3 of the overall state machine (the derived classes provide the remaining 1/3 depending on a template argument, and the base cannot be a template for other reasons).

I NEED to have the common base class in order to make many of the public APIs work correctly (I'm using variadic templates), but I cannot let that object out into the wild. Worse, if I leave the craters in the state machine- in the form of pure virtual functions- anywhere but in "Private", I allow a clever or clueless user deriving from one of its child classes to override methods that users should never touch. So, I put the state machine 'brains' in PRIVATE virtual functions. Then the immediate children of the base class fill in the blanks on their NON-virtual overrides, and users can safely use the resulting objects or create their own further derived classes without worrying about messing up the state machine.

As for the argument that you shouldn't HAVE public virtual methods, I say BS. Users can improperly override private virtuals just as easily as public ones- they're defining new classes after all. If the public shouldn't modify a given API, don't make it virtual AT ALL in publicly accessible objects.

Zack Yezek
  • 1,408
  • 20
  • 7
0

another reason can be a common logic for all inherit classes:

class Base
{
public:
    void interfaceMethod() {
        /** common logic **/
        factoryMethod();
    }
private:
    virtual void factoryMethod() = 0;
};

class concrete1 : Base 
{
private:
    void factoryMethod() override {
        /** specific logic **/
    }
};
    
class concrete2 : Base 
{
private:
    void factoryMethod() override {
        /** specific logic **/
    }
};

Than, for

 Base* obj = new concrete1();

or

 Base* obj = new concrete2();

Than obj.interfaceMethod() will execute common logic and specific logic for each concrete object.

Guy Sadoun
  • 427
  • 6
  • 17