Pytanie w temacie sugeruje dość powszechne zamieszanie. Zamieszanie jest na tyle powszechne, że C ++ FAQ od dawna opowiadał się przeciwko używaniu prywatnych wirtualiów, ponieważ zamieszanie wydawało się czymś złym.
Aby najpierw pozbyć się zamieszania: tak, prywatne funkcje wirtualne można przesłonić 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 Suttera posiadanie publicznego niewirtualnego interfejsu w klasie bazowej oraz prywatnej implementacji, którą można dostosowywać w klasach pochodnych, pozwala na lepsze „oddzielenie specyfikacji interfejsu od specyfikacji dostosowywalnego zachowania implementacji”. Więcej na ten temat można przeczytać w jego artykule „Wirtualność” .
W przedstawionym kodzie jest jednak 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 niewirtualnych, które wywołują niepubliczne, niezciążone funkcje wirtualne. Jak zwykle w świecie C ++ jest to idiom, ma nazwę i oczywiście jest przydatny. Nazywam się (niespodzianka, niespodzianka!)
„Public Overloaded Non-Virtuals Call Protected Non-Overloaded Virtuals”
Pomaga odpowiednio zarządzać regułą ukrywania . Możesz przeczytać więcej na ten temat tutaj , ale postaram się to wkrótce wyjaśnić.
Wyobraź sobie, że funkcje wirtualne Engine
klasy są jednocześnie jej interfejsem i jest to zestaw przeciążonych funkcji, które nie są czysto wirtualne. Gdyby były czysto wirtualne, nadal można by napotkać ten sam problem, jak opisano poniżej, ale niżej w hierarchii klas.
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 chcesz utworzyć klasę pochodną i musisz podać nową implementację tylko dla metody, która przyjmuje dwa argumenty typu int.
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ę using w klasie pochodnej (lub przedefiniować drugie przeciążenie), możesz mieć kłopoty w poniższym scenariuszu.
MyTurbochargedV8* myV8 = new MyTurbochargedV8();
myV8->SetState(5, true);
Jeśli nie zapobiegłeś ukrywaniu Engine
członków, oświadczenie:
myV8->SetState(5, true);
wywoła void SetState( int var, int val )
z klasy pochodnej, konwertując true
na int
.
Jeśli interfejs nie jest wirtualny, a wirtualna implementacja jest niepubliczna, jak w Twoim przykładzie, autor klasy pochodnej ma o jeden problem mniej do przemyślenia i może po prostu napisać
class MyTurbochargedV8 : public Engine
{
private:
void SetStateInt(int var, int val ) {/*new implementation*/}
};
Prywatna czysta funkcja wirtualna jest podstawą idiomu interfejsu niewirtualnego (OK, nie jest to absolutnie zawsze czysto wirtualne, ale nadal wirtualne). 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żesz umieścić kilka typowych rzeczy (takich jak rejestrowanie, statystyki itp.) Na początku i na końcu funkcji, a następnie „w środku”, aby wywołać tę prywatną funkcję wirtualną, która będzie inna dla określonej klasy pochodnej. Coś w rodzaju:
Czysto wirtualne - po prostu zobowiązuje klasy pochodne do jej implementacji.
EDYCJA : Więcej na ten temat: Wikipedia :: NVI-idiom
źródło
Po pierwsze, pozwoliłoby to klasie pochodnej na zaimplementowanie funkcji, którą może wywołać klasa bazowa (zawierająca deklarację czystej funkcji wirtualnej).
źródło
EDYCJA: Wyjaśnione stwierdzenia dotyczące możliwości przesłonięcia i możliwości dostępu / wywołania.
Będzie mógł zastąpić te funkcje prywatne. Na przykład następujący wymyślony przykład działa ( EDIT: uczynił metodę klasy pochodnej prywatną i porzuć wywołanie metody klasy pochodnej,
main()
aby lepiej zademonstrować cel użycia wzorca projektowego ):Private
virtual
metody w klasie bazowej, takie jak te w kodzie, są zwykle używane do implementowania wzorca projektowego metody szablonu . Ten wzorzec projektowy pozwala na zmianę zachowania algorytmu w klasie bazowej bez zmiany kodu w klasie bazowej. Powyższy kod, w którym metody klasy bazowej są wywoływane za pośrednictwem wskaźnika klasy bazowej, jest prostym przykładem wzorca Template Method.źródło
Engine
iDerivedEngine
nie ma nic wspólnego z tym, coDerivedEngine
może lub nie może przesłonić (lub uzyskać dostęp, jeśli o to chodzi).Prywatna metoda wirtualna służy do ograniczania liczby klas pochodnych, które mogą przesłonić daną funkcję. Klasy pochodne, które muszą przesłonić prywatną metodę wirtualną, będą musiały być przyjacielem klasy bazowej.
Krótkie wyjaśnienie można znaleźć w witrynie DevX.com .
EDYCJA Prywatna metoda wirtualna jest efektywnie używana we wzorcu metody szablonowej . Klasy pochodne mogą przesłonić prywatną metodę wirtualną, ale klasy pochodne nie mogą wywołać prywatnej metody wirtualnej klasy bazowej (w naszym przykładzie
SetStateBool
iSetStateInt
). Tylko klasa bazowa może skutecznie wywołać swoją prywatną metodę wirtualną ( tylko jeśli klasy pochodne muszą wywoływać podstawową implementację funkcji wirtualnej, należy zabezpieczyć funkcję wirtualną ).Ciekawy artykuł można znaleźć na temat wirtualności .
źródło
friend
klasą bazową. Qt przyjął to samo podejście, kiedy zaimplementował swój model dokumentu XML DOM.Odpowiedź TL; DR:
Możesz traktować to jako inny poziom hermetyzacji - gdzieś między chronionym a prywatnym : nie możesz go wywołać z klasy podrzędnej, ale możesz go zastąpić.
Jest to przydatne przy implementacji wzorca projektowego Template Method . Możesz używać chronionych , ale prywatnych razem z wirtualnymi, które mogą być uznane za lepszy wybór ze względu na lepszą hermetyzację.
źródło