Access specifiers
Dans une spécification-de-membre d'une classe/structure ou d'une union , définit l'accessibilité des membres suivants.
Dans un spécificateur-de-base d'une déclaration de classe dérivée , définissez l'accessibilité des membres hérités de la classe de base suivante.
Table des matières |
Syntaxe
public
:
déclarations-de-membres
|
(1) | ||||||||
protected
:
déclarations-de-membres
|
(2) | ||||||||
private
:
déclarations-de-membres
|
(3) | ||||||||
| public classe-de-base | (4) | ||||||||
| protected classe-de-base | (5) | ||||||||
| private classe-de-base | (6) | ||||||||
Les membres privés de la classe de base sont toujours inaccessibles à la classe dérivée, qu'il s'agisse d'un héritage public, protégé ou privé.
Explication
Le nom de chaque membre de classe (statique, non-statique, fonction, type, etc.) possède un « accès membre » associé. Lorsqu'un nom de membre est utilisé n'importe où dans un programme, son accès est vérifié, et s'il ne satisfait pas aux règles d'accès, le programme ne compile pas :
#include <iostream> class Example { public: // toutes les déclarations après ce point sont publiques void add(int x) // la fonction membre "add" a un accès public { n += x; // OK : Example::n privé peut être accédé depuis Example::add } private: // toutes les déclarations après ce point sont privées int n = 0; // le membre "n" a un accès privé }; int main() { Example e; e.add(1); // OK : Example::add public peut être accédé depuis main // e.n = 7; // erreur : Example::n privé ne peut pas être accédé depuis main }
Les spécificateurs d'accès donnent à l'auteur de la classe la capacité de décider quels membres de la classe sont accessibles aux utilisateurs de la classe (c'est-à-dire l' interface ) et quels membres sont destinés à l'usage interne de la classe (l' implémentation ).
En détail
Tous les membres d'une classe (corps des fonctions membres , initialiseurs des objets membres, et l'ensemble des définitions de classes imbriquées ) ont accès à tous les noms auxquels la classe peut accéder. Une classe locale au sein d'une fonction membre a accès à tous les noms auxquels la fonction membre peut accéder.
Une classe définie avec le mot-clé
class
a un accès privé pour ses membres et ses classes de base par défaut. Une classe définie avec le mot-clé
struct
a un accès public pour ses membres et ses classes de base par défaut. Une
union
a un accès public pour ses membres par défaut.
Pour accorder l'accès à des fonctions ou classes supplémentaires aux membres protégés ou privés, une déclaration d'amitié peut être utilisée.
L'accessibilité s'applique à tous les noms sans considération de leur origine, donc un nom introduit par une typedef ou using declarations (sauf les constructeurs hérités) est vérifié, et non le nom auquel il fait référence :
class A : X { class B {}; // B est privé dans A public: typedef B BB; // BB est public }; void f() { A::B y; // erreur : A::B est privé A::BB x; // OK : A::BB est public }
L'accès aux membres n'affecte pas la visibilité : les noms des membres privés et hérités privés sont visibles et pris en compte par la résolution de surcharge, les conversions implicites vers les classes de base inaccessibles sont toujours considérées, etc. La vérification de l'accès aux membres est la dernière étape après l'interprétation de toute construction linguistique donnée. L'intention de cette règle est que remplacer tout
private
par
public
n'altère jamais le comportement du programme.
La vérification d'accès pour les noms utilisés dans les arguments par défaut des fonctions ainsi que dans les paramètres de template par défaut est effectuée au point de déclaration, et non au point d'utilisation.
Les règles d'accès pour les noms des fonctions virtuelles sont vérifiées au point d'appel en utilisant le type de l'expression utilisée pour désigner l'objet pour lequel la fonction membre est appelée. L'accès du final overrider est ignoré :
struct B { virtual int f(); // f est public dans B }; class D : public B { private: int f(); // f est privé dans D }; void f() { D d; B& b = d; b.f(); // OK : B::f est public, D::f est invoqué même s'il est privé d.f(); // erreur : D::f est privé }
Un nom qui est privé selon la recherche de nom non qualifiée , peut être accessible via la recherche de nom qualifiée :
class A {}; class B : private A {}; class C : public B { A* p; // erreur : la recherche de nom non qualifié trouve A comme base privée de B ::A* q; // OK : la recherche de nom qualifié trouve la déclaration au niveau du namespace };
Un nom accessible par plusieurs chemins dans le graphe d'héritage possède l'accessibilité du chemin le plus accessible :
class W { public: void f(); }; class A : private virtual W {}; class B : public virtual W {}; class C : public A, public B { void f() { W::f(); // OK : W est accessible à C via B } };
N'importe quel nombre de spécificateurs d'accès peut apparaître dans une classe, dans n'importe quel ordre.
|
Les spécificateurs d'accès des membres peuvent affecter la disposition de la classe : les adresses des membres de données non statiques sont uniquement garanties d'augmenter dans l'ordre de déclaration pour les membres non séparés par un spécificateur d'accès (jusqu'à C++11) avec le même accès (depuis C++11) . |
(jusqu'à C++23) |
|
Pour les types à disposition standard , tous les membres de données non statiques doivent avoir le même accès. |
(depuis C++11) |
Lorsqu'un membre est redéclaré dans la même classe, il doit le faire avec le même accès membre :
struct S { class A; // S::A est public private: class A {}; // erreur : impossible de modifier l'accès };
Accès public aux membres
Les membres publics constituent une partie de l'interface publique d'une classe (les autres parties de l'interface publique sont les fonctions non-membres trouvées par ADL ).
Un membre public d'une classe est accessible partout :
class S { public: // n, E, A, B, C, U, f sont des membres publics int n; enum E {A, B, C}; struct U {}; static void f() {} }; int main() { S::f(); // S::f est accessible dans main S s; s.n = S::B; // S::n et S::B sont accessibles dans main S::U x; // S::U est accessible dans main }
Accès aux membres protégés
Les membres protégés constituent l'interface d'une classe envers ses classes dérivées (ce qui est distinct de l'interface publique de la classe).
Un membre protégé d'une classe est uniquement accessible
struct Base { protected: int i; private: void g(Base& b, struct Derived& d); }; struct Derived : Base { friend void h(Base& b, Derived& d); void f(Base& b, Derived& d) // fonction membre d'une classe dérivée { ++d.i; // OK : le type de d est Derived ++i; // OK : le type du '*this' implicite est Derived // ++b.i; // erreur : impossible d'accéder à un membre protégé via // Base (sinon il serait possible de modifier // d'autres classes dérivées, comme un hypothétique // Derived2, implémentation de base) } }; void Base::g(Base& b, Derived& d) // fonction membre de Base { ++i; // OK ++b.i; // OK ++d.i; // OK } void h(Base& b, Derived& d) // Ami de Derived { ++d.i; // OK : l'ami de Derived peut accéder à un membre // protégé via un objet de Derived // ++b.i; // erreur : l'ami de Derived n'est pas ami de Base } void x(Base& b, Derived& d) // non-membre non-ami { // ++b.i; // erreur : aucun accès depuis un non-membre // ++d.i; // erreur : aucun accès depuis un non-membre }
Lorsqu'un pointeur vers un membre protégé est formé, il doit utiliser une classe dérivée dans sa déclaration :
struct Base { protected: int i; }; struct Derived : Base { void f() { // int Base::* ptr = &Base::i; // erreur : doit être nommé en utilisant Derived int Base::* ptr = &Derived::i; // OK } };
Accès aux membres privés
Les membres privés constituent l'implémentation d'une classe, ainsi que l'interface privée pour les autres membres de la classe.
Un membre privé d'une classe est uniquement accessible aux membres et amis de cette classe, indépendamment du fait que les membres se trouvent sur la même instance ou sur des instances différentes :
class S { private: int n; // S::n est privé public: S() : n(10) {} // this->n est accessible dans S::S S(const S& other) : n(other.n) {} // other.n est accessible dans S::S };
La conversion explicite (style C et style fonction) permet de convertir un lvalue dérivé en référence vers sa base privée, ou un pointeur vers un dérivé en pointeur vers sa base privée.
Héritage
Voir les classes dérivées pour la signification de l'héritage public, protected et private.
Mots-clés
Rapports de défauts
Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.
| DR | Appliqué à | Comportement tel que publié | Comportement correct |
|---|---|---|---|
| CWG 1873 | C++98 | les membres protégés étaient accessibles aux amis des classes dérivées | rendus inaccessibles |