c++ c++11 c++14 c++03 c++98

c++ - Valor predeterminado, valor y desorden de inicialización cero



c++11 c++14 (2)

Estoy muy confundido acerca de la inicialización de valor y por defecto y cero. y especialmente cuando entran en acción para los diferentes estándares C ++ 03 y C ++ 11 (y C ++ 14 ).

Estoy citando e intentando extender una respuesta realmente buena Valor- / Predeterminado- / Cero- Inicia C ++ 98 y C ++ 03 aquí para hacerlo más general, ya que ayudaría a muchos usuarios si alguien pudiera ayudar a completar el necesitaba huecos para tener una buena visión general de lo que sucede cuando?

La visión completa de los ejemplos en pocas palabras:

A veces, la memoria devuelta por el nuevo operador se inicializará, y a veces no dependerá de si el tipo que está actualizando es un POD (datos antiguos simples) o si es una clase que contiene miembros de POD y está utilizando un constructor predeterminado generado por el compilador.

  • En C ++ 1998 hay 2 tipos de inicialización: inicialización cero y predeterminada
  • En C ++ 2003 se agregó un tercer tipo de inicialización, inicialización de valor .
  • En C ++ 2011 / C ++ 2014 solo se agregó la inicialización de lista y las reglas para la inicialización de valor / predeterminado / cero cambiaron un poco.

Asumir:

struct A { int m; }; struct B { ~B(); int m; }; struct C { C() : m(){}; ~C(); int m; }; struct D { D(){}; int m; }; struct E { E() = default; int m;} /** only possible in c++11/14 */ struct F {F(); int m;} F::F() = default; /** only possible in c++11/14 */

En un compilador de C ++ 98, debe ocurrir lo siguiente :

  • new A - valor indeterminado ( A es POD)
  • new A() - inicialización cero
  • new B - construcción predeterminada ( B::m no está inicializado, B no es POD)
  • new B() - construcción predeterminada ( B::m no está inicializada)
  • new C - construcción predeterminada ( C::m está inicializada en cero, C no es POD)
  • new C() - construcción predeterminada ( C::m está inicializada en cero)
  • new D - construcción predeterminada ( D::m no está inicializado, D no es POD)
  • new D() - construcción predeterminada? ( D::m no está inicializado)

En un compilador conforme a C ++ 03, las cosas deberían funcionar así:

  • new A - valor indeterminado ( A es POD)
  • new A() - value-initialize A , que es cero inicialización ya que es un POD.
  • new B - inicializaciones predeterminadas (deja B::m inicializar, B no es POD)
  • new B() : el valor inicializa B que inicializa a cero todos los campos, ya que su ctor predeterminado es generado por el compilador en lugar de definido por el usuario.
  • new C - default-initializes C , que llama al ctor predeterminado. ( C::m está inicializado en cero, C no es POD)
  • new C() - value-initializes C , que llama al ctor predeterminado. ( C::m está inicializado en cero)
  • new D - construcción predeterminada ( D::m no está inicializado, D no es POD)
  • new D() - valor-inicializa D? , que llama al ctor predeterminado ( D::m no está inicializado)

Valores en cursiva y? son incertidumbres, por favor ayuda a corregir esto :-)

En un compilador conforme a C ++ 11, las cosas deberían funcionar así:

??? (por favor ayuda si empiezo aquí, de todos modos saldrá mal)

