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


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 este extraño : bar(num)? De alguna manera parece inicializar la variable miembro, pero nunca he visto esta sintaxis antes. Parece una llamada de función / constructor pero para un int? No tiene sentido para mí. Tal vez alguien podría iluminarme. Y, por cierto, ¿hay otras características del lenguaje esotérico como esta, que nunca encontrarás en un libro de C++ común?

Author: Taryn, 2009-11-11

12 answers

Es una lista de inicialización de miembros . Usted debe encontrar información al respecto en cualquier buen libro de C++ .

En la mayoría de los casos, debe inicializar todos los objetos 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,

Si todas las demás cosas son iguales, su código se ejecutará más rápido si usa listas de inicialización en lugar de asignación.

 167
Author: James McNellis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-15 17:16:33
Foo(int num): bar(num)    

Esta construcción se llama Lista Inicializadora de Miembros en C++.

Dicho simplemente, inicializa su 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 inicializador de miembros y asignándole un valor dentro del cuerpo del constructor.

Cuando inicializar los constructores serán llamados una vez y el objeto será construido e inicializado en una operación.

Si usa 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 ves, 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 un miembro de clase incorporado (su ejemplo de código) o POD no hay una sobrecarga práctica.


¿Cuándo TIENE QUE usar la lista de inicializadores de miembros?

Tendrá (bastante forzado) a usar una lista Inicializadora de Miembros si:

  • Su clase tiene un miembro de referencia
  • Su clase tiene un miembro const no estático o
  • El miembro de la clase no tiene un constructor predeterminado o
  • Para la inicialización de los 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 NECESARIO)

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 member lista de inicializadores.
  • La clase base MyClass no tiene un constructor predeterminado, por lo que para inicializar su miembro uno necesitará usar la Lista Inicializador de miembros.

Puntos importantes a tener en cuenta al usar Listas Inicializadoras de Miembros:

Las variables miembro de la clase siempre se inicializan en el orden en que se declaran en la clase.

Son no inicializados en el orden en que se especifican en el Inicializador de miembros Lista.
En resumen, la lista de inicialización de miembros no determina el orden de inicialización.

Dado lo anterior, siempre es una buena práctica mantener el mismo orden de miembros para la inicialización de Miembros que el orden en el 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 del inicializador de miembros como el orden de inicialización y escribir algún código dependiente de que.

 264
Author: Alok Save,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-10-08 16:01:16

Eso es 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.

 14
Author: Josh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-10 23:34:18

Esto se llama una lista de inicialización. Es una forma alternativa de inicializar miembros de 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 deben ser inicializados.

 11
Author: LeopardSkinPillBoxHat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-04 14:27:49

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

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

 9
Author: wkl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-04 16:57:10

El otro ya le explicó que la sintaxis que observa se llama "constructor initializer list". Esta sintaxis le permite inicializar de forma personalizada subobjetos base y subobjetos miembro de la clase (en lugar de permitirles inicializar por defecto o permanecer sin inicializar).

Solo quiero señalar que la sintaxis que, como usted dijo, "se parece a una llamada de constructor", no es necesariamente una llamada de constructor. En el lenguaje C++ la sintaxis () es solo una forma estándar de sintaxis de inicialización. Se interpreta de manera diferente para diferentes tipos. Para los tipos de clase con constructor definido por el usuario significa una cosa (es de hecho una llamada constructor), para los tipos de clase sin constructor definido por el usuario significa otra cosa (llamado inicialización de valores ) para empty ()) y para tipos que no son de clase significa otra vez algo diferente (ya que los tipos que no son de clase no tienen constructores).

En su caso, el miembro de datos tiene el tipo int. int no es un tipo de clase, por lo que no tiene constructor. Para type int esta sintaxis significa simplemente "inicializar bar con el valor de num" y eso es todo. Se hace así, directamente, sin constructores involucrados, ya que, una vez más, int no es un tipo de clase de por lo tanto no puede tener ningún constructor.

 8
Author: AnT,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-11 16:25:48

No se como puedes perderte este, es bastante básico. Esa es la sintaxis para inicializar variables miembro o constructores de clase base. Funciona para tipos de datos antiguos, así como para objetos de clase.

 7
Author: Mark Ransom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-10 23:32:54

Esta es una lista de inicialización. Inicializará los miembros antes de que se ejecute el cuerpo del constructor. Considere

class Foo {
 public:
   string str;
   Foo(string &p)
   {
      str = p;
   };
 };

Vs

class Foo {
public:
  string str;
  Foo(string &p): str(p) {};
};

En el primer ejemplo, str será inicializado por su constructor sin argumento

string();

Antes del cuerpo del constructor Foo. Dentro del constructor foo, el

string& operator=( const string& s );

Se llamará en ' str ' como lo haces str = p;

Whereas en el segundo ejemplo, str será inicializado directamente por llamando a su constructor

string( const string& s );

Con 'p' como argumento.

 6
Author: nos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-10 23:42:06

Tiene razón, esta es de hecho una forma de inicializar variables miembro. No estoy seguro de que haya mucho beneficio en esto, aparte de expresar claramente que es una inicialización. Tener un "bar = num" dentro del código podría moverse, borrarse o malinterpretarse mucho más fácilmente.

 5
Author: Aric TenEyck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-10 23:32:53

Hay otro 'beneficio'

Si el tipo de variable miembro no admite inicialización nula o si es una referencia (que no puede ser inicializada null), entonces no tiene más opción que proporcionar una lista de inicialización

 5
Author: pm100,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-10 23:46:41

Es una lista de inicialización para el constructor. En lugar de construir por defecto x, y y z y luego asignándoles los valores recibidos en los parámetros, esos miembros se inicializarán con esos valores de inmediato. Esto puede no parecer terriblemente útil para floats, pero puede ser bastante ahorrador de tiempo con clases personalizadas que son costosas de construir.

 5
Author: suszterpatt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-11 19:58:10

No se menciona aún en este hilo: desde C++11, la lista de inicializadores de miembros puede usar inicialización de lista (también conocida como list-initialization. "inicialización uniforme","inicialización arriostrada"):

Foo(int num): bar{num} {}

Que tiene la misma semántica que la inicialización de listas en otros contextos.

 1
Author: M.M,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-29 23:07:29