Non-static data members
Nicht-statische Datenelemente werden in einer Member-Spezifikation einer Klasse deklariert.
class S { int n; // nicht-statisches Datenelement int& r; // nicht-statisches Datenelement vom Referenztyp int a[2] = {1, 2}; // nicht-statisches Datenelement mit Standard-Member-Initialisierer (C++11) std::string s, *ps; // zwei nicht-statische Datenelemente struct NestedS { std::string s; } d5; // nicht-statisches Datenelement vom geschachtelten Typ char bit : 2; // zweibit-Bitfeld };
Jede einfache Deklaration ist erlaubt, außer
|
(seit C++11) |
-
unvollständige Typen
,
abstrakte Klassentypen
und Arrays davon sind nicht erlaubt: Insbesondere kann eine Klasse
Ckein nicht-statisches Datenelement vom TypChaben, obwohl sie ein nicht-statisches Datenelement vom TypC&(Referenz auf C) oderC*(Zeiger aufC) haben kann; - ein nicht-statisches Datenelement kann nicht denselben Namen wie die Klasse haben, wenn mindestens ein benutzerdeklarierter Konstruktor vorhanden ist;
|
(seit C++11) |
Zusätzlich sind Bitfeld -Deklarationen erlaubt.
Inhaltsverzeichnis |
Layout
Wenn ein Objekt einer Klasse
C
erstellt wird, wird jedes nicht-statische Datenelement vom Nicht-Referenztyp in einem Teil der Objektdarstellung von
C
allokiert. Ob Referenzelemente Speicher belegen, ist implementierungsdefiniert, aber ihre
Speicherdauer
ist dieselbe wie die des Objekts, zu dem sie gehören.
|
Für Nicht- Union -Klassentypen werden nicht-nullgroße (seit C++20) Member ohne Trennung durch einen Zugriffs-Spezifizierer (bis C++11) mit demselben Member-Zugriff (seit C++11) immer so allokiert, dass später deklarierte Member innerhalb eines Klassenobjekts höhere Adressen haben. Member getrennt durch einen Zugriffs-Spezifizierer (bis C++11) mit unterschiedlicher Zugriffssteuerung (seit C++11) werden in nicht spezifizierter Reihenfolge allokiert (der Compiler kann sie zusammen gruppieren). |
(bis C++23) |
|
Für Nicht- Union -Klassentypen werden nicht-nullgroße Member immer so allokiert, dass später deklarierte Member innerhalb eines Klassenobjekts höhere Adressen haben. Beachten Sie, dass die Zugriffssteuerung von Membern weiterhin die Standard-Layout-Eigenschaft beeinflusst (siehe unten). |
(seit C++23) |
Ausrichtungsanforderungen können Auffüllungen zwischen Mitgliedern oder nach dem letzten Mitglied einer Klasse erforderlich machen.
Standard-Layout
|
Eine Klasse wird als Standard-Layout betrachtet und hat die unten beschriebenen Eigenschaften, wenn und nur wenn sie eine POD-Klasse ist. |
(bis C++11) |
|
Eine Klasse, bei der alle nicht-statischen Datenelemente die gleiche Zugriffssteuerung haben und bestimmte andere Bedingungen erfüllt sind, wird als Standard-Layout-Klasse bezeichnet (siehe Standard-Layout-Klasse für die Liste der Anforderungen). |
(seit C++11) |
Die gemeinsame Anfangssequenz von zwei Standard-Layout-Nicht-Union-Klassentypen ist die längste Folge von nicht-statischen Datenelementen und Bitfeldern in Deklarationsreihenfolge, beginnend mit der ersten solchen Entität in jeder der Klassen, so dass
|
(seit C++20) |
- entsprechende Entitäten haben layout-kompatible Typen,
- entsprechende Entitäten haben dieselben Ausrichtungsanforderungen , und
- entweder sind beide Entitäten Bitfelder mit derselben Breite oder keine ist ein Bitfeld.
struct A { int a; char b; }; struct B { const int b1; volatile char b2; }; // Die gemeinsame Anfangssequenz von A und B ist A.a, A.b und B.b1, B.b2 struct C { int c; unsigned : 0; char b; }; // Die gemeinsame Anfangssequenz von A und C ist A.a und C.c struct D { int d; char b : 4; }; // Die gemeinsame Anfangssequenz von A und D ist A.a und D.d struct E { unsigned int e; char b; }; // Die gemeinsame Anfangssequenz von A und E ist leer
Zwei Standard-Layout-Klassen-Typen (non-union) werden als
layout-kompatibel
bezeichnet, wenn sie denselben Typ unter Ignorierung von CV-Qualifizierern haben, falls vorhanden, wenn sie layout-kompatible
Enumerationen
sind (d.h. Enumerationen mit demselben zugrundeliegenden Typ), oder wenn ihre
gemeinsame Anfangssequenz
aus jedem nicht-statischen Datenelement und Bitfeld besteht (im obigen Beispiel sind
A
und
B
layout-kompatibel).
Zwei Standard-Layout-Unions werden als layout-kompatibel bezeichnet, wenn sie die gleiche Anzahl von nicht-statischen Datenelementen haben und entsprechende nicht-statische Datenelemente (in beliebiger Reihenfolge) layout-kompatible Typen aufweisen.
Standard-Layout-Typen haben die folgenden besonderen Eigenschaften:
-
-
In einer Standard-Layout-Union mit einem aktiven Member vom Nicht-Union-Klassentyp
T1ist es erlaubt, ein nicht-statisches Datenelementmeines anderen Union-Members vom Nicht-Union-KlassentypT2zu lesen, sofernmTeil der gemeinsamen Anfangssequenz vonT1undT2ist (außer dass das Lesen eines volatile-Members durch einen nicht-volatile-Glvalue undefiniert ist). -
Ein Zeiger auf ein Objekt vom Standard-Layout-Klassentyp kann mittels
reinterpret_castin einen Zeiger auf sein erstes nicht-statisches Nicht-Bitfeld-Datenelement (falls es nicht-statische Datenelemente hat) oder andernfalls auf eines seiner Basisklassen-Subobjekte (falls vorhanden) umgewandelt werden, und umgekehrt. Mit anderen Worten, Padding vor dem ersten Datenelement eines Standard-Layout-Typs ist nicht erlaubt. Beachten Sie, dass die Strict-Aliasing-Regeln weiterhin auf das Ergebnis einer solchen Umwandlung angewendet werden. - Das Makro offsetof kann verwendet werden, um den Offset eines beliebigen Members vom Anfang einer Standard-Layout-Klasse zu bestimmen.
-
In einer Standard-Layout-Union mit einem aktiven Member vom Nicht-Union-Klassentyp
Member-Initialisierung
Nicht-statische Datenelemente können auf eine von zwei Arten initialisiert werden:
struct S { int n; std::string s; S() : n(7) {} // direct-initializes n, default-initializes s };
|
2)
Durch einen
Default Member Initializer
, welcher eine geschweifte oder Gleichheits-
Initialisierung
ist, die in der Member-Deklaration enthalten ist und verwendet wird, wenn das Member in der Member-Initialisierungsliste eines Konstruktors weggelassen wird.
struct S { int n = 7; std::string s{'a', 'b', 'c'}; S() {} // default member initializer will copy-initialize n, list-initialize s }; Wenn ein Member sowohl einen Default Member Initializer besitzt als auch in der Member-Initialisierungsliste eines Konstruktors erscheint, wird der Default Member Initializer für diesen Konstruktor ignoriert.
Code ausführen
#include <iostream> int x = 0; struct S { int n = ++x; S() {} // uses default member initializer S(int arg) : n(arg) {} // uses member initializer }; int main() { std::cout << x << '\n'; // prints 0 S s1; // default initializer ran std::cout << x << '\n'; // prints 1 S s2(7); // default initializer did not run std::cout << x << '\n'; // prints 1 }
Member vom Array-Typ können ihre Größe nicht aus Member-Initialisierern ableiten: struct X { int a[] = {1, 2, 3}; // error int b[3] = {1, 2, 3}; // OK }; Default Member Initializer dürfen nicht die implizite Definition eines defaulted Default Constructor für die einschließende Klasse oder die Exception Specification dieses Konstruktors verursachen: struct node { node* p = new node; // error: use of implicit or defaulted node::node() }; Referenz-Member können in einem Default Member Initializer nicht an Temporaries gebunden werden (Anmerkung: dieselbe Regel existiert für Member Initializer Lists ): struct A { A() = default; // OK A(int v) : v(v) {} // OK const int& v = 42; // OK }; A a1; // error: ill-formed binding of temporary to reference A a2(1); // OK (default member initializer ignored because v appears in a constructor) // however a2.v is a dangling reference |
(seit C++11) |
|
Wenn ein Referenzmitglied aus seinem Standard-Mitgliedsinitialisierer initialisiert wird (bis C++20) ein Mitglied einen Standard-Mitgliedsinitialisierer hat (seit C++20) und ein potenziell ausgewerteter Teilausdruck davon eine Aggregatinitialisierung ist, die diesen Standard-Mitgliedsinitialisierer verwenden würde, ist das Programm fehlerhaft: struct A; extern A a; struct A { const A& a1{A{a, a}}; // OK const A& a2{A{}}; // Fehler }; A a{a, a}; // OK |
(seit C++17) |
Verwendung
Der Name eines nicht-statischen Datenelements oder einer nicht-statischen Elementfunktion darf nur in den folgenden drei Situationen erscheinen:
this
erlaubt ist (innerhalb von Mitgliedsfunktionskörpern, in Mitgliedsinitialisierungslisten, in den in-Klasse Standard-Mitgliedsinitialisierern).
struct S { int m; int n; int x = m; // OK: implizites this-> erlaubt in Standardinitialisierern (C++11) S(int i) : m(i), n(m) // OK: implizites this-> erlaubt in Mitgliedsinitialisierungslisten { this->f(); // expliziter Mitglied-Zugriffsausdruck f(); // implizites this-> erlaubt in Mitgliedsfunktionskörpern } void f(); };
struct S { int m; void f(); }; int S::*p = &S::m; // OK: Verwendung von m zur Bildung eines Zeigers auf Mitglied void (S::*fp)() = &S::f; // OK: Verwendung von f zur Bildung eines Zeigers auf Mitglied
struct S { int m; static const std::size_t sz = sizeof m; // OK: m in unevaluated operand }; std::size_t j = sizeof(S::m + 42); // OK: even though there is no "this" object for m
Hinweise
| Feature-Test-Makro | Wert | Std | Feature |
|---|---|---|---|
__cpp_nsdmi
|
200809L
|
(C++11) | Initialisierer für nicht-statische Datenelemente |
__cpp_aggregate_nsdmi
|
201304L
|
(C++14) | Aggregatklassen mit Standard-Member-Initialisierern |
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 80 | C++98 |
alle Datenelemente dürfen nicht denselben Namen
wie der Klassenname haben (bricht C-Kompatibilität) |
nicht-statische Datenelemente erlauben,
den Klassennamen zu teilen, wenn kein benutzerdefinierter Konstruktor vorhanden ist |
| CWG 190 | C++98 |
bei der Bestimmung der Layout-Kompatibilität
wurden alle Mitglieder berücksichtigt |
nur nicht-statische
Datenelemente berücksichtigen |
| CWG 613 | C++98 | nicht ausgewertete Verwendungen nicht-statischer Datenelemente nicht erlaubt | solche Verwendungen sind erlaubt |
| CWG 645 | C++98 |
es war nicht spezifiziert, ob Bitfeld- und
Nicht-Bitfeld-Mitglieder layout-kompatibel sind |
nicht layout-kompatibel |
| CWG 1397 | C++11 |
Klasse wurde in den Standard-Mitgliedsinitialisierern
als vollständig betrachtet |
Standard-Mitgliedsinitialisierung kann keine
Definition des Standardkonstruktors auslösen |
| CWG 1425 | C++98 |
es war unklar, ob ein Standard-Layout-Objekt
dieselbe Adresse mit dem ersten nicht-statischen Datenelement oder dem ersten Basisklassen-Subobjekt teilt |
nicht-statisches Datenelement
falls vorhanden, andernfalls Basis- klassen-Subobjekt falls vorhanden |
| CWG 1696 | C++98 |
Referenzmitglieder konnten auf Temporärwerte initialisiert werden
(deren Lebensdauer am Ende des Konstruktors enden würde) |
solche Initialisierung ist fehlerhaft |
| CWG 1719 | C++98 | unterschiedlich cv-qualifizierte Typen waren nicht layout-kompatibel | cv-Qualifizierer ignoriert, Spezifikation verbessert |
| CWG 2254 | C++11 |
Zeiger auf Standard-Layout-Klasse ohne Datenelemente
kann per reinterpret_cast auf ihre erste Basisklasse umgewandelt werden |
kann per reinterpret_cast
auf jede ihrer Basisklassen umgewandelt werden |
| CWG 2583 | C++11 |
gemeinsame Anfangssequenz berücksichtigte keine
Ausrichtungsanforderungen |
berücksichtigt |
| CWG 2759 | C++20 |
gemeinsame Anfangssequenz könnte
Mitglieder enthalten, die mit
[[
no_unique_address
]]
deklariert sind
|
sie sind nicht enthalten |
Siehe auch
| Klassen | |
| Statische Elemente | |
| Nicht-statische Elementfunktionen | |
|
(C++11)
|
prüft, ob ein Typ ein
Standard-Layout
Typ ist
(Klassentemplate) |
|
Byte-Offset vom Anfang eines
Standard-Layout
Typs zum angegebenen Element
(Funktionsmakro) |
|