¿Qué es esta extraña sintaxis de miembro de dos puntos (” : “) en el constructor?

11 minutos de lectura

¿Que es esta extrana sintaxis de miembro de dos puntos
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

¿Que es esta extrana sintaxis de miembro de dos puntos
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

1647664392 715 ¿Que es esta extrana sintaxis de miembro de dos puntos
james mcnellis

Es un lista de inicialización de miembros. Debería encontrar información al respecto en cualquier buen libro de C++.

En la mayoría de los casos, debe inicializar todos los objetos de miembro en la lista de inicialización de miembros (sin embargo, tenga en cuenta las excepciones enumeradas al final de la entrada de preguntas frecuentes).

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


1647664392 720 ¿Que es esta extrana sintaxis de miembro de dos puntos
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

¿Que es esta extrana sintaxis de miembro de dos puntos
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.

1647664393 539 ¿Que es esta extrana sintaxis de miembro de dos puntos
逆さま

Esto no es oscuro, es el Sintaxis de la lista de inicialización de C++

Básicamente, en su caso, x se inicializará con _x, y con _y, z con _z.

1647664394 561 ¿Que es esta extrana sintaxis de miembro de dos puntos
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.

1647664394 349 ¿Que es esta extrana sintaxis de miembro de dos puntos
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?

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
Privacidad