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

1014

Quelle est la différence entre public, privateet l' protectedhéritage en C ++?

Toutes les questions que j'ai trouvées sur SO concernent des cas spécifiques.

Arsen Khachaturyan
la source

Réponses:

1065

Pour répondre à cette question, j'aimerais d'abord décrire les accesseurs des membres dans mes propres mots. Si vous le savez déjà, passez à la rubrique "suivant:".

Il y a trois accesseurs que je connais: public, protectedet private.

Laisser:

class Base {
    public:
        int publicMember;
    protected:
        int protectedMember;
    private:
        int privateMember;
};
  • Tout ce qui est conscient Baseest également conscient de ce qui Basecontient publicMember.
  • Seuls les enfants (et leurs enfants) savent que Basecontient protectedMember.
  • Personne mais Basen'est au courant privateMember.

Par "est au courant de", je veux dire "reconnaître l'existence de, et donc pouvoir y accéder".

suivant:

La même chose se produit avec l'héritage public, privé et protégé. Prenons une classe Baseet une classe Childqui hérite de Base.

  • Si l'héritage est public, tout ce qui est conscient Baseet qui Childest également conscient de ce qui Childhérite Base.
  • Si l'héritage est protected, seulement Child, et ses enfants, sont conscients qu'ils héritent de Base.
  • Si l'héritage est private, personne d'autre que lui Childn'est au courant de l'héritage.
Anzurio
la source
183
Je voudrais ajouter quelques mots selon lesquels la visibilité en C ++ est basée sur la classe plutôt que sur l'objet, ce qui signifie que les objets de la même classe peuvent accéder mutuellement aux champs privés sans restriction.
Zhe Chen
48
Si vous avez du mal à comprendre cela, lisez la réponse de Kirill V. Lyadvinsky, puis revenez et lisez ceci.
The Vivandiere
6
Ceci est juste un autre cas qui illustre comment, pour la plupart, hériter de SomeBaseest comme une façon codée en dur de composer un membre anonyme de type SomeBase. Celui-ci, comme tout autre membre, possède un spécificateur d'accès, qui exerce le même contrôle sur l'accès externe.
underscore_d
1
@ZheChen si j'ai des objets Tom et Jerry de la classe Personne avec l'âge du champ privé comment accéder (et modifier?) L'âge de Jerry en utilisant Tom?
gen
2
Pourriez-vous illustrer ce que vous entendez par «conscient de l'héritage»? Je peux comprendre "je peux accéder à ceci je ne peux pas accéder à cela" mais je ne comprends pas quand on dit "je sais que A hérite de B" que fais-je ici est-ce que je vérifie l'héritage?
neilxdims
1461
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 de l'héritage protégé et privé, vous pouvez lire ici .

Kirill V. Lyadvinsky
la source
35
Ce qu'Anzurio a écrit n'a cliqué qu'en conjonction avec votre réponse immédiatement ci-dessous. Плус 1.
Iwillnotexist Idonotexist
2
Ma compréhension de la façon dont cela fonctionnait était TELLEMENT LOIN! Merci beaucoup d'avoir clarifié.
tjwrona1992
il m'a fallu un certain temps pour comprendre cela. Mais maintenant, c'est clair. Merci!
Chan Kim
115

Limiter la visibilité de l'héritage rendra le code incapable de voir que certaines classes héritent d'une autre classe: les conversions implicites du dérivé vers la base ne fonctionneront pas, et static_castde la base vers le dérivé ne fonctionneront 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é.

héritage public

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

héritage protégé

  1. Protégé mis en œuvre en termes de. Rarement utile. Utilisé boost::compressed_pairpour dériver de classes vides et économiser de la mémoire en utilisant l'optimisation de classe de base vide (l'exemple ci-dessous n'utilise pas de modèle pour rester au 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!
      }
    };