En un compilador conforme a C ++ 14, las cosas deberían funcionar así: ??? (por favor ayuda si empiezo aquí, de todos modos saldrá mal) (Borrador basado en la respuesta)

  • new A - inicializa por defecto A , compilador gen. ctor, (deja A::m inicializar) ( A es POD)
  • new A() : el valor inicializa A , que es una inicialización cero desde 2. punto en [dcl.init] / 8

  • new B - inicializa por defecto B , compilador gen. ctor, (deja B::m inicializar) ( B no es POD)

  • new B() : el valor inicializa B que inicializa a cero todos los campos, ya que su ctor predeterminado es generado por el compilador en lugar de definido por el usuario.
  • new C - default-initializes C , que llama al ctor predeterminado. ( C::m está inicializado en cero, C no es POD)
  • new C() - value-initializes C , que llama al ctor predeterminado. ( C::m está inicializado en cero)
  • new D : inicializa por defecto D ( D::m no está inicializado, D no es POD)
  • new D() : el valor inicializa D , que llama al ctor predeterminado ( D::m no está inicializado)
  • new E - inicializa por defecto E , que llama a la comp. gen. ctor. ( E::m no está inicializado, E no es POD)
  • new E() - valor inicializa E , que cero inicializa E desde 2 puntos en [dcl.init] / 8 )
  • new F - default-initializes F , que llama a la comp. gen. ctor. ( F::m no está inicializado, F no es POD)
  • new F() - value-initializes F , que inicializa por defecto F desde 1. punto en [dcl.init] / 8 (la función F ctor es proporcionada por el usuario si es declarada por el usuario y no está explícitamente predeterminada o eliminada por primera vez declaración. Link )

C ++ 14 especifica la inicialización de los objetos creados con new en [expr.new] / 17 ([expr.new] / 15 en C ++ 11, y la nota no era una nota sino texto normativo en aquel entonces):

Una nueva expresión que crea un objeto de tipo T inicializa ese objeto de la siguiente manera:

  • Si se omite el nuevo inicializador , el objeto se inicializa por defecto (8.5). [ Nota: Si no se realiza la inicialización, el objeto tiene un valor indeterminado. - nota final ]
  • De lo contrario, el nuevo inicializador se interpreta de acuerdo con las reglas de inicialización de 8.5 para la inicialización directa .

La inicialización predeterminada se define en [dcl.init] / 7 (/ 6 en C ++ 11, y la redacción misma tiene el mismo efecto):

Inicializar por defecto un objeto de tipo T significa:

  • si T es un tipo de clase (posiblemente calificado para cv) (Cláusula 9), se llama al constructor predeterminado (12.1) para T (y la inicialización está mal formada si T no tiene un constructor predeterminado o una resolución de sobrecarga (13.3) da como resultado un ambigüedad o en una función que se elimina o no se puede acceder desde el contexto de la inicialización);
  • si T es un tipo de matriz, cada elemento se inicializa por defecto ;
  • de lo contrario, no se realiza la inicialización.

Así

  • new A únicamente hace que se llame al constructor predeterminado de A , que no inicializa m . Valor indeterminado. Debería ser lo mismo para el new B
  • new A() se interpreta de acuerdo con [dcl.init] / 11 (/ 10 en C ++ 11):

    Un objeto cuyo inicializador es un conjunto vacío de paréntesis, es decir, () , se inicializará con valor.

    Y ahora considere [dcl.init] / 8 (/ 7 en C ++ 11 †):

    Para inicializar un objeto de tipo T significa:

    • si T es un tipo de clase (posiblemente calificado por cv) (Cláusula 9) sin constructor predeterminado (12.1) o un constructor predeterminado proporcionado o eliminado por el usuario, entonces el objeto se inicializa por defecto;
    • si T es un tipo de clase (posiblemente calificado por cv) sin un constructor predeterminado proporcionado o eliminado por el usuario, entonces el objeto se inicializa a cero y se comprueban las restricciones semánticas para la inicialización predeterminada, y si T tiene un constructor predeterminado no trivial , el objeto está inicializado por defecto;
    • si T es un tipo de matriz, entonces cada elemento tiene un valor inicializado;
    • de lo contrario, el objeto se inicializa a cero.

    Por lo tanto, la new A() inicializará cero m . Y esto debería ser equivalente para A y B

  • new C y new C() volverán a inicializar el objeto por defecto, ya que se aplica el primer punto de la última cita (¡C tiene un constructor predeterminado proporcionado por el usuario!). Pero, claramente, ahora m se inicializa en el constructor en ambos casos.

