2011-01-12 14 views
8

Ecco un esempio di codice che mi infastidisce:Come accedere al metodo protetto nella classe base dalla classe derivata?

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

Come si accede alla funzione overrided protetta?

Grazie per il vostro aiuto. : o)

+7

Non penso che la vostra implementazione sia giusta. Perché hai un'istanza di Base come variabile membro? questo-> b-> pippo() proverebbe a chiamare un metodo virtuale puro. – GWW

+1

Questo programma non dovrebbe essere compilato. Non è possibile creare un'istanza di una classe astratta .... A meno che 'b' stia puntando a un'istanza di un'altra classe derivata da' Base'. – 341008

+0

Ho omesso precisione: l'attributo Derived :: b è inteso per memorizzare qualsiasi istanza di classi derivate da Base –

risposta

8

I membri protetti in una classe base sono accessibili solo dall'oggetto corrente.
Pertanto, è possibile chiamare this->foo(), ma non è consentito chiamare this->b->foo(). Ciò è indipendente dal fatto che Derived fornisca o meno un'implementazione per foo.

La ragione di questa limitazione è che sarebbe altrimenti molto semplice aggirare l'accesso protetto. Devi solo creare una classe come Derived e improvvisamente hai accesso anche a parti di altre classi (come OtherDerived) che dovevano essere inaccessibili agli estranei.

+0

Grazie, ora capisco chiaramente le ragioni della restrizione ... Sarebbe un buco di sicurezza ... grande! –

+5

Si prega di non considerarlo un buco di sicurezza. I modificatori di accesso non forniscono alcuna sicurezza, puoi solo leggere il percorso di memoria se desideri i dati. – DrYap

5

Normalmente, lo si farebbe utilizzando , che si riferisce alla classe base dell'istanza corrente.

Tuttavia, se il codice ha bisogno di farlo nel modo in cui si sta provando e non è consentito, allora è necessario rendere pubblico foo() o rendere Derived un amico di Base.

0

Si richiamano esplicitamente le funzioni di base con l'operatore di ambito (Base :: pippo()). Ma in questo caso, la classe Base non definisce foo (è puro virtuale), quindi non c'è in realtà alcuna funzione da eseguire quando si dice this->b->foo(); poiché b è un puntatore a Base e non derivato.

+1

Ma il codice dell'OP non fa riferimento alla classe base dell'istanza corrente. Sta accedendo a un'altra istanza, che presumibilmente è una classe derivata che implementa la funzione puramente virtuale. (Altrimenti, l'istanza non può essere creata.) –

+0

@Jonathan Wood Capisco cosa stai dicendo, ma andando dal codice che ha pubblicato, sembra che stia cercando di creare un'istanza di una classe base astratta (Base) e di chiamare un puro funzione virtuale (Base :: foo()), che è un no-no (come GWW e 341008 anche menzionati sopra). – Gemini14

0

Come si accede alla funzione protetta annullata?

--- da dove?

È possibile accedere a un membro protetto solo tramite ereditarietà (a parte i metodi della stessa classe). Supponiamo ad esempio di avere un class Derived1 che eredita da Derived, quindi gli oggetti di Derived1 possono chiamare foo().

MODIFICA: MSDN article su identificatore di accesso protetto.

1

È un po 'fragile, ma con le classi che hai definito qui, non funzionerà?

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

I punti reinterpret_cast al VTABLE per l'oggetto di base, e chiede attraverso questo membri di accesso.

2

Una soluzione sarebbe dichiarare una funzione protetta statica in Base che reindirizza la chiamata alla funzione privata/protetta (foo nell'esempio).

consente di dire:

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

In questo modo, non si rompono incapsulamento perché il progettista di Base può fare una scelta esplicita per consentire tutte le classi derivate di chiamare foo a vicenda, evitando di mettere foo nell'interfaccia pubblica o esplicitamente trasformando tutte le possibili sottoclassi di Base in amici.

Inoltre, questo metodo funziona indipendentemente dal fatto che foo sia virtuale o meno, o che sia privato o protetto.

Here è un collegamento a una versione in esecuzione del codice sopra e here un'altra versione della stessa idea con un po 'più di logica aziendale.

Problemi correlati