Differenza tra eredità privata, pubblica e protetta


Risposte:


1065

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, protectede private.

Permettere:

class Base {
    public:
        int publicMember;
    protected:
        int protectedMember;
    private:
        int privateMember;
};
  • Tutto ciò che è consapevole Baseè anche consapevole che Basecontiene publicMember.
  • Solo i bambini (e i loro figli) sono consapevoli che Basecontiene protectedMember.
  • Nessuno ma ne Baseè consapevole privateMember.

Con "è a conoscenza di", intendo "riconoscere l'esistenza di, e quindi essere in grado di accedere".

Il prossimo:

Lo stesso succede con l'eredità pubblica, privata e protetta. Consideriamo una classe Basee una classe Childche eredita da Base.

  • Se l'eredità è public, tutto ciò che è consapevole Baseed Childè anche consapevole che Childeredita Base.
  • Se l'eredità è protected, solo Child, e i suoi figli, sono consapevoli che ereditano Base.
  • Se l'eredità è private, nessuno Childè a conoscenza dell'eredità.

183
Vorrei aggiungere alcune parole secondo cui la visibilità in C ++ si basa sulla classe anziché sull'oggetto, il che significa che gli oggetti della stessa classe possono accedere ai rispettivi campi privati ​​senza restrizioni.
Zhe Chen,

48
Se hai difficoltà a comprenderlo, leggi la risposta di Kirill V. Lyadvinsky, poi torna indietro e leggi questo.
Il Vivandiere il

6
Questo è solo un altro caso che illustra come, per la maggior parte, ereditare da 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.
underscore_d

1
@ZheChen se ho oggetti Tom e Jerry di classe Persona con età di campo privata come accedi (e modifichi?) L'età di Jerry usando Tom?
gen

2
Potresti illustrare cosa intendi per "consapevole dell'eredità"? Posso capire "posso accedere a questo non posso accedervi" ma non capisco quando si dice "So che A eredita da B" che cosa ci faccio qui sto controllando l'eredità?
Neilxdims,

1458
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 .


35
Ciò che Anzurio ha scritto ha fatto clic solo insieme alla tua risposta immediatamente di seguito. Плус 1.
Iwillnotexist Idonotexist

2
La mia comprensione di come ha funzionato è stata così lontana! Grazie mille per il chiarimento.
tjwrona1992,

mi ci è voluto del tempo per capirlo. Ma ora è chiaro. Grazie!
Chan Kim,

115

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_castdalla 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

  1. Eredità IS-A. Un pulsante è una finestra e ovunque sia necessaria una finestra, è possibile passare anche un pulsante.

    class button : public window { };

eredità protetta

  1. Protetto implementato in termini di. Raramente utile Utilizzato boost::compressed_pairper 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

  1. 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

  1. Aggregato

    class pair {
    public:
      First first;
      Second second;
    };
  2. di accesso

    class window {
    public:
        int getWidth() const;
    };

membro protetto

  1. Fornire un accesso migliorato per le classi derivate

    class stack {
    protected:
      vector<element> c;
    };
    
    class window {
    protected:
      void registerClass(window_descriptor w);
    };

membro privato

  1. 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.


7
Penso che Scott Myers (per quanto mi piaccia la sua roba) abbia molto da rispondere per la confusione generale. Ora penso che le sue analogie tra IS-A e IS-IMPLEMENTED-IN-TERMS-OF siano sufficienti per quello che sta succedendo.
DangerMouse,

65

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.

inserisci qui la descrizione dell'immagine

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, rin 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, znella 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 Supero quella Subclasse, deriva dalla Superclasse. La Subclasse non introduce né nuove variabili né nuove funzioni. Significa che qualsiasi oggetto della Subclasse eredita tutti i tratti dopo che la Superclasse è in realtà una copia degli Superoggetti di una classe?

No . Non

Se si compila il seguente codice, otterremo nulla, ma errori di compilazione dicendo che pute getmetodi 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 Subclasse possono fare "quasi" le stesse cose dei fratelli maggiori creati dalla Superclasse. "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 Subclasse che sia in grado di manipolare direttamente la variabile di archiviazione.

Questa è una limitazione molto seria. C'è qualche soluzione?

.

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 Subclasse 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

4
Il primo a menzionare la mancanza di un modificatore (come nella classe: SuperClass) rende privato. Questo è un pezzo importante che gli altri stanno perdendo, insieme a spiegazioni approfondite. +1
Acqua,

2
Overkill IMO, ma all'inizio mi piace il tavolo.
cp.engr

63