héritage privé

  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 est importante (les traits vides qui ne contiennent que des fonctions utiliseront l'optimisation de classe de base vide). Cependant, le confinement est souvent la meilleure solution. La taille des chaînes est critique, c'est donc une utilisation souvent vue ici

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

membre public

  1. Agrégat

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

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

membre protégé

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

membre privé

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

    class window {
    private:
      int width;
    };

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

Johannes Schaub - litb
la source
7
Je pense que Scott Myers (autant que j'aime ses trucs) a beaucoup à répondre de la confusion générale. Je pense maintenant que ses analogies entre IS-A et IS-IMPLEMENTED-IN-TERM-OF sont suffisantes pour ce qui se passe.
DangerMouse
65

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

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

entrez la description de l'image ici

Le tableau ci-dessus est interprété de la manière suivante (jetez un œil à la première ligne):

si un composant est déclarée comme publique et sa classe est héritée comme publique résultant accès est publique .

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, ren classe sous - sous est pas .

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, zdans la classe Sub est protégé et pour la variable xest aucun .

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

Permet maintenant de définir 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 classe nommée Superou cette Subclasse est dérivée de la Superclasse. La Subclasse n'introduit ni nouvelles variables ni nouvelles fonctions. Cela signifie-t-il qu'un objet de la Subclasse hérite de tous les traits après que la Superclasse soit en fait une copie des Superobjets d'une classe?

Non . Ce n'est pas le cas.

Si nous compilons le code suivant, nous n'obtiendrons que des erreurs de compilation disant que putet les getméthodes sont inaccessibles. Pourquoi?

Lorsque nous omettons le spécificateur de visibilité, le compilateur suppose que nous allons appliquer l' héritage dit privé . Cela signifie que tous les publics les composants se transforment en privé l' accès, les composants superclasse privés ne seront pas accessibles à tous. 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 voulons conserver la politique d'accès utilisée précédemment.

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 d'une manière quelque peu magique. Les composants privés resteront privés , publics restera public .

Les objets de la Subclasse peuvent faire «presque» les mêmes choses que leurs frères et sœurs plus âgés créés à partir de la Superclasse. "Presque" car le fait d'être une sous-classe signifie également que la classe a perdu l'accès aux composants privés de la superclasse . Nous ne pouvons pas écrire une fonction membre de la Subclasse qui serait capable de manipuler directement la variable de stockage.

Il s'agit d'une restriction très grave. Y a-t-il une solution de contournement?

Oui .

Le troisième niveau d'accès est appelé protégé . Le mot-clé protected signifie que le composant marqué avec lui se comporte comme un public lorsqu'il est utilisé par l'une des sous-classes et ressemble à un privé pour le reste du monde . - Cela 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 une nouvelle fonctionnalité pour la Subclasse et cela fait une chose importante: il accède à la variable de stockage de 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 la sortie suivante:

storage = 101
BugShotGG
la source
4
Premier à mentionner l'absence d'un modificateur (comme dans la classe: SuperClass) donne des rendements privés. Il s'agit d'un élément important que les autres manquent, ainsi que des explications approfondies. +1
Eau
2
Overkill IMO, mais j'aime la table au début.
cp.engr
63

Cela concerne la façon 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 publics de la classe de base seront protégés
  • privé -> les membres publics de la classe de base seront privés

Comme litb le fait remarquer, l'héritage public est un héritage traditionnel que vous verrez dans la plupart des langages de programmation. C'est-à-dire qu'elle modélise une relation "IS-A". L'héritage privé, quelque chose d'AFAIK propre au C ++, est une relation "MISE EN ŒUVRE EN TERMES DE". Autrement dit, vous souhaitez utiliser l'interface publique dans la classe dérivée, mais vous ne voulez 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 en tant que base privée, faire un membre de dérivé afin de réutiliser la fonctionnalité de la classe de base.

Doug T.
la source
13
Mieux vaut dire "public: l'héritage sera vu par tout le monde". protégé: l'héritage ne sera vu que par les classes dérivées et les amis "," privé: l'héritage ne sera vu que par la classe elle-même et les amis ". Ceci est différent de votre formulation, car non seulement les membres peuvent être invisibles, mais aussi la relation IS-A peut être invisible
Johannes Schaub - litb
4
La seule fois où j'ai utilisé l'héritage privé était de faire exactement ce que Doug T décrit, c'est-à-dire "vous voulez utiliser l'interface publique dans la classe dérivée, mais vous ne voulez pas que l'utilisateur de la classe dérivée ait accès à cette interface". Je l'ai essentiellement utilisé pour sceller l'ancienne interface et en exposer une autre via la classe dérivée.
Rich
36
Member in base class : Private   Protected   Public   

Type d'héritage :              objet hérité comme :

Private            :   Inaccessible   Private     Private   
Protected          :   Inaccessible   Protected   Protected  
Public             :   Inaccessible   Protected   Public
kinshuk4
la source
23
C'est trompeur. Les membres privés d'une classe de base se comportent très différemment des membres ordinaires d'une classe privée - ils ne sont pas du tout accessibles à partir de la classe dérivée. Je pense que votre colonne de trois "Privé" devrait être une colonne "Inaccessible". Voir la réponse de Kirill V. Lyadvinsky à cette question.
Sam Kauffman
27

1) Héritage public :

une. Les membres privés de la classe Base ne sont pas accessibles dans la classe Derived.

b. Les membres protégés de la classe Base restent protégés dans la classe Derived.

c. Les membres publics de la classe Base restent publics dans la classe Derived.

Ainsi, d'autres classes peuvent utiliser des membres publics de la classe Base via l'objet de classe Derived.

2) Héritage protégé :

