Частный виртуальный метод в С++

В чем преимущество создания частного метода virtual на С++?

Я заметил это в проекте С++ с открытым исходным кодом:

class HTMLDocument : public Document, public CachedResourceClient {
private:
    virtual bool childAllowed(Node*);
    virtual PassRefPtr<Element> createElement(const AtomicString& tagName, ExceptionCode&);
};

Ответ 1

Herb Sutter очень хорошо объяснил это здесь.

Руководство № 2: Предпочтение делать виртуальные функции частными.

Это позволяет производным классам переопределять функцию, чтобы настроить поведение по мере необходимости, без дальнейшего непосредственного отображения виртуальных функций, делая их вызываемыми производными классами (как это было бы возможно, если функции были просто защищены). Дело в том, что существуют виртуальные функции для настройки; если они также не должны быть вызваны непосредственно из кода производных классов, нет необходимости когда-либо делать что-либо, кроме частного

Ответ 2

Если метод является виртуальным, он может быть переопределен производными классами, даже если он является приватным. Когда вызывается виртуальный метод, вызывается переопределенная версия.

(Напротив Херба Саттера, цитируемого Прасуном Сауравом в его ответе, С++ FAQ Lite рекомендует против частных виртуальных машин, главным образом потому, что он часто путает чел.)

Ответ 3

Несмотря на все призывы объявить виртуальный член закрытым, аргумент просто не содержит воды. Часто переопределение производного класса виртуальной функции должно вызывать версию базового класса. Он не может, если объявлен private:

class Base
{
 private:

 int m_data;

 virtual void cleanup() { /*do something*/ }

 protected:
 Base(int idata): m_data (idata) {}

 public:

 int data() const { return m_data; }
 void set_data (int ndata) { m_data = ndata; cleanup(); }
};

class Derived: public Base
{
 private:
 void cleanup() override
 {
  // do other stuff
  Base::cleanup(); // nope, can't do it
 }
 public:
 Derived (int idata): base(idata) {}
};

Вы должны объявить метод базового класса protected и указать, что метод должен быть переопределен, но не вызываться.

class Base
{
 ...
 protected:
 // chained virtual function!
 // call in your derived version but nowhere else.
 // Use set_data instead
 virtual void cleanup() { /* do something */ }
 ...

Таким образом, Herb Sutter guideline # 3... Но лошадь все равно выходит из сарая.

Когда вы объявляете что-то protected, вы неявно доверяете писателю какого-либо производного класса понимать и правильно использовать защищенные внутренние компоненты, как способ объявления friend подразумевает более глубокое доверие для членов private.

Пользователи, которые плохое поведение нарушают это доверие (например, помечены как "невежественные", не удосужившись прочитать вашу документацию), сами виноваты.

Обновление. У меня была некоторая обратная связь, в которой утверждается, что вы можете "цепочки" реализации виртуальных функций таким образом, используя частные виртуальные функции. Если это так, я бы хотел это увидеть.

Компиляторы С++, которые я использую, определенно не позволят реализации производного класса вызывать реализацию частного базового класса.

Если комитет С++ расслабился "private", чтобы разрешить этот конкретный доступ, я бы все был для частных виртуальных функций. В его нынешнем виде нам по-прежнему советуют запирать дверь сарая после того, как лошадь украдена.

Ответ 4

Я впервые встретил эту концепцию, читая "Эффективный С++" Скотта Майерса, пункт 35: Рассмотрите альтернативы виртуальным функциям. Я хотел обратиться к Скотту Майерсу за другими, которые могут быть заинтересованы.

Это часть шаблона метода шаблона через идиому не виртуального интерфейса: методы публичного доступа не являются виртуальными; скорее, они обертывают вызовы виртуального метода, которые являются частными. Базовый класс может запускать логику до и после вызова частной виртуальной функции:

public:
  void NonVirtualCalc(...)
  {
    // Setup
    PrivateVirtualCalcCall(...);
    // Clean up
  }

Я думаю, что это очень интересный шаблон дизайна, и я уверен, что вы можете видеть, как добавленный элемент управления полезен.

  • Зачем создавать виртуальную функцию private? Лучшая причина в том, что мы уже предоставили метод public.
  • Почему бы просто не сделать это protected, чтобы я мог использовать этот метод для других интересных вещей? Я полагаю, что это всегда будет зависеть от вашего дизайна и того, как вы считаете, что базовый класс подходит. Я бы сказал, что создатель производного класса должен сосредоточиться на реализации необходимой логики; все остальное уже позаботится. Кроме того, есть вопрос об инкапсуляции.

С точки зрения С++ вполне законно переопределять частный виртуальный метод, даже если вы не сможете назвать его из своего класса. Это поддерживает описанную выше конструкцию.

Ответ 5

Я использую их, чтобы позволить производным классам "заполнять пробелы" для базового класса, не подвергая такое отверстие конечным пользователям. Например, у меня есть объекты с высокой степенью объектности, основанные на общей базе, которые могут реализовывать только 2/3 общего состояния машины (производные классы предоставляют оставшуюся 1/3 в зависимости от аргумента шаблона, а база не может быть шаблоном для другие причины).

Мне НЕОБХОДИМО иметь общий базовый класс, чтобы заставить многие из публичных API работать правильно (я использую вариационные шаблоны), но я не могу позволить этому объекту выйти в дикую природу. Хуже того, если я оставлю кратеры в государственной машине - в виде чистых виртуальных функций - где угодно, но в "Private", я разрешаю умному или невежественному пользователю получать от одного из своих дочерних классов переопределение методов, которые пользователи никогда не должны касаться. Итак, я поставил "мозги" автомата в виртуальные функции ЧАСТНОГО. Затем непосредственные дети базового класса заполняют пробелы в своих N-виртуальных переопределениях, и пользователи могут безопасно использовать результирующие объекты или создавать свои собственные дополнительные производные классы, не беспокоясь о запуске конечного автомата.

Что касается аргумента, что вы не должны иметь общедоступные виртуальные методы, я говорю BS. Пользователи могут ненадлежащим образом переопределять частные виртуальные машины так же легко, как и обычные, ведь они все-таки определяют новые классы. Если публикация не должна изменять данный API, не делайте ее виртуальной AT ALL в общедоступных объектах.