web-dev-qa-db-fra.com

Différence entre héritage privé, public et protégé

Quelle est la différence entre l'héritage public, private et protected en C++? Toutes les questions que j'ai trouvées sur SO traitent de cas spécifiques.

932
user106599

Pour répondre à cette question, je voudrais décrire les accesseurs du membre d'abord avec mes propres mots. Si vous le savez déjà, passez à la rubrique "suivant:".

À ma connaissance, il existe trois accesseurs: public, protected et private.

Laisser:

class Base {
    public:
        int publicMember;
    protected:
        int protectedMember;
    private:
        int privateMember;
};
  • Tout ce qui est conscient de Base sait également que Base contient publicMember.
  • Seuls les enfants (et leurs enfants) savent que Base contient protectedMember.
  • Personne d'autre que Base n'a connaissance de privateMember.

Par "est conscient de", j'entends "reconnaître l'existence de et donc pouvoir y accéder".

suivant:

Il en va de même pour les héritages publics, privés et protégés. Considérons une classe Base et une classe Child qui hérite de Base.

  • Si l'héritage est public, tout ce qui est conscient de Base et Child sait également que Child hérite de Base.
  • Si l'héritage est protected, seuls Child et ses enfants sont conscients qu'ils héritent de Base.
  • Si l'héritage est private, personne d'autre que Child n'est au courant de l'héritage.
1002
Anzurio
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
};

REMARQUE IMPORTANTE: Les classes B, C et D contiennent toutes les variables x, y et z. C'est juste une question d'accès.

À propos de l’utilisation des héritages protégé et privé, vous pouvez lire ici .

1372

Limiter la visibilité de l'héritage rendra le code incapable de voir qu'une classe hérite d'une autre classe: les conversions implicites du dérivé vers la base ne fonctionneront pas, et static_cast de la base vers le dérivé ne fonctionnera pas non plus.

Seuls les membres/amis d'une classe peuvent voir l'héritage privé, et seuls les membres/amis et les classes dérivées peuvent voir l'héritage protégé.

public héritage

  1. Héritage IS-A. Un bouton est une fenêtre, et partout où une fenêtre est nécessaire, un bouton peut également être transmis.

    class button : public window { };
    

protégé héritage

  1. Protégé mis en œuvre en termes de. Rarement utile. Utilisé dans boost::compressed_pair pour dériver des classes vides et économiser de la mémoire en utilisant l'optimisation des classes de base vides (l'exemple ci-dessous n'utilise pas de modèle pour rester au même point):

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

