2011-01-12 14 views
8

Oto przykład kodu, który mnie denerwuje:Jak uzyskać dostęp do metody chronionej w klasie bazowej z klasy pochodnej?

class Base { 
    protected: 
    virtual void foo() = 0; 
}; 

class Derived : public Base { 
    private: 
    Base *b; /* Initialized by constructor, not shown here 
       Intended to store a pointer on an instance of any derived class of Base */ 

    protected: 
    virtual void foo() { /* Some implementation */ }; 
    virtual void foo2() { 
     this->b->foo(); /* Compilator sets an error: 'virtual void Base::foo() is protected' */ 
    } 
}; 

Jak uzyskać dostęp do chronionego funkcją overrided?

Dzięki za pomoc. : o)

+7

Nie sądzę, że twoja implementacja jest całkiem poprawna. Dlaczego masz instancję Base jako zmienną składową? ten-> b-> foo() będzie próbował wywołać czystą wirtualną metodę. – GWW

+1

Ten program nie powinien się kompilować. Nie można utworzyć instancji klasy abstrakcyjnej ... chyba że 'b' wskazuje na instancję innej klasy pochodnej' Base'. – 341008

+0

Pominięto precyzję: atrybut Derived :: b służy do przechowywania instancji klas pochodnych z bazy –

Odpowiedz

8

Elementy chronione w klasie bazowej są dostępne tylko dla bieżącego obiektu.
W ten sposób możesz zadzwonić pod numer this->foo(), ale nie możesz dzwonić pod numer this->b->foo(). Jest to niezależne od tego, czy Derived zapewnia implementację dla foo, czy też nie.

Powodem tego ograniczenia jest to, że w przeciwnym razie bardzo łatwo byłoby obejść dostęp chroniony. Po prostu tworzysz klasę, taką jak Derived, i nagle masz także dostęp do części innych klas (takich jak OtherDerived), które miały być niedostępne dla osób postronnych.

+0

Dzięki, teraz jasno rozumiem powody ograniczenia ... To byłaby dziura w bezpieczeństwie ... duża! –

+5

Proszę nie myśleć o tym jako o luce bezpieczeństwa. Modyfikatory dostępu nie zapewniają żadnych zabezpieczeń, możesz po prostu odczytać lokalizację pamięci, jeśli chcesz dane. – DrYap

5

Zwykle zrobiłbyś to używając Base::foo(), która odnosi się do klasy bazowej bieżącej instancji.

Jeśli jednak twój kod wymaga tego w sposób, w jaki próbujesz, a to nie jest dozwolone, musisz uczynić foo() publicznym lub uczynić z Derived znajomego Base.

0

Bezpośrednie wywołanie funkcji podstawowych za pomocą operatora zasięgu (Base :: foo()). Ale w tym przypadku klasa Base nie definiuje foo (jest to czysto wirtualna), więc właściwie nie ma żadnej funkcji do wykonania, gdy mówisz this->b->foo();, ponieważ b jest wskaźnikiem do Base, a nie Derived.

+1

. Kod OP nie odnosi się do klasy bazowej bieżącej instancji. Ma dostęp do innej instancji, która prawdopodobnie jest klasą pochodną, ​​która implementuje funkcję czysto wirtualną. (W przeciwnym razie instancja nie mogłaby zostać utworzona.) –

+0

@ Jonathan Wood Rozumiem, co mówisz, ale po prostu wychodząc z kodu, który opublikował, wygląda na to, że próbuje utworzyć abstrakcyjną klasę podstawową (Base) i nazwać czystą funkcja wirtualna (Base :: foo()), która jest nie-nie (tak jak wspomniane wyżej GWW i 341008). – Gemini14

0

W jaki sposób uzyskujesz dostęp do chronionej funkcji ?

--- od czego?

Dostęp do chronionego członka można uzyskać tylko poprzez dziedziczenie (oprócz metod tej samej klasy). Załóżmy na przykład, że masz class Derived1, która dziedziczy po Derived, wtedy obiekty Derived1 mogą wywoływać foo().

EDYCJA: MSDN article w specyfikatorze chronionego dostępu.

1

To jest trochę delikatne, ale z klasami, które tu zdefiniowałeś, czy to nie zadziała?

virtual void foo2() { 
    reinterpret_cast<Derived *>(this->b)->foo(); 
} 

Punkty reinterpret_cast na VTABLE dla obiektu podstawowego i wywołuje to przez ten element członkowski.

2

Jednym z rozwiązań może być zadeklarowanie funkcji chronionej statycznie w Base, która przekierowuje połączenie do funkcji prywatnej/chronionej (w tym przykładzie: foo).

Powiedzmy:

class Base { 
protected: 
    static void call_foo(Base* base) { base->foo(); } 
private: 
    virtual void foo() = 0; 
}; 

class Derived : public Base { 
private: 
    Base* b; 
protected: 
    virtual void foo(){/* Some implementation */}; 
    virtual void foo2() 
    { 
     // b->foo(); // doesn't work 
     call_foo(b); // works 
    } 
}; 

ten sposób, że nie łamią enkapsulacji ponieważ projektant Base może dokonać wyraźnego wyboru, aby wszystkie klasy pochodne zadzwonić foo na siebie nawzajem, unikając umieścić foo do publicznego interfejsu lub jawnie zmieniając wszystkie możliwe podklasy na przyjaciół.

Ta metoda działa również niezależnie od tego, czy foo jest wirtualna czy nie, czy też jest prywatna lub chroniona.

Here to link do działającej wersji powyższego kodu i here innej wersji tego samego pomysłu z nieco większą logiką biznesową.

Powiązane problemy