Private virtual method in C++
Asked Answered
W

6

162

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&);
};
Wentzel answered 31/1, 2010 at 5:39 Comment(3)
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. :-)Sandiesandifer
@Sandiesandifer But you didn't even answer your own question......Antonia
@Sandiesandifer I thought you mean backwards in a different way: What is the advantage of making a virtual method not private?Incisure
M
158

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

Mastersinger answered 31/1, 2010 at 5:42 Comment(7)
As you might guess from my answer, I think Sutter's guideline #3 rather shoves guideline #2 out the window.Antonia
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?Ligula
@flarn2006 Guideline #3: Only if derived classes need to invoke the base implementation of a virtual function, make the virtual function protected.Boomerang
But how do you call down to the base function?Afflux
@Afflux NameOfBaseClass::NameOfMethod(whatever, arguments, you, need);Nevlin
@Mar But you the method is private.Afflux
@Afflux 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.Nevlin
P
81

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

Peppermint answered 31/1, 2010 at 5:46 Comment(2)
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."Rodge
Confusion of experts, however, remains a concern. None of the four C++ professionals sitting next to me were aware of private virtuals.Mantling
A
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.

Antonia answered 25/2, 2016 at 16:0 Comment(11)
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.Karankaras
@Karankaras Then you will just have to post an answer showing how a derived class function can call a private base class function.Antonia
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 functionKarankaras
@Karankaras 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.Antonia
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.Karankaras
@Karankaras 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.Antonia
Then let's agree to disagree ;)Karankaras
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.Linotype
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.Frausto
@Frausto You've just described a vtable.Antonia
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)Frausto
C
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.

Coelenteron answered 11/6, 2014 at 19:58 Comment(0)
C
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.

Chaunceychaunt answered 5/2, 2014 at 21:10 Comment(1)
If t may help, C++11 has 'final' keyword to prevent further overrides.Mcvey
H
2

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();

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

Heartily answered 19/3, 2023 at 15:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.