Non-static data members
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
|
(depuis C++11) |
-
types incomplets
,
types de classes abstraites
, et leurs tableaux ne sont pas autorisés : en particulier, une classe
Cne peut pas avoir de membre de données non statique de typeC, bien qu'elle puisse avoir un membre de données non statique de typeC&(référence à C) ouC*(pointeur versC); - 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
|
(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 statiquemd'un autre membre de l'union de type classe non-unionT2à condition quemfasse partie de la séquence initiale commune deT1etT2(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_casten 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.
-
Dans une union de disposition standard avec un membre actif de type classe non-union
Initialisation des membres
Les membres de données non statiques peuvent être initialisés de l'une des deux manières suivantes :
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.
Exécuter ce code
#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 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 :
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(); };
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
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
| 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 | |
|
(C++11)
|
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) |
|