2010-10-19 4 views
108

я наткнулся на следующий код в файле заголовка:Что такое частная чистая виртуальная функция?

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;  
}; 

Для меня это означает, что либо Engine класс или класс, производный от него, должен обеспечить реализацию этих чисто виртуальных функций. Но я не думал, что производные классы могут иметь доступ к этим частным функциям, чтобы переопределить их - так зачем делать их виртуальными?

ответ

170

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

Итак, чтобы сначала избавиться от путаницы: Да, частные производные функции могут быть переопределены в производных классах. Методы производных классов не могут вызывать виртуальные функции из базового класса, но они могут обеспечить им собственную реализацию. Согласно Herb Sutter, наличие публичного не виртуального интерфейса в базовом классе и частная реализация, которые могут быть настроены в производных классах, позволяет лучше «отделять спецификацию интерфейса от спецификации настраиваемого поведения реализации». Вы можете прочитать об этом в своей статье "Virtuality".

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

"Открытый перегружен Non-Virtuals вызовов Защищенные не перегруженный виртуалов"

Это помогает properly manage the hiding rule. Вы можете узнать больше об этом here, но я попытаюсь объяснить это в ближайшее время.

Представьте, что виртуальные функции класса Engine также являются его интерфейсом, и это набор перегруженных функций, которые не являются чистыми виртуальными. Если бы они были чистыми виртуальными, то все же можно было столкнуться с одной и той же проблемой, как описано ниже, но ниже в иерархии классов.

class Engine 
{ 
public: 
    virtual void SetState(int var, bool val) {/*some implementation*/} 
    virtual void SetState(int var, int val) {/*some implementation*/} 
}; 

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

class MyTurbochargedV8 : public Engine 
{ 
public: 
    // To prevent SetState(int var, bool val) from the base class, 
    // from being hidden by the new implementation of the other overload (below), 
    // you have to put using declaration in the derived class 
    using Engine::SetState; 

    void SetState(int var, int val) {/*new implementation*/} 
}; 

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

MyTurbochargedV8* myV8 = new MyTurbochargedV8(); 
myV8->SetState(5, true); 

Если вы не допустить скрытие Engine членов, утверждение:

myV8->SetState(5, true); 

назвал бы void SetState(int var, int val) из производного класса, преобразования true в int.

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

class MyTurbochargedV8 : public Engine 
{ 
private: 
    void SetStateInt(int var, int val) {/*new implementation*/} 
}; 
+6

+1 для «Public Overloaded Non-Virtuals Call Protected Non-Overloaded Virtuals» xD – GabLeRoux

+0

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

+0

Интересно, сохраняются ли сегодня рекомендации, данные Хербом Саттером в его статье «Виртуальность»? – nurabha

14

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

+3

что ** только ** базовый класс может позвонить! –

3

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

Краткое описание: DevX.com.


РЕДАКТИРОВАТЬ частный виртуальный метод эффективно используется в Template Method Pattern. Производные классы могут переопределять частный виртуальный метод, но производные классы не могут назвать его виртуальным методом базового класса (в вашем примере SetStateBool и SetStateInt). Только базовый класс может эффективно вызвать свой частный виртуальный метод (Только если производные классы должны вызвать базовую реализацию виртуальной функции, сделайте виртуальную функцию защищенной).

Интересную статью можно найти о Virtuality.

+0

@ Джентльмен ... hmmm прокрутите вниз до комментария Колина Д Беннета. Кажется, он считает, что «приватная виртуальная функция может быть переопределена производными классами, но может быть вызвана только из базового класса». Майкл Голдштейн тоже так думает. – BeeBand

+0

Я предполагаю, что вы забыли принцип, что частный класс не может быть замечен его производным классом. Это правила ООП, и это применимо ко всему языку, который является ООП. Чтобы производный класс реализовал частный виртуальный метод своего базового класса, он должен быть «другом» базового класса. Qt применяет тот же подход, когда они реализовали свою модель документа XML DOM. –

+0

