В соответствии с идиомой NVI, почему виртуальная функция не может быть общедоступной?
C++ частный и защищенный виртуальный метод и есть ли веская причина не использовать общедоступные виртуальные методы? речь идет о не виртуальном интерфейсе (NVI) и непубличной виртуальной функции и их симбиозе. Скотт Мейерс также говорит в Effective C++, что
Иногда виртуальная функция даже должна быть публичной, но тогда идиома NVI не может быть применена.
Что мне не удалось увидеть, так это почему NVI требует, чтобы конкретные виртуальные функции реализации были непубличными? Из статьи Херба Саттера " Виртуальность" говорится, что это хорошая практика, которой следует следовать, например, хорошо отделить публичный (клиентский) интерфейс от деталей реализации (непубличный интерфейс). Я хочу знать, есть ли какая-то языковая функция, которую я пропустил, которая семантически препятствует применению 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;
};
Каковы эффекты, если я положу SetStateBool
а также SetStateInt
в публичном разделе определения класса?
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();
};
Поскольку открытый интерфейс не является виртуальным, все производные классы также автоматически регистрируются. Если бы вместо этого вы сделали SetStateBool
а также SetStateInt
public, вы не могли бы принудительное ведение журнала для всех производных классов.
Таким образом, рекомендация использовать идиому NVI не является синтаксическим требованием, но это инструмент для обеспечения семантики базового класса (ведение журнала или кэширование) для всех производных классов.
Нет, в языке нет ничего, что мешало бы вам сделать функцию реализации public
, В принципе, вы можете сделать что-то вроде:
class Base {
public:
virtual ~Base(){}
void work() { do_work(); }
virtual void do_work() = 0;
};
где реализация является публичной. По словам Майерса, что иногда вам приходится это делать, он, вероятно, заявляет, что разработчик может быть вынужден порой развиваться в плохо спроектированном контексте.
Например, вы можете пойти против идиомы RAII и сделать что-то вроде этого:
std::unique_ptr<MyClass,DoNothingDeleter> ptr ( new MyClass(...) );
где деструктор фактически не освободит память (и да, мне приходилось иметь дело с этим типом сценария раньше). Язык не запрещает этого, но в целом это плохая идея. Другими словами, только то, что это законно, не означает, что это морально (кредит Маршалла Клайна)... и это понятие идиомы.