† Bueno, este párrafo tiene una redacción ligeramente diferente en C ++ 11, que no altera el resultado:

Para inicializar un objeto de tipo T significa:

  • si T es un tipo de clase (posiblemente calificado por cv) (Cláusula 9) con un constructor proporcionado por el usuario (12.1), se llama al constructor predeterminado para T (y la inicialización está mal formada si T no tiene un constructor predeterminado accesible) ;
  • si T es un tipo de clase no sindicalizado (posiblemente calificado por cv) sin un constructor proporcionado por el usuario, entonces el objeto se inicializa a cero y, si el constructor predeterminado declarado implícitamente por T no es trivial, se llama a ese constructor.
  • si T es un tipo de matriz, entonces cada elemento tiene un valor inicializado;
  • de lo contrario, el objeto se inicializa a cero.

La siguiente respuesta amplía la respuesta https://.com/a/620402/977038 que serviría como referencia para C ++ 98 y C ++ 03

Citando la respuesta

  1. En C ++ 1998 hay 2 tipos de inicialización: cero y predeterminado
  2. En C ++ 2003, un tercer tipo de inicialización, se agregó la inicialización de valor.

C ++ 11 (en referencia a n3242)

Inicializadores

8.5 Inicializadores [dcl.init] especifica que un POD variable o no POD se puede inicializar como inicializador de paréntesis o igual, que puede ser una lista de inicialización con paréntesis o una cláusula de inicializador denominada en conjunto como paréntesis o igual- inicializador o usando (lista de expresiones) . Antes de C ++ 11, solo se admitía (lista de expresiones) o cláusula initializer aunque la cláusula initializer estaba más restringida que la que tenemos en C ++ 11. En C ++ 11, la cláusula initializer ahora es compatible con la lista de inicializaciones entre paréntesis, aparte de la expresión de asignación, como sucedía en C ++ 03. La siguiente gramática resume la nueva cláusula admitida, donde la parte en negrita se agrega nuevamente en el estándar C ++ 11.

inicializador:
inicializador de paréntesis o igual
(lista de expresiones)
inicializador de paréntesis o igual:
= cláusula-inicializador
lista-inicializada
cláusula-inicializador:
asignación-expresión
lista-inicializada
lista de inicializadores:
cláusula-inicializador ... opt
lista-inicializador, cláusula-inicializador ... opt **
lista-inicializada:
{lista-inicializador, opt}
{}

Inicialización

Al igual que C ++ 03, C ++ 11 todavía admite tres formas de inicialización

Nota

La parte resaltada en negrita se ha agregado en C ++ 11 y la que está tachada se ha eliminado de C ++ 11.

  1. Tipo de inicializador: 8.5.5 [dcl.init] _zero-initialize_

Realizado en los siguientes casos

  • Los objetos con duración de almacenamiento estático o de subprocesos se inicializan en cero
  • Si hay menos inicializadores que elementos de matriz, cada elemento no inicializado explícitamente se inicializará a cero
  • Durante la inicialización de valor , si T es un tipo de clase no sindicalizada (posiblemente calificada por cv) sin un constructor proporcionado por el usuario, entonces el objeto se inicializa en cero.

Inicializar a cero un objeto o referencia de tipo T significa:

  • si T es un tipo escalar (3.9), el objeto se establece en el valor 0 (cero), tomado como una expresión constante integral , convertido a T;
  • si T es un tipo de clase sin unión (posiblemente calificado por cv) , cada miembro de datos no estático y cada subobjeto de clase base se inicializa en cero y el relleno se inicializa en cero bits;
  • si T es un tipo de unión (posiblemente calificado por cv) , el primer miembro de datos con nombre no estático del objeto se inicializa a cero y el relleno se inicializa a cero bits;
  • si T es un tipo de matriz, cada elemento tiene inicialización cero;
  • si T es un tipo de referencia, no se realiza ninguna inicialización.

