Почему виртуальная функция должна быть частной?


Я только что заметил это в некотором коде:

class Foo {
[...]
private:
    virtual void Bar() = 0;
[...]
}

Это имеет какой-либо цели?

(я пытаюсь перенести некоторый код из VS в G++, и это привлекло мое внимание)

8 26

8 ответов:

СмотритеЭту статью Херба Саттера о том, почему вы хотите сделать такую вещь.

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

Я думаю, что вы можете быть смущены b/c это делается для создания" интерфейсов " в C++, и много раз люди думают о них как о публичных. В некоторых случаях может потребоваться определить закрытый интерфейс, в котором открытый метод использует эти частные методы, чтобы обеспечить порядок их вызова. (Я полагаю, что это называется Шаблонный Метод)

Для относительно плохого примера :)

class RecordFile
{
    public:
       RecordFile(const std::string &filename);

       void process(const Record &rec)
       {
           // Call the derived class function to filter out
           // records the derived instance of this class does
           // not care about
           if (filterRecord(rec))    
           {
               writeRecordToFile(rec);           
           }
       };

    private:
       // Returns true if the record is of importance
       // and should be kept
       virtual bool filterRecord(const Record &rec) = 0;

       void writeRecordToFile(const Record &rec);
};

ISO C++ 2003 явно разрешает это:

§10.3 ничего не говорит о спецификаторе доступа и содержит даже сноску во втором предложении, в которой говорится в контексте переопределений виртуальных функций:

[...] Контроль доступа (пункт 11) является не учитывается при определении основной.

Кодекс полностью легален.

Обычный "академический" ответ: спецификаторы доступа и виртуальность ортогональны - одно не влияет на другое.

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

Я приведу краткое объяснение из Великого C++ FAQ Lite , которое хорошо подводит итог:

[23.4] когда кто-то должен использовать private виртуалы?

Почти никогда.

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

Новые программисты на C++ путаются частные виртуалы потому что они думают, что частное виртуальное не может быть переопределено. В конце концов, производный класс не может доступ к членам, которые являются частными в его базовый класс, так как же, спрашивают они, это может быть переопределение частного виртуального из его базовый класс? Этому есть свои объяснения. все вышесказанное, но это академично. То реальная проблема в том, что почти все путается в первый раз, когда они бегут в частные виртуалы, и путаница это плохо.

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


C++ FAQ Lite был обновлен в то же время:

Кстати, большинство начинающих программистов на C++ смущает то, что частные виртуалы могут быть переопределены, не говоря уже о том, что они вообще допустимы. Нас всех учили, что частные члены в базовом классе недоступны в классах, производных от него, и это правильно. Однако эта недоступность производного класса не имеет ничего общего с механизмом виртуального вызова, который является производным классом. С это может смутить новичков, FAQ по C++ ранее рекомендовал использовать защищенные виртуалы, а не частные виртуалы. Однако частный виртуальный подход в настоящее время достаточно распространен, так что путаница новичков вызывает меньше беспокойства.

Это чисто виртуальная функция. Окончательные сроки внедрения dervied от "Фу" необходимо реализовать функцию "бар".

Это делает функцию чисто виртуальной в отличие от виртуальной.

Реализация по умолчанию не предусмотрена, и предполагается, что реализация функции должна быть определена наследующим классом. Однако это можно переопределить.

Иногда можно увидеть полные классы, где все функции-члены задаются как чисто виртуальные таким образом.

Это абстрактные базовые классы, иногда называемые интерфейсными классами, и разработчик ABC говорит вам: "я теперь есть идея, как эта функциональность будет реализована для всех специализаций этого базового класса. Но, вы должны иметь все это определено для вашей специализации, чтобы работать, и вы знаете, как ваш объект должен вести себя".

Edit: Упс, только что заметил тот факт, что член чисто виртуальной функции является частным. (Спасибо Майклу) это немного меняет дело.

Когда этот базовый класс наследуется с помощью частного наследования, это меняет дело. В основном то, что дизайнер из базовый класс делает то, что говорит, Когда ваш производный класс вызывает непубличную функцию в базовом классе. часть поведения была делегирована вашей специализации функции в производном классе. Не-частный член делает "что-то", и часть этого" что-то " является вызовом, через чисто виртуальную функцию базового класса, к вашей реализации.

Таким образом, некоторая публичная функция в Foo вызывает функцию Bar внутри Foo, и она полагается на то, что вы предоставите специализированная реализация функции бара для вашего конкретного случая.

Скотт Мейерс называет это "реализованным в терминах".

Кстати, просто посмеиваюсь над количеством ответов, которые были быстро удалены людьми, которые также не видели "мелкий шрифт" в вопросе! (- :

HTH

Ура,

Единственная цель, которой он, по-видимому, служит, - обеспечить общий интерфейс.

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

Тем не менее, такого рода вещи обычно должны служить интерфейсом, но я не делаю этого таким образом.