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

Les membres de données non statiques sont déclarés dans une spécification de membre d'une classe.

class S
{
    int n;              // membre de données non statique
    int& r;             // membre de données non statique de type référence
    int a[2] = {1, 2};  // membre de données non statique avec initialiseur de membre par défaut (C++11)
    std::string s, *ps; // deux membres de données non statiques
    struct NestedS
    {
        std::string s;
    } d5;               // membre de données non statique de type imbriqué
    char bit : 2;       // champ de bits de deux bits
};

Toute déclaration simple est autorisée, à l'exception

  • thread_local le spécificateur de classe de stockage n'est pas autorisé (mais il est autorisé pour les static membres de données);
(depuis C++11)
  • types incomplets , types de classes abstraites , et leurs tableaux ne sont pas autorisés : en particulier, une classe C ne peut pas avoir de membre de données non statique de type C , bien qu'elle puisse avoir un membre de données non statique de type C& (référence à C) ou C* (pointeur vers C );
  • un membre de données non statique ne peut pas avoir le même nom que celui de la classe si au moins un constructeur déclaré par l'utilisateur est présent ;
(depuis C++11)

De plus, bit-field les déclarations sont autorisées.

Table des matières

Disposition

Lorsqu'un objet d'une certaine classe C est créé, chaque membre de données non statique de type non-référence est alloué dans une partie de la représentation objet de C . Le fait que les membres référence occupent ou non un stockage est défini par l'implémentation, mais leur durée de stockage est la même que celle de l'objet dont ils sont membres.

Pour les types de classe non- union , de taille non nulle (depuis C++20) les membres non séparés par un spécificateur d'accès (jusqu'à C++11) avec le même accès membre (depuis C++11) sont toujours alloués de sorte que les membres déclarés plus tard aient des adresses plus élevées dans un objet de classe. Les membres séparés par un spécificateur d'accès (jusqu'à C++11) avec un contrôle d'accès différent (depuis C++11) sont alloués dans un ordre non spécifié (le compilateur peut les regrouper ensemble).

