Частный виртуальный метод в C ++ - PullRequest
112 голосов
/ 31 января 2010

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

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

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

Ответы [ 5 ]

110 голосов
/ 31 января 2010

Херб Саттер очень хорошо объяснил это здесь .

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

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

61 голосов
/ 31 января 2010

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

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

9 голосов
/ 25 февраля 2016

Несмотря на все призывы объявить виртуального члена приватным, аргумент просто не выдерживает критики. Часто переопределение производного класса виртуальной функции должно вызывать версию базового класса. Не может, если объявлено 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 */ }
 ...

Таким образом, рекомендация Херба Саттера №3 ... Но лошадь все равно вышла из сарая.

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

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

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

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

Если бы комитет C ++ ослабил «приватность», чтобы разрешить этот конкретный доступ, я бы все для приватных виртуальных функций. Пока он стоит, нам все еще советуют запирать дверь сарая после того, как лошадь украдена.

8 голосов
/ 11 июня 2014

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

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

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

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

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

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

3 голосов
/ 06 февраля 2014

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

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

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

...