Quelle est la différence entre
public
,private
et l'protected
héritage en C ++?
Toutes les questions que j'ai trouvées sur SO concernent des cas spécifiques.
Quelle est la différence entre
public
,private
et l'protected
héritage en C ++?
Toutes les questions que j'ai trouvées sur SO concernent des cas spécifiques.
Réponses:
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
, protected
et private
.
Laisser:
class Base {
public:
int publicMember;
protected:
int protectedMember;
private:
int privateMember;
};
Base
est également conscient de ce qui Base
contient publicMember
.Base
contient protectedMember
.Base
n'est au courant privateMember
.Par "est au courant de", je veux dire "reconnaître l'existence de, et donc pouvoir y accéder".
La même chose se produit avec l'héritage public, privé et protégé. Prenons une classe Base
et une classe Child
qui hérite de Base
.
public
, tout ce qui est conscient Base
et qui Child
est également conscient de ce qui Child
hérite Base
.protected
, seulement Child
, et ses enfants, sont conscients qu'ils héritent de Base
.private
, personne d'autre que lui Child
n'est au courant de l'héritage.SomeBase
est 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.
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 .
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_cast
de 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
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é
Protégé mis en œuvre en termes de. Rarement utile. Utilisé boost::compressed_pair
pour 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é
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
Agrégat
class pair {
public:
First first;
Second second;
};
Accesseurs
class window {
public:
int getWidth() const;
};
membre protégé
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é
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.
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.
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
, r
en 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
, z
dans la classe Sub est protégé et pour la variable x
est 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 Super
ou cette Sub
classe est dérivée de la Super
classe. La Sub
classe n'introduit ni nouvelles variables ni nouvelles fonctions. Cela signifie-t-il qu'un objet de la Sub
classe hérite de tous les traits après que la Super
classe soit en fait une copie des Super
objets 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 put
et les get
mé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 Sub
classe peuvent faire «presque» les mêmes choses que leurs frères et sœurs plus âgés créés à partir de la Super
classe. "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 Sub
classe 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 Sub
classe 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
Cela concerne la façon dont les membres publics de la classe de base sont exposés à partir de la classe dérivée.
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.
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
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.
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 D
n'est pas a B
, mais chaque D
utilise son B
dans 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 D
aussi 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 protected
modèles d'héritage. Au moins, je n'ai pas encore vu d'explication convaincante.
D
dérive en privé D
, il peut remplacer les fonctions virtuelles de B
. (Si, par exemple, B
est une interface d'observateur, alors D
pourrait l'implémenter et passer this
à des fonctions nécessitant auch une interface, sans que tout le monde puisse l'utiliser D
en tant qu'observateur.) En outre, D
pourrait sélectivement rendre les membres de B
disponibles dans son interface en faisant using B::member
. Les deux sont syntaxiquement peu pratiques à implémenter quand B
est membre.
protected
héritage que j'ai trouvé utile avec une virtual
classe de base et 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) {} };
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
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.myPrivateMember
ne fonctionnera pas. Cependant, this.myProtectedMember
sera. La valeur est toujours encapsulée, donc si nous avons une instanciation de cette classe appelée myObj
, myObj.myProtectedMember
cela ne fonctionnera pas, donc sa fonction est similaire à celle d'un membre de données privées.
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 :)
Sommaire:
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.
Les membres privés d'une classe de base ne sont accessibles qu'aux membres de cette classe de base.
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.
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.
privé : base
protégé : base + dérivé
public : base + dérivé + tout autre membre
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
}
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.