(jusqu'à C++23)

Pour les types de classe non- union , les membres de taille non nulle sont toujours alloués de sorte que les membres déclarés plus tard aient des adresses plus élevées dans un objet de classe. Notez que le contrôle d'accès des membres affecte toujours la propriété de disposition standard (voir ci-dessous).

(depuis C++23)

Les exigences d'alignement peuvent nécessiter un remplissage entre les membres, ou après le dernier membre d'une classe.

Disposition standard

Une classe est considérée comme ayant une disposition standard et possède les propriétés décrites ci-dessous si et seulement s'il s'agit d'une classe POD .

(jusqu'à C++11)

Une classe où tous les membres de données non statiques ont le même contrôle d'accès et où certaines autres conditions sont satisfaites est connue sous le nom de classe à disposition standard (voir classe à disposition standard pour la liste des exigences).

(depuis C++11)

La séquence initiale commune de deux types de classes non-union à disposition standard est la plus longue séquence de membres de données non statiques et de champs de bits dans l'ordre de déclaration, commençant par la première entité de ce type dans chacune des classes, telle que

  • si __has_cpp_attribute ( no_unique_address ) n'est pas 0 , aucune entité n'est déclarée avec l'attribut [[ no_unique_address ]] ,
(depuis C++20)
  • les entités correspondantes ont des types compatibles de mise en page,
  • les entités correspondantes ont les mêmes exigences d'alignement , et
  • soit les deux entités sont des champs de bits de même largeur, soit aucune n'est un champ de bits.
struct A { int a; char b; };
struct B { const int b1; volatile char b2; }; 
// La séquence initiale commune de A et B est A.a, A.b et B.b1, B.b2
struct C { int c; unsigned : 0; char b; };
// La séquence initiale commune de A et C est A.a et C.c
struct D { int d; char b : 4; };
// La séquence initiale commune de A et D est A.a et D.d
struct E { unsigned int e; char b; };
// La séquence initiale commune de A et E est vide

Deux types de classes non-union à disposition standard sont dits layout-compatibles s'ils sont du même type en ignorant les qualifications cv, le cas échéant, s'ils sont des énumérations layout-compatibles (c'est-à-dire des énumérations avec le même type sous-jacent), ou si leur séquence initiale commune comprend chaque membre de données non statique et champ de bits (dans l'exemple ci-dessus, A et B sont layout-compatibles).

Deux unions standard-layout sont dites layout-compatible si elles ont le même nombre de membres de données non statiques et que les membres de données non statiques correspondants (dans n'importe quel ordre) ont des types layout-compatibles.

Les types de disposition standard ont les propriétés spéciales suivantes :

  • Dans une union de disposition standard avec un membre actif de type classe non-union T1 , il est permis de lire un membre de données non statique m d'un autre membre de l'union de type classe non-union T2 à condition que m fasse partie de la séquence initiale commune de T1 et T2 (sauf que la lecture d'un membre volatile via une glvalue non volatile est indéfinie).
  • Un pointeur vers un objet de type classe de disposition standard peut être reinterpret_cast en pointeur vers son premier membre de données non statique non-champ de bits (s'il a des membres de données non statiques) ou sinon vers l'un de ses sous-objets de classe de base (s'il en a), et vice versa. En d'autres termes, le remplissage n'est pas autorisé avant le premier membre de données d'un type de disposition standard. Notez que les règles de strict aliasing s'appliquent toujours au résultat d'un tel cast.
  • La macro offsetof peut être utilisée pour déterminer le décalage de n'importe quel membre depuis le début d'une classe de disposition standard.

Initialisation des membres

Les membres de données non statiques peuvent être initialisés de l'une des deux manières suivantes :

1) Dans la liste d'initialisation des membres du constructeur.
struct S
{
    int n;
    std::string s;
    S() : n(7) {} // initialise directement n, initialise par défaut s
};
2) Via un initialiseur de membre par défaut , qui est un initialiseur entre accolades ou avec le signe égal initializer inclus dans la déclaration du membre et utilisé si le membre est omis de la liste d'initialisation des membres d'un constructeur.
struct S
{
    int n = 7;
    std::string s{'a', 'b', 'c'};
    S() {} // default member initializer will copy-initialize n, list-initialize s
};

Si un membre possède un initialiseur par défaut et apparaît également dans la liste d'initialisation des membres d'un constructeur, l'initialiseur de membre par défaut est ignoré pour ce constructeur.

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

Les initialiseurs de membre par défaut ne sont pas autorisés pour les membres bit-field .

(jusqu'en C++20)

Les membres de type tableau ne peuvent pas déduire leur taille à partir des initialiseurs de membre :

struct X
{
    int a[] = {1, 2, 3};  // error
    int b[3] = {1, 2, 3}; // OK
};

Les initialiseurs de membre par défaut ne peuvent pas provoquer la définition implicite d'un constructeur par défaut par défaut pour la classe englobante ou la spécification d'exception de ce constructeur :

struct node
{
    node* p = new node; // error: use of implicit or defaulted node::node() 
};

Les membres référence ne peuvent pas être liés à des temporaires dans un initialiseur de membre par défaut (note : la même règle existe pour les listes d'initialisation des membres ) :

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
(depuis C++11)


Si un membre référence est initialisé à partir de son initialiseur de membre par défaut (jusqu'à C++20) un membre a un initialiseur de membre par défaut (depuis C++20) et qu'une sous-expression potentiellement évaluée de celui-ci est une initialisation d'agrégat qui utiliserait cet initialiseur de membre par défaut, le programme est mal formé :

struct A;
extern A a;
struct A
{
    const A& a1{A{a, a}}; // OK
    const A& a2{A{}};     // erreur
};
A a{a, a};                // OK
(depuis C++17)

Utilisation

Le nom d'un membre de données non statique ou d'une fonction membre non statique ne peut apparaître que dans les trois situations suivantes :

1) Dans le cadre d'une expression d'accès aux membres d'une classe, où la classe possède ce membre ou est dérivée d'une classe qui possède ce membre, y compris les expressions d'accès aux membres this - > implicites qui apparaissent lorsqu'un nom de membre non statique est utilisé dans l'un des contextes où this est autorisé (dans les corps des fonctions membres, dans les listes d'initialisation des membres, dans les initialiseurs par défaut des membres en classe).
struct S
{
    int m;
    int n;
    int x = m;            // OK: implicit this-> allowed in default initializers (C++11)
    S(int i) : m(i), n(m) // OK: implicit this-> allowed in member initializer lists
    {
        this->f();        // explicit member access expression
        f();              // implicit this-> allowed in member function bodies
    }
    void f();
};
2) Pour former un pointeur vers membre non statique .
struct S
{
    int m;
    void f();
};
int S::*p = &S::m;       // OK: use of m to make a pointer to member
void (S::*fp)() = &S::f; // OK: use of f to make a pointer to member
3) (uniquement pour les membres de données, pas les fonctions membres) Lorsqu'il est utilisé dans des opérandes non évalués .
struct S
{
    int m;
    static const std::size_t sz = sizeof m; // OK: m dans un opérande non évalué
};
std::size_t j = sizeof(S::m + 42); // OK: même s'il n'y a pas d'objet "this" pour m
Notes : de telles utilisations sont autorisées via la résolution de CWG issue 613 dans N2253 , qui est traitée comme un changement en C++11 par certains compilateurs (par exemple clang).

Notes

Macro de test de fonctionnalité Valeur Std Fonctionnalité
__cpp_nsdmi 200809L (C++11) Initialiseurs de membres de données non statiques
__cpp_aggregate_nsdmi 201304L (C++14) Classes agrégées avec initialiseurs de membres par défaut

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 publié Comportement corrigé
CWG 80 C++98 tous les membres de données ne peuvent pas avoir le même nom
que celui de la classe (brise la compatibilité C)
autoriser les membres de données non statiques
à partager le nom de la classe s'il n'y a
pas de constructeur déclaré par l'utilisateur
CWG 190 C++98 lors de la détermination de la compatibilité de disposition,
tous les membres étaient considérés
ne considérer que les membres
de données non statiques
CWG 613 C++98 les utilisations non évaluées des membres de données non statiques non autorisées ces utilisations sont autorisées
CWG 645 C++98 il n'était pas spécifié si les champs de bits et
les membres non champs de bits sont compatibles en disposition
non compatibles en disposition
CWG 1397 C++11 la classe était considérée comme complète
dans les initialiseurs de membres par défaut
l'initialiseur de membre par défaut ne peut pas déclencher
la définition du constructeur par défaut
CWG 1425 C++98 il n'était pas clair si un objet à disposition standard
partage la même adresse avec le premier membre de données
non statique ou le premier sous-objet de classe de base
membre de données non statique
s'il est présent, sinon sous-objet
de classe de base s'il est présent
CWG 1696 C++98 les membres référence pouvaient être initialisés avec des temporaires
(dont la durée de vie se terminait à la fin du constructeur)
cette initialisation est incorrecte
CWG 1719 C++98 les types différemment qualifiés cv n'étaient pas compatibles en disposition qualifications cv ignorées, spécification améliorée
CWG 2254 C++11 pointeur vers une classe à disposition standard sans données
membres peut être reinterpret_cast vers sa première classe de base
peut être reinterpret_cast
vers n'importe laquelle de ses classes de base
CWG 2583 C++11 la séquence initiale commune ne
prenait pas en compte les exigences d'alignement
prise en compte
CWG 2759 C++20 la séquence initiale commune pouvait inclure
des membres déclarés [[ no_unique_address ]]
ils ne sont pas inclus

Voir aussi

classes
membres statiques
fonctions membres non statiques
vérifie si un type est un type standard-layout
(modèle de classe)
décalage en octets depuis le début d'un type standard-layout jusqu'au membre spécifié
(macro fonction)