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*/}
};
+1 dla "Wirtualnie przeładowanych wirtualnie niechronionych połączeń z wirtualnymi przeciążeniami" xD – GabLeRoux
Dlaczego funkcja wirtualna musi być prywatna? Czy to może być publiczne? – Rich
Zastanawiam się, czy wskazówki wydane przez Herb Sutter w jego artykule "Wirtualność" nadal są aktualne? – nurabha