une. Les membres privés de la classe Base ne sont pas accessibles dans la classe Derived.

b. Les membres protégés de la classe Base restent protégés dans la classe Derived.

c. Les membres publics de la classe Base deviennent également des membres protégés de la classe Derived.

Ainsi, les autres classes ne peuvent pas utiliser les membres publics de la classe Base via l'objet de classe Derived; mais ils sont disponibles pour la sous-classe de Derived.

3) Héritage privé :

une. Les membres privés de la classe Base ne sont pas accessibles dans la classe Derived.

b. Les membres protégés et publics de la classe Base deviennent des membres privés de la classe Derived.

Ainsi, aucun membre de la classe Base n'est accessible aux autres classes via l'objet de classe Derived car ils sont privés dans la classe Derived. Ainsi, même la sous-classe de la classe Derived ne peut pas y accéder.

yuvi
la source
20

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

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

tout D est un B .

L'héritage privé modélise une relation IS-IMPLEMENTED-USING (ou tout autre nom). Avec

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

a Dn'est pas a B, mais chaque Dutilise son Bdans sa mise en œuvre. L'héritage privé peut toujours être éliminé en utilisant le confinement à la place:

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

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

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

sbi
la source
Certains disent que c'est une relation. Comme utiliser une chaise comme un marteau. Chaise Here: marteau protégé
user4951
lorsque l'utilisation de confinement au lieu de l'héritage privé n'est pas aussi pratique que l'héritage privé? Pourriez-vous l'expliquer à l'aide d'un exemple?
Destructor
@Pravasi: Si Ddérive en privé D, il peut remplacer les fonctions virtuelles de B. (Si, par exemple, Best une interface d'observateur, alors Dpourrait l'implémenter et passer thisà des fonctions nécessitant auch une interface, sans que tout le monde puisse l'utiliser Den tant qu'observateur.) En outre, Dpourrait sélectivement rendre les membres de Bdisponibles dans son interface en faisant using B::member. Les deux sont syntaxiquement peu pratiques à implémenter quand Best membre.
sbi
@sbi: ancien mais ... le confinement est un non-go dans le cas de CRTP et / ou de virtuels (comme vous l'avez correctement décrit dans le commentaire - mais cela signifie qu'il ne peut pas être modélisé comme confinement si B a des méthodes abstraites et vous ne sont pas autorisés à le toucher). protectedhéritage que j'ai trouvé utile avec une virtualclasse de base et 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

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 n'importe qui via un pointeur de classe de base.

Si vous héritez de manière protégée, seules vos classes enfants pourront vous utiliser de manière polymorphe.

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

Ce qui symbolise fondamentalement les connaissances que les autres classes ont sur votre relation avec votre classe parent

Arkaitz Jimenez
la source
9

Les membres de données protégés sont accessibles à toutes les classes qui héritent de votre classe. Les membres de données privées, cependant, ne le peuvent pas. Disons que nous avons les éléments suivants:

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

De l'intérieur de votre extension à cette classe, le référencement this.myPrivateMemberne fonctionnera pas. Cependant, this.myProtectedMembersera. La valeur est toujours encapsulée, donc si nous avons une instanciation de cette classe appelée myObj, myObj.myProtectedMembercela ne fonctionnera pas, donc sa fonction est similaire à celle d'un membre de données privées.

Andrew Noyes
la source
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

Sur la base de cet exemple pour java ... je pense qu'une petite table vaut mille mots :)

Enissay
la source
Java n'a qu'un héritage public
Zelldon
Ce n'est pas le sujet pour parler de Java mais NON, vous vous trompez ... Suivez le lien dans ma réponse ci-dessus pour plus de détails
Enissay
Vous avez mentionné Java, c'est donc le sujet. Et votre exemple gère les spécificateurs qui utilisent dans jaca. La question concerne les spécificateurs d'héritage qui n'existent pas en Java et ont fait une différence. Si un champ de la superclasse est public et que l'héritage est privé, le champ n'est accessible qu'à l'intérieur de la sous-classe. À l'extérieur, rien n'indique si la sous-classe prolonge la superclasse. Mais votre tableau explique uniquement les spécificateurs de champ et de méthodes.
Zelldon
7

Sommaire:

  • Privé: personne ne peut le voir sauf dans la classe
  • Protégé: 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 une certaine direction, par exemple de protégé à public.

Roee Adler
la source
6

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 qui sont en dehors de la classe de base et de 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

varun
la source
5

J'ai trouvé une réponse facile et j'ai donc pensé à la poster pour ma future référence aussi.

Son 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
}
Prajosh Premdas
la source
3

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é, ce n'est pas possible. Avec protected, la classe dérivée et toutes les classes dérivées qui peuvent les voir.

Dan Olson
la source