Access specifiers
Legt in einer member-specification einer class/struct oder union die Zugänglichkeit nachfolgender Member fest.
In einem base-specifier einer derived class -Deklaration definieren Sie die Zugänglichkeit geerbter Member der nachfolgenden Basisklasse.
Inhaltsverzeichnis |
Syntax
public
:
Mitgliederdeklarationen
|
(1) | ||||||||
protected
:
Mitgliederdeklarationen
|
(2) | ||||||||
private
:
Mitgliederdeklarationen
|
(3) | ||||||||
| public Basisklasse | (4) | ||||||||
| protected Basisklasse | (5) | ||||||||
| private Basisklasse | (6) | ||||||||
Die privaten Member der Basisklasse sind für die abgeleitete Klasse stets unzugänglich, unabhängig von public-, protected- oder private-Vererbung.
Erklärung
Der Name jedes class -Mitglieds (static, non-static, function, type, etc.) hat einen zugehörigen "Mitgliedszugriff". Wenn der Name des Mitglieds irgendwo in einem Programm verwendet wird, wird sein Zugriff überprüft, und wenn er die Zugriffsregeln nicht erfüllt, wird das Programm nicht kompiliert:
#include <iostream> class Example { public: // alle Deklarationen ab diesem Punkt sind public void add(int x) // Member "add" hat public access { n += x; // OK: private Example::n kann von Example::add aus zugegriffen werden } private: // alle Deklarationen ab diesem Punkt sind private int n = 0; // Member "n" hat private access }; int main() { Example e; e.add(1); // OK: public Example::add kann von main aus zugegriffen werden // e.n = 7; // Fehler: private Example::n kann nicht von main aus zugegriffen werden }
Zugriffsspezifizierer geben dem Autor der Klasse die Möglichkeit zu entscheiden, welche Klassenmitglieder für die Benutzer der Klasse zugänglich sind (das heißt, die Schnittstelle ) und welche Mitglieder für den internen Gebrauch der Klasse bestimmt sind (die Implementierung ).
Im Detail
Alle Mitglieder einer Klasse (Rumpf von member functions , Initialisierer von Member-Objekten und die gesamten nested class definitions ) haben Zugriff auf alle Namen, auf die die Klasse zugreifen kann. Eine lokale Klasse innerhalb einer Member-Funktion hat Zugriff auf alle Namen, auf die die Member-Funktion zugreifen kann.
Eine Klasse, die mit dem Schlüsselwort
class
definiert wird, hat standardmäßig privaten Zugriff für ihre Mitglieder und ihre Basisklassen. Eine Klasse, die mit dem Schlüsselwort
struct
definiert wird, hat standardmäßig öffentlichen Zugriff für ihre Mitglieder und ihre Basisklassen. Eine
Union
hat standardmäßig öffentlichen Zugriff für ihre Mitglieder.
Um zusätzlichen Funktionen oder Klassen Zugriff auf geschützte oder private Member zu gewähren, kann eine Friend-Deklaration verwendet werden.
Zugänglichkeit gilt für alle Namen unabhängig von ihrem Ursprung, daher wird ein durch eine typedef oder using declarations (außer vererbte Konstruktoren) eingeführter Name geprüft, nicht der Name, auf den er verweist:
class A : X { class B {}; // B ist privat in A public: typedef B BB; // BB ist öffentlich }; void f() { A::B y; // Fehler: A::B ist privat A::BB x; // OK: A::BB ist öffentlich }
Der Zugriff auf Mitglieder beeinflusst nicht die Sichtbarkeit: Namen von privaten und privat geerbten Mitgliedern sind sichtbar und werden bei der Überladungsauflösung berücksichtigt, implizite Konvertierungen zu unzugänglichen Basisklassen werden weiterhin berücksichtigt usw. Die Überprüfung des Mitgliederzugriffs ist der letzte Schritt nach der Interpretation eines beliebigen Sprachkonstrukts. Die Absicht dieser Regel ist, dass das Ersetzen von
private
durch
public
niemals das Verhalten des Programms verändert.
Die Zugriffsprüfung für die Namen, die in default function arguments sowie in den default template parameters verwendet werden, erfolgt am Punkt der Deklaration, nicht am Punkt der Verwendung.
Zugriffsregeln für die Namen von virtual functions werden am Aufrufpunkt unter Verwendung des Typs des Ausdrucks überprüft, der zur Bezeichnung des Objekts verwendet wird, für das die Memberfunktion aufgerufen wird. Der Zugriff des final overrider wird ignoriert:
struct B { virtual int f(); // f ist public in B }; class D : public B { private: int f(); // f ist private in D }; void f() { D d; B& b = d; b.f(); // OK: B::f ist public, D::f wird aufgerufen obwohl es private ist d.f(); // Fehler: D::f ist private }
Ein Name, der gemäß unqualifizierter Namenssuche privat ist, kann durch qualifizierte Namenssuche zugänglich sein:
class A {}; class B : private A {}; class C : public B { A* p; // Fehler: Unqualifizierte Namenssuche findet A als private Basisklasse von B ::A* q; // OK: Qualifizierte Namenssuche findet die Deklaration auf Namespace-Ebene };
Ein Name, der über mehrere Pfade im Vererbungsgraph zugänglich ist, hat den Zugriff des Pfades mit dem größten Zugriff:
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 ist für C über B zugänglich } };
Jede beliebige Anzahl von Zugriffsspezifizierern kann in einer Klasse in beliebiger Reihenfolge erscheinen.
|
Zugriffsspezifizierer können das Klassenlayout beeinflussen: Die Adressen nicht-statischer Datenelemente sind nur garantiert in der Reihenfolge ihrer Deklaration aufsteigend für Elemente ohne trennenden Zugriffsspezifizierer (bis C++11) mit gleichem Zugriff (seit C++11) . |
(bis C++23) |
|
Für Standard-Layout-Typen müssen alle nicht-statischen Datenelemente den gleichen Zugriff haben. |
(seit C++11) |
Wenn ein Member innerhalb derselben Klasse erneut deklariert wird, muss dies unter demselben Memberzugriff erfolgen:
struct S { class A; // S::A ist öffentlich private: class A {}; // Fehler: Zugriff kann nicht geändert werden };
Öffentlicher Member-Zugriff
Öffentliche Member bilden einen Teil der öffentlichen Schnittstelle einer Klasse (andere Teile der öffentlichen Schnittstelle sind die Nicht-Member-Funktionen, die durch ADL gefunden werden).
Ein öffentliches Mitglied einer Klasse ist überall zugänglich:
class S { public: // n, E, A, B, C, U, f sind öffentliche Member int n; enum E {A, B, C}; struct U {}; static void f() {} }; int main() { S::f(); // S::f ist in main zugreifbar S s; s.n = S::B; // S::n und S::B sind in main zugreifbar S::U x; // S::U ist in main zugreifbar }
Geschützter Member-Zugriff
Geschützte Mitglieder bilden die Schnittstelle einer Klasse zu ihren abgeleiteten Klassen (was sich von der öffentlichen Schnittstelle der Klasse unterscheidet).
Ein geschütztes Mitglied einer Klasse ist nur zugänglich
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) // Memberfunktion einer abgeleiteten Klasse { ++d.i; // OK: Der Typ von d ist Derived ++i; // OK: Der Typ des impliziten '*this' ist Derived // ++b.i; // Fehler: Kann nicht auf ein geschütztes Mitglied über // Base zugreifen (ansonsten wäre es möglich, die // Implementierung anderer abgeleiteter Klassen, // wie z.B. einer hypothetischen Derived2, zu ändern) } }; void Base::g(Base& b, Derived& d) // Memberfunktion von Base { ++i; // OK ++b.i; // OK ++d.i; // OK } void h(Base& b, Derived& d) // Friend von Derived { ++d.i; // OK: Friend von Derived kann auf ein geschütztes // Mitglied über ein Objekt von Derived zugreifen // ++b.i; // Fehler: Friend von Derived ist kein Friend von Base } void x(Base& b, Derived& d) // Nicht-Member, Nicht-Friend { // ++b.i; // Fehler: Kein Zugriff von Nicht-Member // ++d.i; // Fehler: Kein Zugriff von Nicht-Member }
Wenn ein Zeiger auf ein geschütztes Mitglied gebildet wird, muss in seiner Deklaration eine abgeleitete Klasse verwendet werden:
struct Base { protected: int i; }; struct Derived : Base { void f() { // int Base::* ptr = &Base::i; // Fehler: muss mit Derived benannt werden int Base::* ptr = &Derived::i; // OK } };
Zugriff auf private Member
Private-Member bilden die Implementierung einer Klasse, sowie die private Schnittstelle für die anderen Member der Klasse.
Ein privates Mitglied einer Klasse ist nur für die Mitglieder und Freunde dieser Klasse zugänglich, unabhängig davon, ob sich die Mitglieder in derselben oder in verschiedenen Instanzen befinden:
class S { private: int n; // S::n ist privat public: S() : n(10) {} // this->n ist in S::S zugreifbar S(const S& other) : n(other.n) {} // other.n ist in S::S zugreifbar };
Der explizite Cast (C-Stil und Funktions-Stil) erlaubt das Casten von einem abgeleiteten Lvalue zu einer Referenz auf seine private Basis, oder von einem Zeiger auf Abgeleitetes zu einem Zeiger auf seine private Basis.
Vererbung
Siehe abgeleitete Klassen für die Bedeutung von public-, protected- und private-Vererbung.
Schlüsselwörter
Fehlerberichte
Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | Angewendet auf | Verhalten wie veröffentlicht | Korrektes Verhalten |
|---|---|---|---|
| CWG 1873 | C++98 | protected-Member waren für Friends abgeleiteter Klassen zugänglich | unzugänglich gemacht |