2. Tipo de inicializador: 8.5.6 [dcl.init] _default-initialize_

Realizado en los siguientes casos

  • Si se omite el nuevo inicializador, el objeto se inicializa por defecto; Si no se realiza la inicialización, el objeto tiene un valor indeterminado.
  • Si no se especifica ningún inicializador para un objeto, el objeto se inicializa por defecto, excepto para Objetos con duración de almacenamiento estático o de subprocesos
  • Cuando una clase base o un miembro de datos no estático no se menciona en una lista de inicializador de constructor y se llama a ese constructor.

Inicializar por defecto un objeto de tipo T significa:

  • si T es un tipo de clase no POD (posiblemente calificado por cv) (Cláusula 9), se llama al constructor predeterminado para T (y la inicialización está mal formada si T no tiene un constructor predeterminado accesible);
  • si T es un tipo de matriz, cada elemento se inicializa por defecto;
  • de lo contrario, no se realiza la inicialización.

Nota Hasta C ++ 11, solo los tipos de clase que no son POD con duración de almacenamiento automático se consideraron inicializados por defecto cuando no se utiliza ningún inicializador.

3. Tipo de inicializador: 8.5.7 [dcl.init] _value-initialize_

  1. Cuando un objeto (temporal sin nombre, variable con nombre, duración de almacenamiento dinámico o miembro de datos no estático) cuyo inicializador es un conjunto vacío de paréntesis, es decir, () o llaves {}

Para inicializar un objeto de tipo T significa:

  • si T es un tipo de clase (posiblemente calificado por cv) (Cláusula 9) con un constructor proporcionado por el usuario (12.1), se llama al constructor predeterminado para T (y la inicialización está mal formada si T no tiene un constructor predeterminado accesible) ;
  • si T es un tipo de clase no sindicalizado (posiblemente calificado por cv) sin un constructor proporcionado por el usuario, entonces cada miembro de datos no estático y componente de clase base de T tiene un valor inicializado; entonces el objeto se inicializa a cero y, si el constructor predeterminado declarado implícitamente de T no es trivial, se llama a ese constructor.
  • si T es un tipo de matriz, entonces cada elemento tiene un valor inicializado;
  • de lo contrario, el objeto se inicializa a cero.

Entonces para resumir

Nota La cita relevante del estándar se resalta en negrita

  • nueva A: inicializaciones predeterminadas (deja A :: m sin inicializar)
  • new A (): Zero-initialize A, ya que el valor candidato inicializado no tiene un constructor predeterminado proporcionado o eliminado por el usuario. si T es un tipo de clase no sindicalizado (posiblemente calificado por cv) sin un constructor proporcionado por el usuario, entonces el objeto se inicializa a cero y, si el constructor predeterminado declarado implícitamente por T no es trivial, se llama a ese constructor.
  • nuevo B: inicializaciones predeterminadas (deja B :: m sin inicializar)
  • nuevo B (): el valor inicializa B que inicializa cero todos los campos; si T es un tipo de clase (posiblemente calificado por cv) (Cláusula 9) con un constructor proporcionado por el usuario (12.1), entonces el constructor predeterminado para T se llama
  • nuevo C: default-initializes C, que llama al ctor predeterminado. si T es un tipo de clase (posiblemente calificado por cv) (Cláusula 9), se llama al constructor predeterminado para T , Además, si se omite el nuevo inicializador, el objeto se inicializa por defecto
  • new C (): value-initializes C, que llama al ctor predeterminado. si T es un tipo de clase (posiblemente calificado por cv) (Cláusula 9) con un constructor proporcionado por el usuario (12.1), se llama al constructor predeterminado para T. Además, un objeto cuyo inicializador es un conjunto vacío de paréntesis, es decir, (), se inicializará con valor