Non-static data members
Los miembros de datos no estáticos se declaran en una especificación de miembro de una clase.
class S { int n; // miembro de datos no estático int& r; // miembro de datos no estático de tipo referencia int a[2] = {1, 2}; // miembro de datos no estático con inicializador de miembro predeterminado (C++11) std::string s, *ps; // dos miembros de datos no estáticos struct NestedS { std::string s; } d5; // miembro de datos no estático de tipo anidado char bit : 2; // campo de bits de dos bits };
Cualquier declaración simple está permitida, excepto
|
(desde C++11) |
-
tipos incompletos
,
tipos de clase abstracta
, y arreglos de estos no están permitidos: en particular, una clase
Cno puede tener un miembro de datos no estático de tipoC, aunque puede tener un miembro de datos no estático de tipoC&(referencia a C) oC*(puntero aC); - un miembro de datos no estático no puede tener el mismo nombre que el nombre de la clase si hay presente al menos un constructor declarado por el usuario;
|
(desde C++11) |
Además, bit-field las declaraciones están permitidas.
Contenidos |
Diseño
Cuando se crea un objeto de alguna clase
C
, cada miembro de datos no estático de tipo no referencia se asigna en alguna parte de la representación del objeto de
C
. Si los miembros de referencia ocupan almacenamiento está definido por la implementación, pero su
duración de almacenamiento
es la misma que la del objeto en el que son miembros.
|
Para tipos de clase no union , de tamaño no cero (desde C++20) los miembros no separados por un especificador de acceso (hasta C++11) con el mismo acceso de miembro (desde C++11) siempre se asignan de modo que los miembros declarados posteriormente tengan direcciones más altas dentro de un objeto de clase. Los miembros separados por un especificador de acceso (hasta C++11) con diferente control de acceso (desde C++11) se asignan en orden no especificado (el compilador puede agruparlos juntos). |
(hasta C++23) |
|
Para tipos de clase no union , de tamaño no cero los miembros siempre se asignan de modo que los miembros declarados posteriormente tengan direcciones más altas dentro de un objeto de clase. Nótese que el control de acceso del miembro aún afecta la propiedad de diseño estándar (ver más abajo). |
(desde C++23) |
Los requisitos de alineación pueden requerir relleno entre los miembros, o después del último miembro de una clase.
Diseño-estándar
|
Una clase se considera standard-layout y tiene las propiedades descritas a continuación si y solo si es una clase POD . |
(until C++11) |
|
Una clase donde todos los miembros de datos no estáticos tienen el mismo control de acceso y se cumplen ciertas otras condiciones se conoce como clase de diseño estándar (consulte clase de diseño estándar para la lista de requisitos). |
(since C++11) |
La secuencia inicial común de dos tipos de clase no unión de diseño estándar es la secuencia más larga de miembros de datos no estáticos y campos de bits en orden de declaración, comenzando con la primera de estas entidades en cada una de las clases, tal que
|
(desde C++20) |
- las entidades correspondientes tienen tipos compatibles con el diseño,
- las entidades correspondientes tienen los mismos requisitos de alineación , y
- ambas entidades son campos de bits con el mismo ancho o ninguna es un campo de bits.
struct A { int a; char b; }; struct B { const int b1; volatile char b2; }; // La secuencia inicial común de A y B es A.a, A.b y B.b1, B.b2 struct C { int c; unsigned : 0; char b; }; // La secuencia inicial común de A y C es A.a y C.c struct D { int d; char b : 4; }; // La secuencia inicial común de A y D es A.a y D.d struct E { unsigned int e; char b; }; // La secuencia inicial común de A y E está vacía
Dos tipos de clase no unión de diseño estándar se denominan
layout-compatible
si son del mismo tipo ignorando los calificadores cv, si los hay, son enumeraciones
layout-compatible
(es decir, enumeraciones con el mismo tipo subyacente), o si su
common initial sequence
consiste en cada miembro de datos no estático y campo de bits (en el ejemplo anterior,
A
y
B
son layout-compatible).
Dos uniones de diseño estándar se denominan layout-compatible si tienen el mismo número de miembros de datos no estáticos y los miembros de datos no estáticos correspondientes (en cualquier orden) tienen tipos layout-compatible.
Los tipos de diseño estándar tienen las siguientes propiedades especiales:
-
-
En una unión de diseño estándar con un miembro activo de tipo de clase no unión
T1, se permite leer un miembro de datos no estáticomde otro miembro de la unión de tipo de clase no uniónT2siempre quemsea parte de la secuencia inicial común deT1yT2(excepto que leer un miembro volátil a través de un glvalue no volátil es indefinido). -
Un puntero a un objeto de tipo de clase de diseño estándar puede ser
reinterpret_casta un puntero a su primer miembro de datos no estático y no campo de bits (si tiene miembros de datos no estáticos) o de lo contrario a cualquiera de sus subobjetos de clase base (si tiene alguno), y viceversa. En otras palabras, no se permite relleno antes del primer miembro de datos de un tipo de diseño estándar. Nótese que las reglas de aliasing estricto aún se aplican al resultado de dicha conversión. - La macro offsetof puede usarse para determinar el desplazamiento de cualquier miembro desde el inicio de una clase de diseño estándar.
-
En una unión de diseño estándar con un miembro activo de tipo de clase no unión
Inicialización de miembros
Los miembros de datos no estáticos pueden inicializarse de una de dos maneras:
struct S { int n; std::string s; S() : n(7) {} // direct-initializes n, default-initializes s };
|
2)
Mediante un
inicializador de miembro predeterminado
, que es un inicializador entre llaves o con signo igual
incluido en la declaración del miembro
y se utiliza si el miembro se omite de la lista de inicialización de miembros de un constructor.
struct S { int n = 7; std::string s{'a', 'b', 'c'}; S() {} // el inicializador de miembro predeterminado inicializará n por copia, e inicializará s por lista }; Si un miembro tiene un inicializador de miembro predeterminado y también aparece en la lista de inicialización de miembros en un constructor, el inicializador de miembro predeterminado se ignora para ese constructor.
Ejecutar este código
#include <iostream> int x = 0; struct S { int n = ++x; S() {} // utiliza el inicializador de miembro predeterminado S(int arg) : n(arg) {} // utiliza el inicializador de miembro }; int main() { std::cout << x << '\n'; // imprime 0 S s1; // se ejecutó el inicializador predeterminado std::cout << x << '\n'; // imprime 1 S s2(7); // no se ejecutó el inicializador predeterminado std::cout << x << '\n'; // imprime 1 }
Los miembros de tipo array no pueden deducir su tamaño a partir de inicializadores de miembros: struct X { int a[] = {1, 2, 3}; // error int b[3] = {1, 2, 3}; // OK }; Los inicializadores de miembro predeterminados no pueden causar la definición implícita de un constructor predeterminado para la clase envolvente o la especificación de excepciones de ese constructor: struct node { node* p = new node; // error: uso de node::node() implícito o predeterminado }; Los miembros de referencia no pueden vincularse a temporales en un inicializador de miembro predeterminado (nota: existe la misma regla para listas de inicialización de miembros ): struct A { A() = default; // OK A(int v) : v(v) {} // OK const int& v = 42; // OK }; A a1; // error: enlace incorrecto de temporal a referencia A a2(1); // OK (el inicializador de miembro predeterminado se ignora porque v aparece en un constructor) // sin embargo a2.v es una referencia colgante |
(desde C++11) |
|
Si un miembro de referencia se inicializa desde su inicializador de miembro predeterminado (hasta C++20) un miembro tiene un inicializador de miembro predeterminado (desde C++20) y una subexpresión potencialmente evaluada de la misma es una inicialización de agregado que usaría ese inicializador de miembro predeterminado, el programa está mal formado: struct A; extern A a; struct A { const A& a1{A{a, a}}; // OK const A& a2{A{}}; // error }; A a{a, a}; // OK |
(desde C++17) |
Uso
El nombre de un miembro de datos no estático o una función miembro no estática solo puede aparecer en las siguientes tres situaciones:
this
está permitido (dentro de cuerpos de funciones miembro, en listas de inicialización de miembros, en los inicializadores de miembros predeterminados en la clase).
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: uso de m para crear un puntero a miembro void (S::*fp)() = &S::f; // OK: uso de f para crear un puntero a miembro
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
Notas
| Macro de prueba de características | Valor | Estándar | Característica |
|---|---|---|---|
__cpp_nsdmi
|
200809L
|
(C++11) | Inicializadores de miembros de datos no estáticos |
__cpp_aggregate_nsdmi
|
201304L
|
(C++14) | Clases agregadas con inicializadores de miembros predeterminados |
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C++ publicados anteriormente.
| DR | Se aplica a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| CWG 80 | C++98 |
todos los miembros de datos no pueden tener el mismo nombre
que el nombre de la clase (rompe compatibilidad con C) |
permitir que miembros de datos no estáticos
compartan el nombre de la clase si no hay constructor declarado por el usuario |
| CWG 190 | C++98 |
al determinar compatibilidad de layout,
se consideraban todos los miembros |
solo considerar miembros de datos
no estáticos |
| CWG 613 | C++98 | usos no evaluados de miembros de datos no estáticos no permitidos | tales usos están permitidos |
| CWG 645 | C++98 |
no estaba especificado si miembros de campo de bits y
no campo de bits son layout compatibles |
no son layout compatibles |
| CWG 1397 | C++11 |
la clase se consideraba completa
en los inicializadores de miembros por defecto |
la inicialización de miembro por defecto no puede activar
la definición del constructor por defecto |
| CWG 1425 | C++98 |
no estaba claro si un objeto de layout estándar
comparte la misma dirección con el primer miembro de datos no estático o con el primer subobjeto de clase base |
miembro de datos no estático
si está presente, en caso contrario subobjeto de clase base si está presente |
| CWG 1696 | C++98 |
los miembros de referencia podían inicializarse a temporales
(cuya duración terminaría al final del constructor) |
dicha inicialización es incorrecta |
| CWG 1719 | C++98 | tipos con calificación cv diferente no eran layout-compatibles | calificadores cv ignorados, especificación mejorada |
| CWG 2254 | C++11 |
puntero a clase de layout estándar sin datos
miembros puede ser reinterpret_cast a su primera clase base |
puede ser reinterpret_cast
a cualquiera de sus clases base |
| CWG 2583 | C++11 |
la secuencia inicial común no
consideraba requisitos de alineación |
considerada |
| CWG 2759 | C++20 |
la secuencia inicial común podría incluir
miembros declarados
[[
no_unique_address
]]
|
no están incluidos |
Véase también
| clases | |
| miembros estáticos | |
| funciones miembro no estáticas | |
|
(C++11)
|
comprueba si un tipo es un tipo de
diseño estándar
(plantilla de clase) |
|
desplazamiento en bytes desde el inicio de un tipo de
diseño estándar
hasta el miembro especificado
(macro de función) |
|