c++ Differenza tra eredità privata, pubblica e protetta




inheritance encapsulation (12)

Qual è la differenza tra ereditarietà public , private e protected in C ++? Tutte le domande che ho trovato su SO trattano casi specifici.


Answer #1

Privato:

I membri privati ​​di una classe base possono accedere solo ai membri di quella classe base.

Pubblico:

I membri pubblici di una classe base possono accedere ai membri di quella classe base, ai membri della sua classe derivata e ai membri che sono al di fuori della classe base e della classe derivata.

protetto:

I membri protetti di una classe base possono accedere ai membri della classe base e ai membri della classe derivata.

In breve:

privato : base

protetto : base + derivato

pubblico : base + derivato + qualsiasi altro membro


Answer #2

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 il valore predefinito)
  • protetto - i membri pubblici della classe base saranno protetti
  • privato -> i membri pubblici della classe base saranno privati

Come evidenziato da Litb, l'ereditarietà pubblica è un'eredità tradizionale che vedrai nella maggior parte dei linguaggi di programmazione. Questo è un modello di una relazione "IS-A". L'ereditarietà privata, qualcosa di AFAIK peculiare del C ++, è una relazione "IMPLEMENTATA IN TERMINI DI". 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 dovresti aggregare la classe base, che invece di avere la classe base come base privata, creare un membro di derivata per riutilizzare la funzionalità della classe base.


Answer #3

Per rispondere a questa domanda, mi piacerebbe descrivere prima le parole di accesso dei membri con parole mie. Se già lo sai, vai all'intestazione "next:".

Ci sono tre elementi di accesso di cui sono a conoscenza: public , protected e private .

Permettere:

class Base {
    public:
        int publicMember;
    protected:
        int protectedMember;
    private:
        int privateMember;
};
  • Tutto ciò che è a conoscenza di Base è anche consapevole del fatto che Base contiene publicMember .
  • Solo i bambini (ei loro figli) sono consapevoli che Base contiene l' Base protectedMember .
  • Nessuno tranne Base è a conoscenza di privateMember .

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

Il prossimo:

Lo stesso accade con l'eredità pubblica, privata e protetta. Consideriamo una classe Base e una classe Child che eredita da Base .

  • Se l'ereditarietà è public , tutto ciò che è a conoscenza di Base e Child è anche consapevole del fatto che Child eredita dalla Base .
  • Se l'ereditarietà è protected , solo Child e i suoi figli sono consapevoli del fatto che ereditano da Base .
  • Se l'ereditarietà è private , nessuno al di fuori di Child è a conoscenza dell'eredità.

Answer #4

Queste tre parole chiave vengono anche utilizzate in un contesto completamente diverso per specificare il modello di ereditarietà della visibilità .

Questa tabella raccoglie tutte le possibili combinazioni della dichiarazione del componente e del modello di ereditarietà che presenta l'accesso risultante ai componenti quando la sottoclasse è completamente definita.

