6

C++ private and protected virtual method and Is there any valid reason for not using public virtual methods? are talking about Non-Virtual Interface (NVI) and non-public virtual function and their symbiosis. Scott Meyers also says in Effective C++ that

Sometimes a virtual function even has to be public, but then the NVI idiom can't really be applied.

What I failed to see is why NVI requires that the implementation specific virtual function(s) be non-public? From Herb Sutter's article Virtuality, it is saying that it is a good practice to follow, e.g., it is good to separate public (client) interface from the implementation details (the non-public interface). What I want to know is if there is any language feature I missed that semantically prevents NVI being applied if such virtual functions are declared public?

For example:

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;    
};

What are the effects if I put SetStateBool and SetStateInt in public section of the class definition?

Community
  • 1
  • 1
Rich
  • 1,669
  • 2
  • 19
  • 32
  • 2
    It prevents clients from depending on them, that's all – imreal Sep 15 '15 at 17:45
  • 5
    If it's public, it's part of the interface, so the interface isn't non-virtual after all... – T.C. Sep 15 '15 at 17:47
  • @T.C. Oh nice! It goes against the NV part of the name... – Rich Sep 15 '15 at 17:51
  • Are you asking fpr the reason of this idiom, or why NVI implies that virtual functions are non virtual? – MikeMB Sep 15 '15 at 17:54
  • @imreal So it is just an *idiom* that should be followed. Nothing in the language would change the behavior of NVI if virtual functions are declared public, except it pollutes the public interface? – Rich Sep 15 '15 at 17:54
  • @Rich, right, it would work just the same. – imreal Sep 15 '15 at 17:56
  • @MikeMB what is fpr? I understand the reason for this idiom; I just don't see why NVI **requires** non-public virtual. I can indeed see why it is preferable to have these implementation specific virtuals as non-public. But that is different than **requiring**. – Rich Sep 15 '15 at 17:58
  • 1
    Should have been for. I don't understand your confusion. If you have public virtual functions, then you have no longer an Non Virtual Interface, but nothing in the language requires that you follow the NVI Isom in the first place. As all idioms,it is just an advice. – MikeMB Sep 15 '15 at 18:14

2 Answers2

8

TLDR: You can, but you shouldn't.

Suppose you want to make sure that every call to the public interface is properly logged (financial services legal requirement e.g.)

class Engine
{
public:
    void SetState( int var, bool val );
    {  
        logToFile(); 
        SetStateBool( int var, bool val ); 
    }

    void SetState( int var, int val );
    {   
        logToFile();
        SetStateInt( int var, int val ); 
    }
private:
    virtual void SetStateBool(int var, bool val ) = 0;    
    virtual void SetStateInt(int var, int val ) = 0;
    void logToFile();    
};

Because the public interface is non-virtual, all derived classes automatically have logging as well. If instead you would have made SetStateBool and SetStateInt public, you could not have enforced logging for all derived classes.

So the recommendation to use the NVI idiom is not a syntactic requirement, but it's a tool to enforce the base class semantics (logging, or caching) on all derived classes.

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
0

No, there is nothing in the language that prevents you from making an implementation function public. You could, in principle, do something like:

class Base {
public:

   virtual ~Base(){}

   void work() { do_work(); }
   virtual void do_work() = 0;

};

where the implementation is public. By Meyers saying that sometimes you have to do that, he's probably stating that the developer can be constrained at times to develop within a poorly designed context.

For example, you could go against the RAII idiom and do something like this:

std::unique_ptr<MyClass,DoNothingDeleter> ptr ( new MyClass(...) ); 

where the destructor actually will not free the memory (and yes, I've had to deal with this type of scenario before). The language does not forbid it, but it is generally a bad idea. In other words, just because it's legal, does not mean it is moral (credit Marshall Cline)...and that's the concept of the idiom.

KyleKnoepfel
  • 1,426
  • 8
  • 24
  • The `std::unique_ptr ptr ( new MyClass(...) ); ` is obviously malicious or intentionally obfuscated code. Would you mind expanding on your experience with this? – Captain Giraffe Sep 15 '15 at 18:25
  • It was neither malicious, nor intentionally obfuscated...rather it was a means of handling an `std::ostream` object which could be user-specified as `std::cout` (don't you dare try to own it), or `std::fstream`, which is perfectly ownable. I did end up getting rid of the `DoNothingDeleter` ugliness by having two derived classes--one that owns and one that doesn't, and the interface class doesn't care about the ownership semantics. – KyleKnoepfel Sep 15 '15 at 18:44