根据NVI惯用法,为什么虚拟功能不能公开?

时间:2015-09-15 17:41:41

标签: c++ idioms non-virtual-interface

C++ private and protected virtual methodIs there any valid reason for not using public virtual methods?正在讨论非虚拟接口( NVI )和非公共虚拟功能及其共生关系。 Scott Meyers也在Effective C ++中说过

  

有时虚拟功能甚至必须是公开的,但是NVI惯用语才能真正应用。

我未能看到为什么NVI 要求特定于实现的虚拟功能是非公开的?从Herb Sutter的文章Virtuality开始,它是一个很好的做法,例如,将公共(客户端)接口与实现细节(非公共接口)分开是很好的。我想知道的是,如果有任何语言功能我错过了语义上会阻止NVI被应用,如果这些虚拟功能被宣布为公共?

例如:

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

如果我将SetStateBoolSetStateInt放在类定义的公共部分会有什么影响?

2 个答案:

答案 0 :(得分:2)

TLDR :你可以,但你不应该。

假设您要确保每次调用公共接口都已正确记录(例如金融服务法律要求)

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

由于公共接口是非虚拟接口,因此所有派生类也会自动进行日志记录。相反,如果您将公共SetStateBoolSetStateInt,则无法对所有派生类强制执行日志记录。

因此,使用NVI习惯用法的建议不是语法要求,但它是一个在所有派生上强制执行基类语义(日志记录或缓存)的工具类。

答案 1 :(得分:0)

不,语言中没有任何内容阻止您创建实现函数public。原则上,你可以做类似的事情:

class Base {
public:

   virtual ~Base(){}

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

};

实施是公开的。梅耶斯说,有时你必须这样做,他可能会说开发人员有时会受限于在设计不佳的环境中发展。

例如,你可以违背RAII惯用法并做类似的事情:

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

析构函数实际上不会释放内存(是的,我之前必须处理这种类型的场景)。语言不禁止它,但通常是一个坏主意。换句话说,仅仅因为它是合法的,并不意味着它是道德的(信用Marshall Cline)......而这就是成语的概念。