¿Qué es esta extraña sintaxis de miembro de dos puntos (” : “) en el constructor?
⏰ 11 minutos de lectura
ceros
Recientemente he visto un ejemplo como el siguiente:
#include <iostream>
class Foo {
public:
int bar;
Foo(int num): bar(num) {};
};
int main(void) {
std::cout << Foo(42).bar << std::endl;
return 0;
}
¿Qué significa esto extraño : bar(num) ¿significar? De alguna manera parece inicializar la variable miembro, pero nunca antes había visto esta sintaxis. Parece una llamada de función/constructor pero para un int? No tiene sentido para mí. Tal vez alguien me pueda iluminar. Y, por cierto, ¿existen otras características esotéricas del lenguaje como esta, que nunca encontrará en un libro de C++ ordinario?
Un “libro ordinario de c ++” que no menciona esto es probablemente un libro ac donde alguien pensó que “++” se vería bien en la portada …
– Rasmus Kaj
10 de noviembre de 2009 a las 23:35
“nunca encontrarás en un libro de C++ normal”. Oh. Estimado. Deseche su “libro ordinario de C++” ahora mismo. No por la ventana, alguien más podría recogerlo. De preferencia tritúrelo y póngalo a reciclar. ¿Hecho? Ahora consulte stackoverflow.com/questions/388242/… para obtener un nuevo libro.
–Steve Jessop
10 de noviembre de 2009 a las 23:36
Esta característica del lenguaje no es nada esotérica. Es una característica bastante importante de la construcción de objetos.
– Carlos Salvia
10 de noviembre de 2009 a las 23:36
De hecho, lejos de ser esotérico, a menudo no tienes más remedio que usar listas de inicializadores. Por ejemplo, si su clase contiene un const variable miembro, o una referencia, tiene que usar una lista de inicializadores.
– Carlos Salvia
10 de noviembre de 2009 a las 23:42
Guardar
Foo(int num): bar(num)
Esta construcción se llama Lista de inicializadores de miembros en C++.
Dicho simplemente, es inicializa tu miembro bar a un valor num.
¿Cuál es la diferencia entre Inicializar y Asignar dentro de un constructor?
Inicialización de miembros:
Foo(int num): bar(num) {};
Asignación de miembros:
Foo(int num)
{
bar = num;
}
Hay una diferencia significativa entre inicializar un miembro usando la lista de inicializadores de miembros y asignarle un valor dentro del cuerpo del constructor.
Cuando usted inicializar campos a través de la lista de inicializadores de miembros, los constructores se llamarán una vez y el objeto se construirá e inicializará en una sola operación.
Si utiliza asignación luego, los campos se inicializarán primero con constructores predeterminados y luego se reasignarán (a través del operador de asignación) con valores reales.
Como puede ver, hay una sobrecarga adicional de creación y asignación en este último, que podría ser considerable para las clases definidas por el usuario.
Cost of Member Initialization = Object Construction
Cost of Member Assignment = Object Construction + Assignment
Este último es en realidad equivalente a:
Foo(int num) : bar() {bar = num;}
Mientras que el primero es equivalente a solo:
Foo(int num): bar(num){}
Para miembros de clase incorporados (su ejemplo de código) o POD, no hay gastos generales prácticos.
¿Cuándo TIENE que usar la lista de inicializadores de miembros?
Vas a tener (más bien forzado) a use una lista de inicializadores de miembros si:
Su clase tiene un miembro de referencia
Su clase tiene un miembro const no estático o
El miembro de su clase no tiene un constructor predeterminado o
Para la inicialización de miembros de la clase base o
Cuando el nombre del parámetro del constructor es el mismo que el miembro de datos (esto no es realmente IMPRESCINDIBLE)
Un ejemplo de código:
class MyClass {
public:
// Reference member, has to be Initialized in Member Initializer List
int &i;
int b;
// Non static const member, must be Initialized in Member Initializer List
const int k;
// Constructor’s parameter name b is same as class data member
// Other way is to use this->b to refer to data member
MyClass(int a, int b, int c) : i(a), b(b), k(c) {
// Without Member Initializer
// this->b = b;
}
};
class MyClass2 : public MyClass {
public:
int p;
int q;
MyClass2(int x, int y, int z, int l, int m) : MyClass(x, y, z), p(l), q(m) {}
};
int main() {
int x = 10;
int y = 20;
int z = 30;
MyClass obj(x, y, z);
int l = 40;
int m = 50;
MyClass2 obj2(x, y, z, l, m);
return 0;
}
MyClass2 no tiene un constructor predeterminado, por lo que debe inicializarse a través de la lista de inicializadores de miembros.
clase base MyClass no tiene un constructor predeterminado, por lo que para inicializar su miembro será necesario usar la Lista de inicializadores de miembros.
Puntos importantes a tener en cuenta al usar listas de inicializadores de miembros:
Las variables de miembro de clase siempre se inicializan en el orden en que se declaran en la clase.
Ellos son no inicializados en el orden en que se especifican en la lista de inicializadores de miembros.
En resumen, la lista de inicialización de miembros no determina el orden de inicialización.
Teniendo en cuenta lo anterior, siempre es una buena práctica mantener el mismo orden de miembros para la inicialización de miembros que el orden en que se declaran en la definición de clase. Esto se debe a que los compiladores no advierten si los dos órdenes son diferentes, pero un usuario relativamente nuevo podría confundir la lista de inicializadores de miembros con el orden de inicialización y escribir algún código que dependa de eso.
@nils Esta es la mejor respuesta hasta ahora. El orden de inicialización señalado por Als también es extremadamente importante, mientras que el compilador de Visual Studio no dirá nada al respecto, otro compilador como gcc fallará. También es importante tener en cuenta que dependiendo de su compilador y la situación en la que se encuentre no siempre cierto que esto mejorará el rendimiento o será más eficiente.
– Fuerza Mágica
14 de marzo de 2012 a las 7:21
@ryf9059: ¿Por qué crees que será un inconveniente? Tienes que enumerarlos de todos modos, entonces, ¿por qué no en el mismo orden que en la declaración?
– Alok Guardar
7 de enero de 2013 a las 12:10
esta debería haber sido la respuesta. Gracias a Dios, me desplacé hacia abajo, de lo contrario me lo habría perdido.
– Amante del café
13 de agosto de 2015 a las 11:41
@AlokGuardar MiClase(int a, int b, int c) : i(a), b(b), k(c) { // Sin inicializador de miembros // this->b = b; } debería ser así MiClase(int &a int b, int c) : i(a), b(b), k(c) { // Sin inicializador de miembros // this->b = b; } y así los respectivos cambios en la declaración y convocatoria. sin este cambio i se referirá a a pero a no puedo referirme x ya que contiene solo el valor de x tan indirectamente i no puedo referirme x. entonces si modificamos el valor de i entonces solo modifica a pero no x
– Abhishek Mané
15 mayo 2021 a las 11:42
@AbhishekMane Tiene razón, y aquí hay un enlace a la pregunta relacionada que lo muestra: stackoverflow.com/q/67619383/3150802
– Peter – Reincorporar a Mónica
20 de mayo de 2021 a las 12:08
james mcnellis
Es un lista de inicialización de miembros. Debería encontrar información al respecto en cualquier buen libro de C++.
El punto para llevar de la entrada de preguntas frecuentes es que,
En igualdad de condiciones, su código se ejecutará más rápido si usa listas de inicialización en lugar de asignación.
Conocer la terminología es fundamental: estoy celoso de no haberlo pensado.
– Mark Ransom
10 de noviembre de 2009 a las 23:36
También hay un montón de otras razones para usar listas de inicio. especialmente cuando el orden de inicialización es importante. Es una pena que tenga una sintaxis de llamada de función falsa tan estúpida.
– Martín Beckett
10 de noviembre de 2009 a las 23:50
@mgb, la lista de inicialización no determina el orden de inicialización. Las variables miembro se inicializan en el orden en que se declaran en la clase, incluso si difiere del orden de las inicializaciones en el constructor.
– ScottJ
11 de noviembre de 2009 a las 0:26
@mgb: no creo que esté destinado a ser una sintaxis de llamada de función falsa. Es sintaxis de inicialización, como int i(23);, std::vector<double> emptyVec(0);, std::vector<double> fullVec(10,23.);etc. Solo con el tipo eliminado, por supuesto, porque el tipo está en la declaración del miembro.
–Steve Jessop
11 de noviembre de 2009 a las 1:03
@Martin: no tiene una sintaxis de llamada de función, tiene una sintaxis de construcción (ala: nueva cadena (“Nombre”)). Encaja mejor con el constructor que Foo(int num) : m_Count = 5. Sin mencionar que las clases deben construirse en este punto de todos modos, ya que se inicializa aquí. Foo(int num) : Bar = num, no compilaría bien. Parece extraño ver Foo(int num) : m_Count(num), ya que los tipos primitivos no se construyen.
– Lee Louviere
22 de abril de 2011 a las 20:33
jose
Esa es la inicialización del constructor. Es la forma correcta de inicializar miembros en un constructor de clase, ya que evita que se invoque el constructor predeterminado.
Considere estos dos ejemplos:
// Example 1
Foo(Bar b)
{
bar = b;
}
// Example 2
Foo(Bar b)
: bar(b)
{
}
En el ejemplo 1:
Bar bar; // default constructor
bar = b; // assignment
En el ejemplo 2:
Bar bar(b) // copy constructor
Se trata de eficiencia.
No diría que se trata de eficiencia. Se trata de proporcionar una forma de inicializar algo que requiere inicialización, pero que no se puede inicializar por defecto. Por alguna razón, la gente menciona constantes y referencias como ejemplos, mientras que el ejemplo más obvio serían las clases sin constructores predeterminados.
– Ant
11 de noviembre de 2009 a las 0:26
Ambos tenemos razón; en su ejemplo, podría argumentar a favor de la eficiencia; para el problema del constructor const/reference/no default, es tanto la eficiencia como la necesidad. Voté una respuesta a continuación debido a eso 🙂 [Farnsworth voice] Puede hacer otras cosas. ¿Por qué no debería?
– José
11 de noviembre de 2009 a las 4:19
Bar bar(); // default constructor ¿Está seguro?
– Carreras de ligereza en órbita
27 de diciembre de 2015 a las 14:55
@LightnessRacesinOrbit Solo tengo curiosidad por saber: ¿Qué debería ser eso según tú?
– ajaysinghnegi
2 ago. 2019 a las 11:01
leopardopielpíldoracuadrosombrero
Esto se llama una lista de inicialización. Es una forma de inicializar los miembros de la clase. Hay beneficios al usar esto en lugar de simplemente asignar nuevos valores a los miembros en el cuerpo del constructor, pero si tiene miembros de clase que son constantes o referencias ellos deber ser inicializado.
Básicamente, en su caso, x se inicializará con _x, y con _y, z con _z.
Incinerador de basuras
El otro ya te explicó que la sintaxis que observas se llama “lista de inicializadores de constructores”. Esta sintaxis le permite inicializar de forma personalizada subobjetos base y subobjetos miembros de la clase (en lugar de permitirles inicializarse de forma predeterminada o permanecer sin inicializar).
Solo quiero señalar que la sintaxis que, como dijiste, “parece una llamada de constructor”, no es necesariamente una llamada de constructor. En lenguaje C++ el () la sintaxis es solo una forma estándar de sintaxis de inicialización. Se interpreta de manera diferente para diferentes tipos. Para tipos de clase con constructor definido por el usuario, significa una cosa (de hecho, es una llamada de constructor), para tipos de clase sin constructor definido por el usuario, significa otra cosa (así se llama inicialización de valor ) para vacío ()) y para los tipos que no son de clase, nuevamente significa algo diferente (ya que los tipos que no son de clase no tienen constructores).
En su caso, el miembro de datos tiene tipo int. int no es un tipo de clase, por lo que no tiene constructor. por tipo int esta sintaxis significa simplemente “inicializar bar con el valor de num” y listo. Se hace así, directamente, sin constructores de por medio, ya que, una vez más, int no es un tipo de clase, por lo tanto, no puede tener ningún constructor.
marca rescate
No sé cómo podrías perderte este, es bastante básico. Esa es la sintaxis para inicializar variables miembro o constructores de clases base. Funciona para tipos de datos simples y antiguos, así como para objetos de clase.
Escrito en una línea dentro de la declaración de esa manera, es fácil no detectarlo como una lista de inicio
– Martín Beckett
10 de noviembre de 2009 a las 23:51
¿Ha sido útil esta solución?
Tu feedback nos ayuda a saber si la solución es correcta y está funcionando. De esta manera podemos revisar y corregir el contenido.
Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos.
Configurar y más información
Un “libro ordinario de c ++” que no menciona esto es probablemente un libro ac donde alguien pensó que “++” se vería bien en la portada …
– Rasmus Kaj
10 de noviembre de 2009 a las 23:35
“nunca encontrarás en un libro de C++ normal”. Oh. Estimado. Deseche su “libro ordinario de C++” ahora mismo. No por la ventana, alguien más podría recogerlo. De preferencia tritúrelo y póngalo a reciclar. ¿Hecho? Ahora consulte stackoverflow.com/questions/388242/… para obtener un nuevo libro.
–Steve Jessop
10 de noviembre de 2009 a las 23:36
Esta característica del lenguaje no es nada esotérica. Es una característica bastante importante de la construcción de objetos.
– Carlos Salvia
10 de noviembre de 2009 a las 23:36
De hecho, lejos de ser esotérico, a menudo no tienes más remedio que usar listas de inicializadores. Por ejemplo, si su clase contiene un
const
variable miembro, o una referencia, tiene que usar una lista de inicializadores.– Carlos Salvia
10 de noviembre de 2009 a las 23:42