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

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

  • El especificador de clase de almacenamiento thread_local no está permitido (pero sí está permitido para static miembros de datos);
(desde C++11)
  • tipos incompletos , tipos de clase abstracta , y arreglos de estos no están permitidos: en particular, una clase C no puede tener un miembro de datos no estático de tipo C , aunque puede tener un miembro de datos no estático de tipo C& (referencia a C) o C* (puntero a C );
  • 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

  • si __has_cpp_attribute ( no_unique_address ) no es 0 , ninguna entidad es declarada con el atributo [[ no_unique_address ]] ,
(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ático m de otro miembro de la unión de tipo de clase no unión T2 siempre que m sea parte de la secuencia inicial común de T1 y T2 (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_cast a 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.

Inicialización de miembros

Los miembros de datos no estáticos pueden inicializarse de una de dos maneras:

1) En la lista de inicialización de miembros del constructor.
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.

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

No se permiten inicializadores de miembro predeterminados para miembros de campo de bits .

(hasta C++20)

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:

1) Como parte de una expresión de acceso a miembro de clase, en la cual la clase tiene este miembro o deriva de una clase que lo tiene, incluyendo las expresiones de acceso a miembro this - > implícitas que aparecen cuando un nombre de miembro no estático se usa en cualquiera de los contextos donde 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();
};
2) Para formar un puntero a miembro no estático .
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
3) (solo para miembros de datos, no para funciones miembro) Cuando se utiliza en operandos no evaluados .
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: estos usos están permitidos mediante la resolución de CWG issue 613 en N2253 , que es tratado como un cambio en C++11 por algunos compiladores (por ejemplo, clang).

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
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)