2010-10-19 11 views
108

natknąłem się na następujący kod w pliku nagłówka:Jaki jest cel prywatnej czystej wirtualnej funkcji?

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

Dla mnie oznacza to, że albo klasa Engine lub klasa pochodzi od niego, musi zapewnić realizację dla tych czystych funkcji wirtualnych. Ale nie sądziłem, że klasy pochodne mogą mieć dostęp do tych prywatnych funkcji w celu ich ponownego wykorzystania - dlaczego więc sprawić, by były wirtualne?

Odpowiedz

170

Pytanie w temacie sugeruje dość powszechne zamieszanie. Zamęt jest na tyle powszechny, że od dłuższego czasu opowiadał się za używanie prywatnych wirtualnych urządzeń, ponieważ zamieszanie wydawało się złe.

Aby pozbyć się zamieszania najpierw: Tak, prywatne funkcje wirtualne mogą być nadpisane w klasach pochodnych. Metody klas pochodnych nie mogą wywoływać funkcji wirtualnych z klasy bazowej, ale mogą zapewnić dla nich własną implementację. Według Herba Sutter'a posiadanie publicznego nie-wirtualnego interfejsu w klasie bazowej i prywatna implementacja, która może być dostosowana w klasach pochodnych, pozwala na lepsze "oddzielenie specyfikacji interfejsu od specyfikacji dostosowawczego zachowania implementacji". Możesz przeczytać więcej na ten temat w jego artykule "Virtuality".

W kodzie, który przedstawiłeś, jest jeszcze jedna interesująca rzecz, która moim zdaniem zasługuje na więcej uwagi. Interfejs publiczny składa się z zestawu przeciążonych funkcji innych niż wirtualne i funkcje te wywołują niepubliczne, nie przeciążone funkcje wirtualne. Jak zwykle w świecie C++ jest to idiom, ma nazwę i oczywiście jest użyteczny. Nazwa jest (niespodzianka!)

"publiczne przeciąŜone dla virtuale połączeń chronionych Non-przeciążony virtuals"

Pomaga properly manage the hiding rule. Możesz przeczytać więcej na ten temat here, ale postaram się to krótko wyjaśnić.

Wyobraź sobie, że wirtualne funkcje klasy Engine są również jej interfejsem i jest to zbiór przeciążonych funkcji, które nie są czysto wirtualne. Gdyby były czysto wirtualne, nadal można spotkać ten sam problem, jak opisano poniżej, ale niższy w hierarchii klasowej.

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

Teraz załóżmy, że chcemy utworzyć klasę pochodną i trzeba dostarczyć nową implementację tylko dla metody, które ma dwa ints jako argumenty.

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

Jeśli zapomniałeś umieścić deklarację używania w klasie pochodnej (lub korektę drugiego przeciążenie), można mieć kłopoty w poniższym scenariuszu.

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

Jeśli nie przeszkodziło ukrywanie członków Engine, oświadczenie:

myV8->SetState(5, true); 

nazwałbym void SetState(int var, int val) z klasy pochodnej, konwersja true do int.

Jeśli interfejs nie jest wirtualny i wdrożenie wirtualnej jest niepubliczne, jak w exmaple, autor klasy pochodnej ma jeden problem mniej myślenia i można po prostu napisać

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

+1 dla "Wirtualnie przeładowanych wirtualnie niechronionych połączeń z wirtualnymi przeciążeniami" xD – GabLeRoux

+0

Dlaczego funkcja wirtualna musi być prywatna? Czy to może być publiczne? – Rich

+0

Zastanawiam się, czy wskazówki wydane przez Herb Sutter w jego artykule "Wirtualność" nadal są aktualne? – nurabha

14

Po pierwsze pozwoliłoby to klasie pochodnej zaimplementować funkcję, którą może wywołać klasa podstawowa (zawierająca czystą deklarację funkcji wirtualnej).

+3

że ** tylko ** klasa bazowa może zadzwonić! –

3

Prywatna metoda wirtualna służy do ograniczania liczby klas pochodnych, które mogą przesłonić daną funkcję. Wyprowadzone klasy, które muszą przesłonić prywatną metodę wirtualną, muszą być przyjacielem klasy bazowej.

Krótkie wyjaśnienie można znaleźć pod adresem DevX.com.


EDIT prywatny sposób wirtualnego jest skutecznie stosowane w Template Method Pattern. Klasy pochodne mogą przesłonić prywatną metodę wirtualną, ale pochodne klasy nie mogą nazwać jej prywatnej wirtualnej metody klasy podstawowej (w twoim przykładzie: SetStateBool i SetStateInt). Tylko klasa podstawowa może skutecznie wywoływać swoją prywatną metodę wirtualną (Tylko jeśli klasy pochodne muszą wywoływać podstawową implementację funkcji wirtualnej, uczyń funkcję wirtualną chronioną).

