qué - que es un destructor en c++
¿Cuáles son las diferencias entre C-like, constructor e inicialización uniforme? (2)
¿Cuáles son las diferencias entre c-like, constructor e inicialización uniforme?
Para tipos primitivos como int
, no hay diferencia práctica; así que consideremos un tipo de clase T
lugar.
El primer estilo es equivalente a
T x(T(0));
creando un objeto temporal a partir de la expresión del inicializador, y luego inicializando x
moviéndolo o copiando eso. En la práctica, el movimiento o la copia se eliminarán, de modo que el resultado sea el mismo que el del segundo estilo; la única diferencia es que la primera fallará si no hay una copia accesible o un constructor de movimiento.
El segundo inicializa directamente el objeto usando un constructor que toma un argumento, dando un error si no hay un constructor adecuado.
El tercero depende de qué constructores estén disponibles.
- si hay un constructor que toma
std::initializer_list
, usa eso; - de lo contrario, si hay un constructor tomando un solo argumento de un tipo adecuado, lo usa;
- de lo contrario, si se trata de un agregado (sin constructores) con un miembro, ese miembro se inicializa con cero;
- de lo contrario, es un error.
¿Y debería usar siempre la inicialización uniforme?
No. Algunas veces necesita inicialización de estilo de función para distinguir entre un constructor initializer_list
y uno que toma otros tipos de argumento. Por ejemplo:
std::vector<int> v1(10, 42); // 10 elements with value 42
std::vector<int> v2{10, 42}; // 2 elements with values 10 and 42
Tampoco debe llamarlo "inicialización uniforme", ya que no es "uniforme" en ningún sentido significativo. El término oficial es "inicialización de llaves".
TTBOMK, hay tres formas de inicializar una variable en C ++.
int x = 0; // C-like initialization
int x (0); // Constructor initialization
int x {0}; // Uniform initialization
La inicialización uniforme se introdujo para C++11 para proporcionar una sintaxis más uniforme para inicializar diferentes tipos de variables, lo que requería una sintaxis diferente en C++03 .
¿Cuáles son las diferencias entre C-like, constructor e inicialización uniforme? ¿Y debería usar siempre la inicialización uniforme?
Primero, recomendaría ver la siguiente charla de Herb Sutter, en la que da algunas recomendaciones sobre el tema. La discusión de inicialización de llaves comienza alrededor de las 23:00 .
Cuando habla de tipos de datos primitivos, los 3 producen el mismo resultado. Personalmente prefiero seguir con la vieja sintaxis int x = 0
, pero todo se reduce a preferencias personales.
Para los tipos de clase, la inicialización de llaves y la inicialización del constructor de la vieja escuela no son completamente intercambiables. Por ejemplo:
vector<int> v (100); // Creates a 100-element vector
vector<int> v {100}; // Creates a 1-element vector, holding the value 100.
Esto se debe a que std::vector
tiene un constructor que define explícitamente std::initializer_list
como su único argumento. Manten eso en mente
auto var = {1, 2};
crea una std::initializer_list
, con var
como su identificador.
Lo que pasa con las listas de inicializadores es que proporcionan consistencia que es un cambio bienvenido de lo que estaba disponible de antemano. Por ejemplo, si tuviera que inicializar una matriz en C ++, usaría:
int arr[] = {1, 2, 3, 4};
Pero si quería inicializar un vector<int>
con los mismos elementos, tenía que:
- Inicialice primero el arr anterior y luego pase
arr
yarr + 4
- Crea el vector y push_back () los elementos individualmente o en un bucle.
Con C ++ 11, podrías usar
vector<int> v = {1, 2, 3, 4}; // Same syntax. Nice! Note that the = is optional
Otra instancia en la que la inicialización de llaves es útil es que proporciona una solución alternativa al análisis más molesto de C ++. A partir de la conversación, supongamos que tenemos dos clases, origin
y extents
, cuyas instancias se pueden pasar para construir otro objeto de tipo rectangle
. La siguiente declaración:
rectangle w(origin(), extents());
no le permite crear un objeto rectangle
utilizando origin
y extents
temporales, porque esa declaración se analiza como una declaración de función. Tsk tsk. Entonces, normalmente, tendrías que hacer:
origin o;
extents e;
rectangle w(o, e);
Con la inicialización de llaves, puedes crearlas sobre la marcha, y
rectangle w {origin(), extents()};
funcionará según lo previsto, es decir, pasará al constructor que está sobrecargado con un objeto de origin
ya que es el primer argumento y un objeto de extents
como el segundo.
La regla es para objetos, use inicialización de llaves a menos que tenga una razón para no hacerlo.