6

C++ 私有和受保护的虚拟方法以及不使用公共虚拟方法是否有任何正当理由?正在谈论非虚拟接口(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放在类定义的公共部分会有什么影响?

4

2 回答 2

8

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

因为公共接口是非虚拟的,所以所有派生类也自动具有日志记录。相反,如果您将 makeSetStateBoolSetStateIntpublic,您将无法对所有派生类强制执行日志记录。

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

于 2015-09-15T18:01:03.380 回答
0

不,语言中没有任何东西可以阻止您制作实现功能public。原则上,您可以执行以下操作:

class Base {
public:

   virtual ~Base(){}

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

};

实施是公开的。Meyers 说有时您必须这样做,他可能是在说开发人员有时会受到限制,无法在设计不佳的环境中进行开发。

例如,您可以违背 RAII 习语并执行以下操作:

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

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

于 2015-09-15T17:59:44.297 回答