privé héritage

  1. Mis en œuvre en termes de. L'utilisation de la classe de base est uniquement destinée à l'implémentation de la classe dérivée. Utile avec les traits et si la taille compte (les traits vides contenant uniquement des fonctions utiliseront l'optimisation de la classe de base vide). Bien souvent, le confinement est la meilleure solution. La taille des chaînes est critique, il est donc souvent utilisé ici

    template<typename StorageModel>
    struct string : private StorageModel {
    public:
      void realloc() {
        // uses inherited function
        StorageModel::realloc();
      }
    };
    

public membre

  1. Agrégat

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

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

protégé membre

  1. Fournir un accès amélioré aux classes dérivées

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

privé membre

  1. Conserver les détails de la mise en œuvre

    class window {
    private:
      int width;
    };
    

Notez que les transtypages de style C permettent à dessein de transtyper une classe dérivée vers une classe de base protégée ou privée de manière définie et sécurisée, ainsi que dans l'autre sens. Cela doit être évité à tout prix, car cela peut rendre le code dépendant des détails de l'implémentation - mais si nécessaire, vous pouvez utiliser cette technique.

107

Cela concerne la manière dont les membres publics de la classe de base sont exposés à partir de la classe dérivée.

  • public -> les membres publics de la classe de base seront publics (généralement par défaut)
  • protected - les membres du public de la classe de base seront protégés
  • private -> les membres publics de la classe de base seront private

Comme le fait remarquer litb, l'héritage public est l'héritage traditionnel que vous verrez dans la plupart des langages de programmation. C'est-à-dire qu'il modélise une relation "IS-A". L'héritage privé, une caractéristique AFAIK propre au C++, est une relation "MISE EN ŒUVRE EN TERMES DE". C’est-à-dire que vous souhaitiez tiliser l’interface publique de la classe dérivée, mais ne souhaitez pas que l’utilisateur de la classe dérivée ait accès à cette interface. Beaucoup soutiennent que dans ce cas, vous devez agréger la classe de base, c'est-à-dire, au lieu d'avoir la classe de base comme base privée, créez un membre de dérivé afin de réutiliser les fonctionnalités de la classe de base.

62
Doug T.

Ces trois mots-clés sont également utilisés dans un contexte complètement différent pour spécifier le modèle d'héritage de visibilité .

Cette table regroupe toutes les combinaisons possibles du modèle de déclaration de composant et d'héritage présentant l'accès résultant aux composants lorsque la sous-classe est complètement définie.

enter image description here

Le tableau ci-dessus est interprété de la manière suivante (regardez la première ligne):

si un composant est déclaré comme public et que sa classe est hérité as public l'accès résultant est public .

Un exemple:

 class Super {
    public:      int p;
    private:     int q;
    protected:   int r;
 };

 class Sub : private Super {};

 class Subsub : public Sub {};

L’accès résultant pour les variables p, q, r dans la classe Le sous-sous est none .

Un autre exemple:

class Super {
    private:     int x;
    protected:   int y;
    public:      int z;
 };
class Sub : protected Super {};

L'accès résultant pour les variables y, z de la classe , Sub est protégé et pour la variable x est none .

Un exemple plus détaillé:

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

Définissons maintenant une sous-classe:

class Sub : Super { };

int main(void) {
    Sub object;

    object.put(100);
    object.put(object.get());
    cout << object.get() << endl;
    return 0;
}

La classe définie nommée Sub, qui est une sous-classe de la classe nommée Super ou cette classe Sub, est dérivée de la classe Super. La classe Sub n'introduit ni nouvelles variables ni nouvelles fonctions. Cela signifie-t-il que tout objet de la classe Sub hérite de tous les traits après la classe Super étant en fait une copie des objets de la classe Super?

Non . Ce n’est pas le cas.

Si nous compilons le code suivant, nous n'obtiendrons que des erreurs de compilation indiquant que les méthodes put et get sont inaccessibles. Pourquoi?

Lorsque nous omettons le spécificateur de visibilité, le compilateur suppose que nous allons appliquer l'héritage privé dit . Cela signifie que tous les composants publics de la superclasse se transforment en accès privé , les composants de la superclasse privés ne seront pas du tout accessibles. Cela signifie par conséquent que vous n'êtes pas autorisé à utiliser ce dernier à l'intérieur de la sous-classe.

Nous devons informer le compilateur que nous souhaitons préserver la stratégie d'accès précédemment utilisée.

class Sub : public Super { };

Ne vous y trompez pas : cela ne signifie pas que les composants privés de la classe Super (comme la variable de stockage) se transformeront en composants publics manière magique. Les composants privés resteront privés , publics resteront public .

Les objets de la classe Sub peuvent faire "presque" la même chose que leurs frères et soeurs plus anciens créés à partir de la classe Super. "Presque" car le fait d’être une sous-classe signifie aussi que la classe a perdu l’accès aux composants privés de la superclasse . Nous ne pouvons pas écrire de fonction membre de la classe Sub qui pourrait manipuler directement la variable de stockage.

C'est une restriction très grave. Y at-il une solution de contournement?

Oui .

Le troisième niveau d'accès est appelé protected . Le mot-clé protected signifie que le composant indiqué par celui-ci se comporte comme un composant public lorsqu'il est utilisé par l'une des sous-classes et ressemble à un composant privé pour le reste du monde . - Ceci n'est vrai que pour les classes héritées publiquement (comme la classe Super dans notre exemple) -

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

Comme vous le voyez dans l'exemple de code, nous avons ajouté une nouvelle fonctionnalité à la classe Sub. Cette dernière a une fonction importante: , elle accède à la variable de stockage depuis la classe Super .

Ce ne serait pas possible si la variable était déclarée privée. Dans la portée de la fonction principale, la variable reste de toute façon cachée, donc si vous écrivez quelque chose comme:

object.storage = 0;

Le compilateur vous informera qu'il s'agit d'un error: 'int Super::storage' is protected.

Enfin, le dernier programme produira le résultat suivant:

storage = 101
58
BugShotGG
Member in base class : Private   Protected   Public   

type d'héritage: objet hérité en tant que:

Private            :   Inaccessible   Private     Private   
Protected          :   Inaccessible   Protected   Protected  
Public             :   Inaccessible   Protected   Public
35
kinshuk4

1) héritage public:

une. Les membres privés de la classe de base ne sont pas accessibles dans la classe dérivée.

b. Les membres protégés de la classe de base restent protégés dans la classe dérivée.

c. Les membres publics de la classe de base restent publics dans la classe dérivée.

