Почему виртуальная функция должна быть закрытой? - PullRequest
26 голосов
/ 16 июля 2009

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

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

Имеет ли это какое-либо назначение?

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

Ответы [ 8 ]

22 голосов
/ 16 июля 2009

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

10 голосов
/ 16 июля 2009

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

Я думаю, вы можете быть смущены, потому что это делается для создания "интерфейсов" в 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);
};

9 голосов
/ 16 июля 2009

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

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

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

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

5 голосов
/ 16 июля 2009

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

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

5 голосов
/ 16 июля 2009

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

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

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

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

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

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


Тем временем C ++ FAQ Lite был обновлен:

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

2 голосов
/ 16 июля 2009

Это чисто виртуальная функция. Любая конечная реализация, полученная из «Foo», ДОЛЖНА реализовывать функцию «Bar».

1 голос
/ 16 июля 2009

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

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

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

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

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

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

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

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

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

НТН

ура

0 голосов
/ 16 июля 2009

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

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

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

...