@ Джентльмен: Нет, я не забыл. Я сделал опечатку в своем комментарии. Вместо «доступа к методам базового класса» я должен был написать «может переопределять методы базового класса». Производный класс может переопределить метод частного виртуального базового класса, даже если он не может получить доступ к этому методу базового класса. Статья DevX.com, которую вы указали, была неправильной (публичное наследование). Попробуйте код в моем ответе. Несмотря на частный метод виртуального базового класса, производный класс может переопределить его. Давайте не будем путать возможность переопределить метод частного виртуального базового класса с возможностью его вызова. – Void

36

Частная чистая виртуальная функции является базой Невиртуальных интерфейс идиомов (OK, это не абсолютно всегда чисто виртуальные, но все-таки виртуальным там). Конечно, это используется и для других вещей, но я считаю это наиболее полезным (: двумя словами: в публичной функции вы могли бы поместить некоторые общие вещи (например, журнал, статистику и т. Д.) В начале и в конце функции, а затем, «в середине», чтобы вызвать эту частную виртуальную функцию, которая будет отличаться для конкретного класса производного что-то вроде:.

class Base 
{ 
    // .. 
public: 
    void f(); 
private: 
    virtual void DerivedClassSpecific() = 0; 
    // .. 
}; 
void Base::f() 
{ 
    //.. Do some common stuff 
    DerivedClassSpecific(); 
    //.. Some other common stuff 
} 
// .. 

class Derived: public Base 
{ 
    // .. 
private: 
    virtual void DerivedClassSpecific(); 
    //.. 
}; 
void Derived::DerivedClassSpecific() 
{ 
    // .. 
} 

Чистых виртуальная - просто обязует производную классы для его реализации

EDIT:. Подробнее об этом: Wikipedia::NVI-idiom

2

EDIT: Уточненные заявления о возможности переопределения и возможности доступа/вызова.

Он сможет переопределить эти частные функции. Например, работает следующий надуманный пример (EDIT: сделанный метод производного класса private и отмените вызов метода производного класса в main(), чтобы лучше продемонстрировать намерение используемого шаблона проектирования.):

#include <iostream> 

class Engine 
{ 
public: 
    void SetState(int var, bool val) 
    { 
    SetStateBool(var, val); 
    } 

    void SetState(int var, int val) 
    { 
    SetStateInt(var, val); 
    } 

private: 

    virtual void SetStateBool(int var, bool val) = 0; 
    virtual void SetStateInt(int var, int val) = 0; 

}; 

class DerivedEngine : public Engine 
{ 
private: 
    virtual void SetStateBool(int var, bool val) 
    { 
    std::cout << "DerivedEngine::SetStateBool() called" << std::endl; 
    } 

    virtual void SetStateInt(int var, int val) 
    { 
    std::cout << "DerivedEngine::SetStateInt() called" << std::endl; 
    } 
}; 


int main() 
{ 
    DerivedEngine e; 
    Engine * be = &e; 

    be->SetState(4, true); 
    be->SetState(2, 1000); 
} 

Privatevirtual методы в базовом классе, как те, в вашем коде, как правило, используются для реализации Template Method design pattern. Этот шаблон проектирования позволяет изменить поведение алгоритма в базовом классе без изменения кода в базовом классе. Вышеприведенный код, в котором методы базового класса вызывается с помощью указателя базового класса, является простым примером шаблона метода шаблона.

+0

Я вижу, но если производные классы имеют какой-то доступ в любом случае, зачем беспокоиться о том, чтобы сделать их частными? – BeeBand

+0

@BeeBand: у пользователя будет доступ к переопределению виртуального метода открытого производного класса, но он не будет иметь доступа к базовым классам. Создатель производного класса в этом случае может сохранить приватный виртуальный метод. На самом деле я сделаю изменение в примере кода выше, чтобы подчеркнуть это. В любом случае, они всегда могут наследовать публично и переопределять виртуальные методы частного базового класса, но они все равно будут иметь доступ к своим собственным виртуальным методам производного класса. Обратите внимание, что я делаю различие между переопределением и доступом/вызовом. – Void

+0

Интересно. Почему вниз вниз? Если вы собираетесь спускать вниз, пожалуйста, объясните, почему в комментарии, чтобы плакат и другие могли воспользоваться вашим мнением. – Void

Смежные вопросы