Ainsi, les autres classes peuvent utiliser des membres publics de la classe de base via l'objet de classe dérivée.

2) héritage protégé:

une. Les membres privés de la classe de base ne sont pas accessibles dans la classe dérivée.

b. Les membres protégés de la classe de base restent protégés dans la classe dérivée.

c. Les membres publics de la classe de base deviennent également des membres protégés de la classe dérivée.

Ainsi, les autres classes ne peuvent pas utiliser les membres publics de la classe de base via l'objet de classe dérivée; mais ils sont disponibles à la sous-classe de Derived.

) héritage privé:

une. Les membres privés de la classe de base ne sont pas accessibles dans la classe dérivée.

b. Les membres protégés et publics de la classe de base deviennent des membres privés de la classe dérivée.

Ainsi, aucun membre de la classe Base ne peut être accédé par d'autres classes via l'objet classe Derived car elles sont privées dans la classe Derived. Ainsi, même la sous-classe de la classe Derived ne peut pas y accéder.

25
yuvi

L'héritage public modélise une relation IS-A. Avec

class B {};
class D : public B {};

chaque Dest aB.

L'héritage privé modélise une relation IS-MISE EN OEUVRE (ou peu importe comment on l'appelle). Avec

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

a D est pas a B, mais chaque D utilise sa B dans son implémentation. L'héritage privé peut toujours être éliminé en utilisant plutôt le confinement:

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

Cette D peut également être implémentée avec B, dans ce cas en utilisant son b_. Le confinement est un couplage moins étroit entre les types que l’héritage, il devrait donc être préféré en général. Parfois, l’utilisation du confinement au lieu de l’héritage privé n’est pas aussi pratique que l’héritage privé. Souvent, c'est une excuse boiteuse pour être paresseux.

Je ne pense pas que quiconque sache ce que protected modèles d'héritage. Au moins, je n'ai pas encore vu d'explication convaincante.

19
sbi

Si vous héritez publiquement d'une autre classe, tout le monde sait que vous héritez et vous pouvez être utilisé de manière polymorphe par quiconque via un pointeur de classe de base.

Si vous héritez de manière protégée, seules les classes de votre enfant pourront vous utiliser de manière polymorphe.

Si vous héritez en privé, seul vous pourrez exécuter les méthodes de la classe parent.

Ce qui symbolise fondamentalement la connaissance que les autres classes ont de votre relation avec votre classe parente

12
Arkaitz Jimenez

Les membres protégés des données sont accessibles à toutes les classes héritées de votre classe. Les membres de données privées, cependant, ne peuvent pas. Disons que nous avons ce qui suit:

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

Depuis votre extension vers cette classe, référencer this.myPrivateMember ne fonctionnera pas. Cependant, this.myProtectedMember le sera. La valeur est toujours encapsulée, donc si nous avons une instanciation de cette classe appelée myObj, alors myObj.myProtectedMember ne fonctionnera pas. Par conséquent, sa fonction est similaire à celle d'un membre de données privé.

9
Andrew Noyes
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

Basé sur this exemple pour Java ... je pense un petit tableau qui vaut mille mots :)

8
Enissay

Sommaire:

  • Privé: personne ne peut le voir sauf dans la classe
  • Protected: Les classes privées + dérivées peuvent le voir
  • Public: le monde peut le voir

Lors de l'héritage, vous pouvez (dans certaines langues) changer le type de protection d'un membre de données dans certaines directions, par exemple. de protégé au public.

7
Roee Adler

Privé:

Les membres privés d'une classe de base ne sont accessibles qu'aux membres de cette classe de base.

Publique:

Les membres publics d'une classe de base sont accessibles aux membres de cette classe de base, aux membres de sa classe dérivée ainsi qu'aux membres extérieurs à la classe de base et à la classe dérivée.

Protégé:

Les membres protégés d'une classe de base sont accessibles aux membres de la classe de base ainsi qu'aux membres de sa classe dérivée.


En bref:

privé: base

protégé: base + dérivé

public: base + dérivé + tout autre membre

6
varun

J'ai trouvé une réponse facile et j'ai donc pensé à l'afficher pour ma future référence également.

Ses des liens 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
}
4
Prajosh Premdas

Il s'agit essentiellement de la protection d'accès du public et des membres protégés de la classe de base dans la classe dérivée. Avec l'héritage public, la classe dérivée peut voir les membres publics et protégés de la base. Avec l'héritage privé, ça ne peut pas. Avec protected, la classe dérivée et toutes les classes dérivées de celle-ci peuvent les voir.

3
Dan Olson