2010-06-07 19 views
45

Ad esempio, la classe Base ha due metodi pubblici: foo() e bar(). La classe Derived è ereditata dalla classe Base. Nella classe Derived, voglio rendere pubblico il numero foo() pubblico ma bar(). Il seguente codice è il modo corretto e naturale per farlo?Come ereditare pubblicamente da una classe base, ma rendere alcuni metodi pubblici dalla classe base privata nella classe derivata?

class Base { 
    public: 
    void foo(); 
    void bar(); 
}; 

class Derived : public Base { 
    private: 
    void bar(); 
}; 

risposta

70

Sezione 11.3 dello standard C++ '03 descrive questa capacità:

11.3 dichiarazioni di accesso
L'accesso di un membro di una classe base può essere modificato nella classe derivata da accennare al suo qualificato -id nella dichiarazione derivata della classe . Tale menzione è chiamata una dichiarazione di accesso . L'effetto di una dichiarazione di accesso qualificata-id; è definita essere equivalente alla dichiarazione utilizzando qualificata id

Quindi ci sono 2 modi per farlo.

Nota: A partire da ISO C++ '11, le dichiarazioni di accesso (Base::bar;) sono vietate come indicato nei commenti. Al suo posto dovrebbe essere utilizzata una dichiarazione di utilizzo (using Base::bar;).

1) È possibile utilizzare l'ereditarietà pubblica e poi fare bar privato:

class Base { 
public: 
    void foo(){} 
    void bar(){} 
}; 

class Derived : public Base { 
private: 
    using Base::bar; 
}; 

2) È possibile utilizzare l'ereditarietà privata e poi fare foo pubblica:

class Base { 
public: 
    void foo(){} 
    void bar(){} 
}; 

class Derived : private Base { 
public: 
    using Base::foo; 
}; 

Nota: Se si avere un puntatore o riferimento di tipo Base che contiene un oggetto di tipo Derivato, quindi l'utente sarà ancora in grado di chiamare il membro.

+5

+1: Può essere fatto, ma era pensato per garantire l'accesso, non per limitarlo. –

+1

Ho scelto questa risposta come risposta accettata semplicemente dall'aspetto della funzione linguistica, non dall'aspetto delle migliori pratiche OOD. – powerboy

+2

-1 per l'uso di 'Base :: pippo' deprecato invece di' usando Base :: pippo' – Walter

11

Non c'è davvero nessun modo per fare quello che vuoi, perché se si deriva pubblicamente da Base, un utente della classe sarà sempre in grado di:

Derived d; 
Base& b = d; 
b.bar(); 

Non è "corretto o naturale "per rendere inaccessibili le funzioni di classe public class in una classe derivata dalla classe base; invece, l'interfaccia della classe base deve essere sottoposta a refactoring in modo tale che tali funzioni non siano pubbliche o suddivise in una classe separata.

+1

-1 perché la tua affermazione che non può essere fatto è sbagliata, ma +1 perché la tua asserzione che probabilmente non dovrebbe essere fatta è giusta –

+1

che dire inversa - derivando attraverso l'accesso privato e poi rendendo pubbliche le funzioni richieste? –

1

Diciamo che hai questo:

class Foo{ 
public: 
    void method1(); 
    void method2(); 
    void notGonnaSeeIt(); 
private: 
    //stuff 
}; 

Per concludere in modo efficace è possibile fare un patrimonio privato e passare i metodi da una dichiarazione pubblica come Brian suggerito:

class Bar : private Foo{ 
    void methodA(){ method1(); } 
    void methodB(){ method2(); } 
    //more stuff 
}; 

o si può avvolgere con un decoratore

template<class T> 
class Bar{ 
public: 
    Bar(T *_input) : m_Input(_input){} 
    void methodA() { m_Input->method1(); } 
    void methodB() { m_Input->method2(); } 
    //whatever else you need/want 
private: 
    T* m_Input; 
}; 

Personalmente, preferisco la via modello come ti permette di fare la stessa cosa con qualsiasi classe che erediti da Foo.

1

Se non è necessario trattarlo come classe base in seguito e sono necessarie solo alcune funzioni della classe base, è possibile utilizzare la composizione anziché l'ereditarietà? (C# è la mia prima lingua, ma ti viene l'idea)

class Base { 
    public void foo(); 
    public void bar(); 
}; 

class Derived { 
    private Base _base; 

    public void bar() { 
     _base.bar(); 
    } 
}; 
8

In primo luogo, devi capire cosa vuoi fare dalla prospettiva OOP. Esistono due tipi di ereditarietà completamente diversi:

  1. Ereditarietà dell'interfaccia. È quando si esegue implement in Java o in altri linguaggi che hanno interfacce come entità indipendenti, ma si verifica anche quando si eredita pubblicamente da una classe astratta vuota in C++. Qui non ti interessa affatto il codice ma vuoi dire al tuo compilatore e tutti usano le tue classi base/derivate che questa classe derivata è un tipo speciale di classe base, ha tutte le proprietà della classe base, si comporta esattamente come classe base, nella misura in cui è visibile all'utente, e può essere utilizzato al posto della classe base in qualsiasi algoritmo.

  2. Ereditarietà del codice. Hai una parte di codice nella classe base che vuoi riutilizzare nella tua classe derivata. La classe base e la classe derivata non devono essere correlate in alcun modo, vuoi solo riutilizzare il codice e basta.

eredità pubblico in C++ è il mix di entrambi i tipi, si ottiene l'ereditarietà di interfaccia e si ottiene il codice eredità pure. L'ereditarietà privata è un tipo diverso di bestie, si ottiene l'ereditarietà del codice solo, gli utenti della classe derivata non possono usarlo al posto della classe base e dalla base delle prospettive utente e le classi derivate non hanno alcuna relazione.

struct Base {}; 
struct PublicDerived : public Base {}; 
struct PrivateDerived: private Base {}; 

Base * base; PublicDerived * public_derived; PrivateDerived * private_derived; 

base = public_derived; //good 
base = private_derived; //compilation error. 

Dal momento che si desidera cambiare l'interfaccia, è non dovrebbe andare con l'ereditarietà pubblica, modificando l'interfaccia che si efficacemente dicendo che queste due classi hanno un comportamento differente e non possono essere usati in modo intercambiabile. Quindi quello che vuoi veramente è ereditare privatamente e quindi rendere tutti i metodi che vuoi pubblici e non il contrario allo.

2

In base allo Liskov Substitution Principle, l'ereditarietà pubblica deve essere modellata "is-a". Quello che stai dicendo con Derived ereditando pubblicamente da Base è che ovunque sia richiesto l'oggetto Base, un oggetto di tipo Derived farà.

Se si desidera impostare Derived per nascondere alcune operazioni disponibili per Base, ciò che si sta modellando è diverso da "is-a" e l'ereditarietà pubblica non è lo strumento corretto.

Ciò che si desidera è l'ereditarietà o la composizione privata, come altre risposte hanno dettagliato.

0

con C++ 11 si poteva effettuare le seguenti operazioni:

class Base { 
    public: 
    void foo(); 
    void bar(); 
}; 

class Derived : public Base { 
    private: 
    using Base::bar; 

}; 

Questo assicura che Base bar ::() non è accessibile dall'esterno della classe derivata. Ovviamente è accessibile anche al di fuori della classe Base.

Problemi correlati