La tabella sopra è interpretata nel modo seguente (date un'occhiata alla prima riga):

se un componente è dichiarato pubblico e la sua classe è ereditata come pubblica, l' accesso risultante è 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 nella sottostruttura di classe non è 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 nessuna .

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 consente di definire 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 di classe denominata Super o che Sub , deriva dalla classe Super . La classe Sub non introduce né nuove variabili né nuove funzioni. Significa che qualsiasi oggetto della classe Sub eredita tutti i tratti dopo che la classe Super è in effetti una copia di oggetti di una classe Super ?

No Non è così.

Se compiliamo il seguente codice, non otterremo altro che errori di compilazione che dicono che i metodi put e get sono inaccessibili. Perché?

Quando omettiamo lo specificatore di visibilità, il compilatore presuppone che applicheremo la cosiddetta ereditarietà privata . Significa che tutte le componenti della superclasse pubblica diventano accesso privato, le componenti della superclasse privata non saranno affatto accessibili. Di conseguenza, significa che non ti è permesso usare quest'ultimo nella sottoclasse.

Dobbiamo informare il compilatore che vogliamo preservare la politica di accesso precedentemente utilizzata.

class Sub : public Super { };

Non lasciatevi 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 .

Oggetti della classe Sub possono fare "quasi" le stesse cose dei loro fratelli più grandi creati dalla classe Super . "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 classe Sub che sia in grado di manipolare direttamente la variabile di archiviazione.

Questa è una restrizione 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 pubblico quando utilizzato da una delle sottoclassi e sembra privato per il resto del mondo . - Questo è vero 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 classe Sub 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 si tratta di un error: 'int Super::storage' is protected .

Infine, l'ultimo programma produrrà il seguente risultato:

storage = 101

Answer #5
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, yez. È solo una questione di accesso.

Informazioni sull'utilizzo dell'eredità protetta e privata che potresti leggere here .


Answer #6

È essenzialmente la protezione dell'accesso dei membri pubblici e protetti della classe base nella classe derivata. Con l'ereditarietà pubblica, la classe derivata può vedere membri pubblici e protetti della base. Con l'ereditarietà privata, non può. Con protected, la classe derivata e le classi derivate da ciò possono vederle.


Answer #7

Limitare la visibilità dell'eredità renderà il codice non in grado di vedere che alcune classi ereditano un'altra classe: le conversioni implicite dalla derivata alla base non funzioneranno, e anche static_cast dalla base alla derivata non funzionerà.

Solo i membri / amici di una classe possono vedere l'ereditarietà privata e solo i membri / amici e le classi derivate possono vedere l'ereditarietà protetta.

eredità pubblica

  1. IS-A ereditarietà. Un pulsante è -una finestra, e ovunque sia necessaria una finestra, può essere passato anche un pulsante.

    class button : public window { };
    

eredità protetta

  1. Protetto implementato in termini di. Raramente utile. Utilizzato in boost::compressed_pair per derivare da classi vuote e risparmiare memoria utilizzando l'ottimizzazione della classe base vuota (nell'esempio seguente non viene utilizzato il modello per continuare a essere al 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'utilizzo della classe base è solo per l'implementazione della classe derivata. Utile con i tratti e se le dimensioni contano (i tratti vuoti che contengono solo funzioni si avvarranno dell'ottimizzazione della classe base vuota). Spesso il contenimento è la soluzione migliore, però. La dimensione delle stringhe è fondamentale, quindi è un utilizzo 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 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;
    };
    

Si noti che i cast in stile C consentono espressamente di convertire una classe derivata in 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, puoi usare questa tecnica.


Answer #8

È possibile accedere ai membri dei dati protetti da qualsiasi classe che erediti dalla classe. I membri dati privati, tuttavia, non possono. Diciamo che abbiamo il seguente:

class MyClass {
    private:
        int myPrivateMember;    // lol
    protected:
        int myProtectedMember;
};

Dall'interno della tua estensione a questa classe, il riferimento a this.myPrivateMember non funzionerà. Tuttavia, this.myProtectedMember farà. Il valore è ancora incapsulato, quindi se abbiamo un'istanza di questa classe chiamata myObj , myObj.myProtectedMember non funzionerà, quindi è simile in funzione a un membro dati privato.


Answer #9

Se erediti pubblicamente da un'altra classe, tutti sanno che stai ereditando e che puoi essere usato in modo polimorfico da chiunque attraverso un puntatore di classe base.

Se erediti in modo protetto, solo le lezioni dei tuoi figli saranno in grado di utilizzarti in modo polimorfico.

Se erediti privatamente solo tu stesso sarai in grado di eseguire i metodi della classe genitore.

Che simboleggia fondamentalmente la conoscenza del resto delle classi sulla tua relazione con la tua classe genitore


Answer #10
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

Answer #11

L'ereditarietà pubblica 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 qualsiasi altra cosa sia chiamata). Con

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

una D non è una B , ma ogni D usa la sua B nella sua implementazione. L'ereditarietà privata può sempre essere eliminata utilizzando il contenimento invece:

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'ereditarietà privata non è conveniente quanto l'ereditarietà privata. Spesso questa è una scusa pessima per essere pigri.

Non credo che nessuno sappia cosa sono i modelli di eredità protected . Almeno non ho ancora visto nessuna spiegazione convincente.


Answer #12
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

Basato su this esempio per java ... Penso che un piccolo tavolo vale più di mille parole :)





c++-faq