NVI惯用语下,虚函数为什么不能是public?
Under the NVI idiom, why can't the virtual function be public?
C++ private and protected virtual method and Is there any valid reason for not using public virtual methods? 正在谈论非虚拟接口 (NVI) 和非 public 虚拟功能及其共生。 Scott Meyers 在 Effective C++ 中也说
Sometimes a virtual function even has to be public, but then the NVI idiom can't really be applied.
我没看到的是为什么 NVI 要求 特定于实现的虚函数是非 public?从 Herb Sutter 的文章 Virtuality 中可以看出,这是一个值得遵循的好习惯,例如,将 public(客户端)接口与实现细节(非 public 界面)。我想知道的是,如果声明了此类虚函数,是否有任何我错过的语言功能在语义上会阻止应用 NVI public?
例如:
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;
};
如果我将 SetStateBool
和 SetStateInt
放在 class 定义的 public 部分,会有什么影响?
不,语言中没有任何内容可以阻止您制作实现功能public
。原则上,您可以这样做:
class Base {
public:
virtual ~Base(){}
void work() { do_work(); }
virtual void do_work() = 0;
};
实现是 public。 Meyers 说有时您必须这样做,他可能是在说开发人员有时会被迫在设计不佳的环境中进行开发。
例如,您可以反对 RAII 习语并执行如下操作:
std::unique_ptr<MyClass,DoNothingDeleter> ptr ( new MyClass(...) );
析构函数实际上不会释放内存(是的,我以前不得不处理过这种情况)。语言并没有禁止它,但这通常是一个坏主意。换句话说,仅仅因为它是合法的,并不意味着它是道德的(来源 Marshall Cline)……这就是成语的概念。
TLDR:可以,但不应该。
假设您想确保对 public 接口的每次调用都被正确记录(金融服务法律要求,例如)
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();
};
因为 public 接口是非虚拟的,所以所有派生的 classes 也自动具有日志记录。相反,如果您会创建 SetStateBool
和 SetStateInt
public,您将无法对所有派生的 classes 执行日志记录。
因此,使用 NVI 习语的建议不是 句法 要求,而是强制执行基础 class 语义(记录或缓存)在所有派生的 classes.
C++ private and protected virtual method and Is there any valid reason for not using public virtual methods? 正在谈论非虚拟接口 (NVI) 和非 public 虚拟功能及其共生。 Scott Meyers 在 Effective C++ 中也说
Sometimes a virtual function even has to be public, but then the NVI idiom can't really be applied.
我没看到的是为什么 NVI 要求 特定于实现的虚函数是非 public?从 Herb Sutter 的文章 Virtuality 中可以看出,这是一个值得遵循的好习惯,例如,将 public(客户端)接口与实现细节(非 public 界面)。我想知道的是,如果声明了此类虚函数,是否有任何我错过的语言功能在语义上会阻止应用 NVI public?
例如:
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;
};
如果我将 SetStateBool
和 SetStateInt
放在 class 定义的 public 部分,会有什么影响?
不,语言中没有任何内容可以阻止您制作实现功能public
。原则上,您可以这样做:
class Base {
public:
virtual ~Base(){}
void work() { do_work(); }
virtual void do_work() = 0;
};
实现是 public。 Meyers 说有时您必须这样做,他可能是在说开发人员有时会被迫在设计不佳的环境中进行开发。
例如,您可以反对 RAII 习语并执行如下操作:
std::unique_ptr<MyClass,DoNothingDeleter> ptr ( new MyClass(...) );
析构函数实际上不会释放内存(是的,我以前不得不处理过这种情况)。语言并没有禁止它,但这通常是一个坏主意。换句话说,仅仅因为它是合法的,并不意味着它是道德的(来源 Marshall Cline)……这就是成语的概念。
TLDR:可以,但不应该。
假设您想确保对 public 接口的每次调用都被正确记录(金融服务法律要求,例如)
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();
};
因为 public 接口是非虚拟的,所以所有派生的 classes 也自动具有日志记录。相反,如果您会创建 SetStateBool
和 SetStateInt
public,您将无法对所有派生的 classes 执行日志记录。
因此,使用 NVI 习语的建议不是 句法 要求,而是强制执行基础 class 语义(记录或缓存)在所有派生的 classes.