Qual è la differenza tra
public
,private
eprotected
l'ereditarietà in C ++?
Tutte le domande che ho trovato su SO riguardano casi specifici.
Qual è la differenza tra
public
,private
eprotected
l'ereditarietà in C ++?
Tutte le domande che ho trovato su SO riguardano casi specifici.
Risposte:
Per rispondere a questa domanda, vorrei descrivere prima gli accessor dei membri con parole mie. Se lo sai già, passa alla voce "successivo:".
Ci sono tre metodi di accesso che io sappia: public
, protected
e private
.
Permettere:
class Base {
public:
int publicMember;
protected:
int protectedMember;
private:
int privateMember;
};
Base
è anche consapevole che Base
contiene publicMember
.Base
contiene protectedMember
.Base
è consapevole privateMember
.Con "è a conoscenza di", intendo "riconoscere l'esistenza di, e quindi essere in grado di accedere".
Lo stesso succede con l'eredità pubblica, privata e protetta. Consideriamo una classe Base
e una classe Child
che eredita da Base
.
public
, tutto ciò che è consapevole Base
ed Child
è anche consapevole che Child
eredita Base
.protected
, solo Child
, e i suoi figli, sono consapevoli che ereditano Base
.private
, nessuno Child
è a conoscenza dell'eredità.SomeBase
è proprio come un modo hardcoded per comporre un membro di tipo anonimo SomeBase
. Questo, come qualsiasi altro membro, ha un identificatore di accesso, che esercita lo stesso controllo sull'accesso esterno.
class A
{
public:
int x;
protected:
int y;
private:
int z;
};
class B : public A
{
// x is public
// y is protected
// z is not accessible from B
};
class C : protected A
{
// x is protected
// y is protected
// z is not accessible from C
};
class D : private A // 'private' is default for classes
{
// x is private
// y is private
// z is not accessible from D
};
NOTA IMPORTANTE: le classi B, C e D contengono tutte le variabili x, ye z. È solo una questione di accesso.
Informazioni sull'utilizzo dell'eredità protetta e privata che puoi leggere qui .
Limitare la visibilità dell'ereditarietà renderà il codice non in grado di vedere che una classe eredita un'altra classe: le conversioni implicite dal derivato alla base non funzioneranno e static_cast
dalla base al derivato non funzionerà neanche.
Solo i membri / amici di una classe possono vedere l'eredità privata e solo i membri / amici e le classi derivate possono vedere l'eredità protetta.
eredità pubblica
Eredità IS-A. Un pulsante è una finestra e ovunque sia necessaria una finestra, è possibile passare anche un pulsante.
class button : public window { };
eredità protetta
Protetto implementato in termini di. Raramente utile Utilizzato boost::compressed_pair
per derivare da classi vuote e risparmiare memoria utilizzando l'ottimizzazione delle classi di base vuota (l'esempio seguente non utilizza il modello per rimanere nel punto):
struct empty_pair_impl : protected empty_class_1
{ non_empty_class_2 second; };
struct pair : private empty_pair_impl {
non_empty_class_2 &second() {
return this->second;
}
empty_class_1 &first() {
return *this; // notice we return *this!
}
};
eredità privata
Implementato-in-termini-di. L'uso della classe base è solo per l'implementazione della classe derivata. Utile con i tratti e se le dimensioni contano (tratti vuoti che contengono solo funzioni faranno uso dell'ottimizzazione della classe base vuota). Spesso il contenimento è la soluzione migliore, però. La dimensione delle stringhe è fondamentale, quindi è un uso spesso visto qui
template<typename StorageModel>
struct string : private StorageModel {
public:
void realloc() {
// uses inherited function
StorageModel::realloc();
}
};
membro pubblico
Aggregato
class pair {
public:
First first;
Second second;
};
di accesso
class window {
public:
int getWidth() const;
};
membro protetto
Fornire un accesso migliorato per le classi derivate
class stack {
protected:
vector<element> c;
};
class window {
protected:
void registerClass(window_descriptor w);
};
membro privato
Mantieni i dettagli di implementazione
class window {
private:
int width;
};
Notare che i lanci di tipo C consentono di lanciare intenzionalmente una classe derivata su una classe di base protetta o privata in modo definito e sicuro e di lanciare anche nell'altra direzione. Questo dovrebbe essere evitato a tutti i costi, perché può rendere il codice dipendente dai dettagli di implementazione, ma se necessario, è possibile utilizzare questa tecnica.
Queste tre parole chiave vengono utilizzate anche in un contesto completamente diverso per specificare il modello di ereditarietà della visibilità .
Questa tabella raccoglie tutte le possibili combinazioni della dichiarazione dei componenti e del modello di ereditarietà presentando l'accesso risultante ai componenti quando la sottoclasse è completamente definita.
La tabella sopra è interpretata nel modo seguente (dai un'occhiata alla prima riga):
se un componente viene dichiarata come pubblico e la sua classe è ereditata come pubblico la risultante di accesso è pubblico .
Un esempio:
class Super {
public: int p;
private: int q;
protected: int r;
};
class Sub : private Super {};
class Subsub : public Sub {};
L'accesso risultante per le variabili p
, q
, r
in classe Subsub è nessuno .
Un altro esempio:
class Super {
private: int x;
protected: int y;
public: int z;
};
class Sub : protected Super {};
L'accesso risultante per le variabili y
, z
nella classe Sub è protetto e per la variabile x
è nessuno .
Un esempio più dettagliato:
class Super {
private:
int storage;
public:
void put(int val) { storage = val; }
int get(void) { return storage; }
};
int main(void) {
Super object;
object.put(100);
object.put(object.get());
cout << object.get() << endl;
return 0;
}
Ora definiamo una sottoclasse:
class Sub : Super { };
int main(void) {
Sub object;
object.put(100);
object.put(object.get());
cout << object.get() << endl;
return 0;
}
La classe definita denominata Sub, che è una sottoclasse della classe denominata Super
o quella Sub
classe, deriva dalla Super
classe. La Sub
classe non introduce né nuove variabili né nuove funzioni. Significa che qualsiasi oggetto della Sub
classe eredita tutti i tratti dopo che la Super
classe è in realtà una copia degli Super
oggetti di una classe?
No . Non
Se si compila il seguente codice, otterremo nulla, ma errori di compilazione dicendo che put
e get
metodi sono inaccessibili. Perché?
Quando omettiamo l'identificatore di visibilità, il compilatore presuppone che applicheremo la cosiddetta eredità privata . Ciò significa che tutte le pubbliche componenti superclasse trasformano in privato l'accesso, i componenti della superclasse privati non saranno accessibili a tutti. Di conseguenza significa che non è consentito utilizzare quest'ultimo all'interno della sottoclasse.
Dobbiamo informare il compilatore che vogliamo preservare la politica di accesso precedentemente utilizzata.
class Sub : public Super { };
Non lasciarti ingannare : ciò non significa che i componenti privati della classe Super (come la variabile di archiviazione) diventeranno pubblici in un modo un po 'magico. I componenti privati rimarranno privati , il pubblico rimarrà pubblico .
Gli oggetti della Sub
classe possono fare "quasi" le stesse cose dei fratelli maggiori creati dalla Super
classe. "Quasi" perché il fatto di essere una sottoclasse significa anche che la classe ha perso l'accesso ai componenti privati della superclasse . Non possiamo scrivere una funzione membro della Sub
classe che sia in grado di manipolare direttamente la variabile di archiviazione.
Questa è una limitazione molto seria. C'è qualche soluzione?
Sì .
Il terzo livello di accesso è chiamato protetto . La parola chiave protetta significa che il componente contrassegnato con esso si comporta come uno pubblico quando viene utilizzato da una delle sottoclassi e assomiglia a un privato per il resto del mondo . - Questo vale solo per le classi ereditate pubblicamente (come la classe Super nel nostro esempio) -
class Super {
protected:
int storage;
public:
void put(int val) { storage = val; }
int get(void) { return storage; }
};
class Sub : public Super {
public:
void print(void) {cout << "storage = " << storage;}
};
int main(void) {
Sub object;
object.put(100);
object.put(object.get() + 1);
object.print();
return 0;
}
Come vedi nel codice di esempio, abbiamo una nuova funzionalità per la Sub
classe e fa una cosa importante: accede alla variabile di archiviazione dalla classe Super .
Non sarebbe possibile se la variabile fosse dichiarata come privata. Nell'ambito della funzione principale la variabile rimane comunque nascosta, quindi se scrivi qualcosa come:
object.storage = 0;
Il compilatore ti informerà che è un error: 'int Super::storage' is protected
.
Infine, l'ultimo programma produrrà il seguente output:
storage = 101
Ha a che fare con il modo in cui i membri pubblici della classe base sono esposti dalla classe derivata.
Come sottolinea litb, l'eredità pubblica è l'eredità tradizionale che vedrai nella maggior parte dei linguaggi di programmazione. Cioè modella una relazione "IS-A". L'eredità privata, qualcosa di AFAIK peculiare del C ++, è una relazione "ATTUATA IN TERMINI DI". Cioè si desidera utilizzare l'interfaccia pubblica nella classe derivata, ma non si desidera che l'utente della classe derivata abbia accesso a tale interfaccia. Molti sostengono che in questo caso è necessario aggregare la classe base, ovvero anziché avere la classe base come base privata, creare un membro derivato per riutilizzare la funzionalità della classe base.
Member in base class : Private Protected Public
Tipo di ereditarietà : oggetto ereditato come :
Private : Inaccessible Private Private
Protected : Inaccessible Protected Protected
Public : Inaccessible Protected Public
1) Eredità pubblica :
un. I membri privati della classe Base non sono accessibili nella classe Derivata.
b. I membri protetti della classe Base rimangono protetti nella classe Derivata.
c. I membri pubblici della classe Base rimangono pubblici nella classe Derivata.
Pertanto, altre classi possono utilizzare membri pubblici della classe Base tramite l'oggetto della classe derivata.
2) Eredità protetta :
un. I membri privati della classe Base non sono accessibili nella classe Derivata.
b. I membri protetti della classe Base rimangono protetti nella classe Derivata.
c. Anche i membri pubblici della classe Base diventano membri protetti della classe Derivata.
Pertanto, altre classi non possono utilizzare membri pubblici della classe Base tramite l'oggetto classe Derivato; ma sono disponibili per la sottoclasse di Derivato.
3) Eredità privata :
un. I membri privati della classe Base non sono accessibili nella classe Derivata.
b. I membri protetti e pubblici della classe Base diventano membri privati della classe Derivata.
Pertanto, nessun membro della classe Base può accedere ad altre classi tramite l'oggetto classe Derivato poiché sono privati nella classe Derivata. Pertanto, anche la sottoclasse della classe Derivata non può accedervi.
L'eredità pubblica modella una relazione IS-A. Con
class B {};
class D : public B {};
ogni D
è un B
.
L'eredità privata modella una relazione IS-IMPLEMENTED-USING (o come viene chiamata). Con
class B {};
class D : private B {};
a nonD
è a , ma ognuno ne usa nella sua implementazione. L'eredità privata può sempre essere eliminata utilizzando invece il contenimento:B
D
B
class B {};
class D {
private:
B b_;
};
Anche questo D
può essere implementato usando B
, in questo caso usando il suo b_
. Il contenimento è un accoppiamento meno stretto tra i tipi rispetto all'eredità, quindi in generale dovrebbe essere preferito. A volte usare il contenimento anziché l'eredità privata non è conveniente come l'eredità privata. Spesso questa è una scusa per essere pigra.
Non credo che nessuno sappia quali protected
modelli di ereditarietà. Almeno non ho ancora visto nessuna spiegazione convincente.
D
derivato privatamente D
, può ignorare le funzioni virtuali di B
. (Se, ad esempio, B
è un'interfaccia di osservatore, allora D
potrebbe implementarla e passare this
a funzioni che richiedono una tale interfaccia, senza che tutti siano in grado di utilizzare D
come osservatore.) Inoltre, D
rendere selettivamente B
disponibili i membri della sua interfaccia using B::member
. Entrambi è sintatticamente scomodo da implementare quando B
è un membro.
protected
eredità che ho trovato utile con una virtual
classe base e un protected
ctor:struct CommonStuff { CommonStuff(Stuff*) {/* assert !=0 */ } }; struct HandlerMixin1 : protected virtual CommonStuff { protected: HandlerMixin1() : CommonStuff(nullptr) {} /*...*/ }; struct Handler : HandlerMixin1, ... { Handler(Stuff& stuff) : CommonStuff(&stuff) {} };
Se erediti pubblicamente da un'altra classe, tutti sanno che stai ereditando e puoi essere usato polimorficamente da chiunque tramite un puntatore di classe base.
Se erediti in modo protetto, solo le lezioni dei tuoi figli saranno in grado di usarti polimorficamente.
Se erediti privatamente, solo te stesso sarà in grado di eseguire metodi di classe genitore.
Il che in sostanza simboleggia la conoscenza che il resto delle classi ha sulla tua relazione con la tua classe genitore
I membri di dati protetti possono accedere a qualsiasi classe che eredita dalla tua classe. I membri di dati privati, tuttavia, non possono. Diciamo che abbiamo il seguente:
class MyClass {
private:
int myPrivateMember; // lol
protected:
int myProtectedMember;
};
Dalla tua estensione a questa classe, il riferimento this.myPrivateMember
non funzionerà. Tuttavia, lo this.myProtectedMember
farà. Il valore è ancora incapsulato, quindi se abbiamo un'istanza di questa classe chiamata myObj
, allora myObj.myProtectedMember
non funzionerà, quindi è simile nella funzione a un membro di dati privato.
Accessors | Base Class | Derived Class | World
—————————————+————————————+———————————————+———————
public | y | y | y
—————————————+————————————+———————————————+———————
protected | y | y | n
—————————————+————————————+———————————————+———————
private | | |
or | y | n | n
no accessor | | |
y: accessible
n: not accessible
Sulla base di questo esempio per Java ... Penso che un tavolino valga più di mille parole :)
Sommario:
Quando si eredita, è possibile (in alcune lingue) modificare il tipo di protezione di un membro di dati in una determinata direzione, ad esempio da protetto a pubblico.
I membri privati di una classe base sono accessibili solo ai membri di quella classe base.
Ai membri pubblici di una classe di base è possibile accedere dai membri di quella classe di base, dai membri della sua classe derivata e dai membri che si trovano al di fuori della classe di base e della classe derivata.
I membri protetti di una classe base sono accessibili dai membri della classe base e dai membri della sua classe derivata.
privato : base
protetto : base + derivato
pubblico : base + derivato + qualsiasi altro membro
Ho trovato una risposta semplice e quindi ho pensato di pubblicarla anche come riferimento futuro.
È dai link http://www.learncpp.com/cpp-tutorial/115-inheritance-and-access-specifiers/
class Base
{
public:
int m_nPublic; // can be accessed by anybody
private:
int m_nPrivate; // can only be accessed by Base member functions (but not derived classes)
protected:
int m_nProtected; // can be accessed by Base member functions, or derived classes.
};
class Derived: public Base
{
public:
Derived()
{
// Derived's access to Base members is not influenced by the type of inheritance used,
// so the following is always true:
m_nPublic = 1; // allowed: can access public base members from derived class
m_nPrivate = 2; // not allowed: can not access private base members from derived class
m_nProtected = 3; // allowed: can access protected base members from derived class
}
};
int main()
{
Base cBase;
cBase.m_nPublic = 1; // allowed: can access public members from outside class
cBase.m_nPrivate = 2; // not allowed: can not access private members from outside class
cBase.m_nProtected = 3; // not allowed: can not access protected members from outside class
}
È essenzialmente la protezione dell'accesso del pubblico e dei membri protetti della classe base nella classe derivata. Con l'eredità pubblica, la classe derivata può vedere membri pubblici e protetti della base. Con l'eredità privata, non può. Con protetto, la classe derivata e qualsiasi classe derivata da quella possono vederli.