Interesujący artykuł można znaleźć na temat Virtuality.

+0

@Gentleman ... hmmm przewiń w dół do komentarza Colina D. Bennetta. Wydaje się, że "Prywatna wirtualna funkcja może być nadpisana przez klasy pochodne, ale może być wywołana tylko z poziomu klasy bazowej.". @Michael Goldshteyn także tak myśli. – BeeBand

+0

Sądzę, że zapomniałeś zasady, że klasa prywatna nie może być postrzegana przez swoją klasę pochodną. To są reguły OOP i dotyczy wszystkich języków, które są OOP. Aby klasa pochodna implementowała swoją prywatną metodę wirtualną klasy podstawowej, musi to być 'przyjaciel' klasy bazowej. Qt zastosowało to samo podejście, gdy wdrożyło swój model dokumentu XML DOM. –

+0

@Gentleman: Nie, nie zapomniałem. Popełniłem błąd w moim komentarzu. Zamiast "dostępu do metod klasy bazowej" powinienem był napisać "może zastąpić metody klasy bazowej". Klasa pochodna może z pewnością zastąpić metodę prywatnej wirtualnej klasy bazowej, nawet jeśli nie może uzyskać dostępu do tej metody klasy bazowej. Wskazany artykuł DevX.com był niepoprawny (dziedziczenie publiczne). Wypróbuj kod w mojej odpowiedzi. Pomimo metody prywatnej wirtualnej klasy bazowej klasa pochodna jest w stanie ją zastąpić. Nie mylmy możliwości nadpisania metody prywatnej wirtualnej klasy bazowej z możliwością jej wywołania. – Void

36

Prywatna pure virtual funkcja jest podstawa dla wirtualnego interfejsu idiomu (OK, to nie zawsze jest absolutnie czystawirtualny, ale wciąż wirtualny tam). Oczywiście, jest to również używane do innych rzeczy, ale uważam to za najbardziej przydatne (: W dwóch słowach: w funkcji publicznej można na początku umieścić pewne typowe rzeczy (takie jak logowanie, statystyki itp.) I w końcu funkcji, a następnie „w środku”, aby wywołać tę prywatną funkcję wirtualną, która będzie inna dla konkretnego pochodzi klasie coś takiego:.

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() 
{ 
    // .. 
} 

Czysta wirtualny - tylko zobowiązuje pochodzi klasy, by go wdrożyć

EDIT. Więcej na ten temat: Wikipedia::NVI-idiom

2

EDYCJA: Wyjaśnione stwierdzenia dotyczące możliwości przesłonięcia i możliwości dostępu/wywołania.

Będzie w stanie zastąpić te prywatne funkcje. Na przykład następujący przykładowy przykład działa (EDYCJA: uczynił metodę klasy pochodnej prywatną i upuść wywołanie metody klasy pochodnej w main(), aby lepiej przedstawić zamiar używanego wzoru projektu.):

#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 metody klasy bazowej jak te w kodzie są zazwyczaj wykorzystywane do realizacji Template Method design pattern. Ten wzorzec projektowy pozwala zmienić zachowanie algorytmu w klasie bazowej bez zmiany kodu w klasie bazowej. Powyższy kod, w którym metody klasy bazowej są wywoływane przez wskaźnik klasy bazowej, jest prostym przykładem wzorca metody szablonów.

+0

Rozumiem, ale jeśli klasy pochodne i tak mają jakiś dostęp, czemu zawracają sobie głowę, czyniąc je prywatnymi? – BeeBand

+0

@BeeBand: Użytkownik będzie miał dostęp do nadrzędnych metod klasy wirtualnej, ale nie będzie miał dostępu do klas bazowych. W tym przypadku autor klasy pochodnej może również zachować metodę wirtualną również nadpisującą jako prywatną. W rzeczywistości dokonam zmiany w powyższym przykładowym kodzie, aby to podkreślić. Tak czy inaczej, zawsze mogli dziedziczyć publicznie i nadpisywać prywatne metody wirtualnej klasy bazowej, ale nadal mieliby dostęp tylko do swoich własnych metod klas pochodnych. Zauważ, że dokonuję rozróżnienia między nadpisaniem a dostępem/wywołaniem. – Void

+0

Interesujące. Dlaczego spadki? Jeśli zamierzasz przegłosować, wyjaśnij dlaczego w komentarzu, aby plakat i inne osoby mogły skorzystać z Twojej opinii. – Void

Powiązane problemy