Namespaces
Variants

Non-static data members

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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

  • thread_local Speicherklassenspezifizierer ist nicht erlaubt (jedoch ist er für static Datenelemente erlaubt);
(seit C++11)
  • unvollständige Typen , abstrakte Klassentypen und Arrays davon sind nicht erlaubt: Insbesondere kann eine Klasse C kein nicht-statisches Datenelement vom Typ C haben, obwohl sie ein nicht-statisches Datenelement vom Typ C& (Referenz auf C) oder C* (Zeiger auf C ) 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

  • falls __has_cpp_attribute ( no_unique_address ) nicht 0 ist, wird keine der Entitäten mit dem [[ no_unique_address ]] -Attribut deklariert,
(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 T1 ist es erlaubt, ein nicht-statisches Datenelement m eines anderen Union-Members vom Nicht-Union-Klassentyp T2 zu lesen, sofern m Teil der gemeinsamen Anfangssequenz von T1 und T2 ist (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_cast in 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.

Member-Initialisierung

Nicht-statische Datenelemente können auf eine von zwei Arten initialisiert werden:

1) Im Member-Initialisierungsliste des Konstruktors.
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.

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

Default Member Initializer sind für Bit-Field Member nicht erlaubt.

(bis C++20)

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:

1) Als Teil eines Klassenmitglied-Zugriffsausdrucks, in dem die Klasse entweder dieses Mitglied besitzt oder von einer Klasse abgeleitet ist, die dieses Mitglied besitzt, einschließlich der impliziten this - > Mitglied-Zugriffsausdrücke, die erscheinen, wenn ein nicht-statischer Mitgliedsname in einem der Kontexte verwendet wird, in denen 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();
};
2) Um einen Zeiger auf nicht-statisches Mitglied zu bilden.
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
3) (nur für Datenelemente, nicht für Elementfunktionen) Wenn verwendet in nicht ausgewerteten Operanden .
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
Hinweis: Solche Verwendungen sind durch die Lösung von CWG issue 613 in N2253 erlaubt, was von einigen Compilern (z.B. clang) als Änderung in C++11 behandelt wird.

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
prüft, ob ein Typ ein Standard-Layout Typ ist
(Klassentemplate)
Byte-Offset vom Anfang eines Standard-Layout Typs zum angegebenen Element
(Funktionsmakro)