Ha a che fare con il modo in cui i membri pubblici della classe base sono esposti dalla classe derivata.

  • pubblico -> i membri pubblici della classe base saranno pubblici (di solito l'impostazione predefinita)
  • protetto -> i membri pubblici della classe base saranno protetti
  • privato -> i membri pubblici della classe base saranno privati

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.


13
Meglio dire "pubblico: l'eredità sarà vista da tutti". protetto: l'eredità sarà vista solo dalle classi e dagli amici derivati ​​"," privato: l'eredità sarà vista solo dalla classe stessa e dagli amici ". Questo è diverso dalla tua formulazione, poiché non solo i membri possono essere invisibili, ma anche la relazione IS-A può essere invisibile
Johannes Schaub - litb

4
L'unica volta che ho usato l'eredità privata è stato fare esattamente ciò che Doug T descrive, ad esempio "si desidera utilizzare l'interfaccia pubblica nella classe derivata, ma non si desidera che l'utente della classe derivata abbia accesso a tale interfaccia". In pratica l'ho usato per sigillare la vecchia interfaccia ed esporne un'altra attraverso la classe derivata.
Ricco

36
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

23
Questo fuorviante. I membri privati ​​di una classe base si comportano in modo abbastanza diverso dai normali membri della classe privata - non sono affatto accessibili dalla classe derivata. Penso che la tua colonna di tre "Privato" dovrebbe essere una colonna di "Inaccessibile". Vedi la risposta di Kirill V. Lyadvinsky a questa domanda.
Sam Kauffman,

27

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.


20

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:BDB

class B {};
class D {
  private: 
    B b_;
};

Anche questo Dpuò 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 protectedmodelli di ereditarietà. Almeno non ho ancora visto nessuna spiegazione convincente.


Alcuni dicono come una relazione. Come usare la sedia come un martello. Qui sedia: martello protetto
utente4951

quando si usa il contenimento anziché l'eredità privata non è conveniente come l'eredità privata? Per favore, puoi spiegarlo usando un esempio?
Distruttore

@Pravasi: se Dderivato privatamente D, può ignorare le funzioni virtuali di B. (Se, ad esempio, Bè un'interfaccia di osservatore, allora Dpotrebbe implementarla e passare thisa funzioni che richiedono una tale interfaccia, senza che tutti siano in grado di utilizzare Dcome osservatore.) Inoltre, Drendere selettivamente Bdisponibili i membri della sua interfaccia using B::member. Entrambi è sintatticamente scomodo da implementare quando Bè un membro.
sabato

@sbi: vecchio ma ... il contenimento è un no-go in caso di CRTP e / o virtuali (come hai correttamente descritto nel commento - ma ciò significa che non può essere modellato come contenimento se B ha metodi astratti e tu non è consentito toccarlo). protectederedità che ho trovato utile con una virtualclasse base e un protectedctor:struct CommonStuff { CommonStuff(Stuff*) {/* assert !=0 */ } }; struct HandlerMixin1 : protected virtual CommonStuff { protected: HandlerMixin1() : CommonStuff(nullptr) {} /*...*/ }; struct Handler : HandlerMixin1, ... { Handler(Stuff& stuff) : CommonStuff(&stuff) {} };
lorro,

11

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


9

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.myPrivateMembernon funzionerà. Tuttavia, lo this.myProtectedMemberfarà. Il valore è ancora incapsulato, quindi se abbiamo un'istanza di questa classe chiamata myObj, allora myObj.myProtectedMembernon funzionerà, quindi è simile nella funzione a un membro di dati privato.


8
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 :)


Java ha solo eredità pubblica
Zelldon,

Questo non è l'argomento per parlare di Java ma NO, ti sbagli ... Segui il link nella mia risposta sopra per i dettagli
Enissay

Hai citato java, quindi è l'argomento. E il tuo esempio gestisce gli specificatori che usano in jaca. La domanda riguarda gli specificatori dell'ereditarietà che non esistono in Java e che hanno fatto la differenza. Se un campo in superclasse è pubblico e l'eredità è privata, il campo è accessibile solo all'interno della sottoclasse. All'esterno non vi è alcuna indicazione se la sottoclasse estende la superclasse. Ma la tua tabella spiega solo gli identificatori per campo e metodi.
Zelldon,

7

Sommario:

  • Privato: nessuno può vederlo tranne all'interno della classe
  • Protetto: le classi private + derivate possono vederlo
  • Pubblico: il mondo può vederlo

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.


6

Privato:

I membri privati ​​di una classe base sono accessibili solo ai membri di quella classe base.

Pubblico:

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.

protetto:

I membri protetti di una classe base sono accessibili dai membri della classe base e dai membri della sua classe derivata.


In breve:

privato : base

protetto : base + derivato

pubblico : base + derivato + qualsiasi altro membro


5

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
}

3

È 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.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.