¿Para qué sirve la lista de variables miembro después de los dos puntos en un constructor?

StackOverflow https://stackoverflow.com/questions/210616

  •  03-07-2019
  •  | 
  •  

Pregunta

Estoy leyendo este código fuente abierto de C ++ y llegué a un constructor pero no lo entiendo (básicamente porque no sé C ++: P)

Entiendo C y Java muy bien.

 TransparentObject::TransparentObject( int w, int x, int y, int z ) : 
     _someMethod( 0 ),
     _someOtherMethod( 0 ),
     _someOtherOtherMethod( 0 ),
     _someMethodX( 0 ) 
  {
       int bla;
       int bla;
  }

Hasta donde puedo "deducir" La primera línea solo declara el nombre del constructor, el " :: " suena como " pertenece a " a mi. Y el código entre {} es el cuerpo del constructor.

I " think " ¿Qué hay después de los parámetros y el primer " {" son como los parámetros predeterminados de los métodos o algo así, pero no encuentro una explicación razonable en la web. La mayoría de los constructores de C ++ que encontré en los ejemplos son casi idénticos a los de Java.

¿Estoy en lo cierto en mis suposiciones? " :: " es como pertenece a, y la lista después de los parámetros y el cuerpo son como "argumentos predeterminados" o algo?

ACTUALIZACIÓN: Gracias por las respuestas ¿Pueden ser llamados métodos? (Supongo que no) y cuál es la diferencia de llamarlos dentro del cuerpo del constructor

¿Fue útil?

Solución

El caso más común es este:

class foo{
private:
    int x;
    int y;
public:
    foo(int _x, int _y) : x(_x), y(_y) {}
}

Esto establecerá x y y en los valores que se dan en _x y _y en el constructor parámetros Esta es a menudo la mejor manera de construir cualquier objeto que se declare como miembro de datos.

También es posible que estuvieras mirando el encadenamiento del constructor:

class foo : public bar{
    foo(int x, int y) : bar(x, y) {}
};

En este caso, el constructor de la clase llamará al constructor de su clase base y pasará los valores x y y .

Para diseccionar la función aún más:

TransparentObject::TransparentObject( int w, int x, int y, int z ) : 
   _someMethod( 0 ),
   _someOtherMethod( 0 ),
   _someOtherOtherMethod( 0 ),
   _someMethodX( 0 ) 
{
     int bla;
     int bla;
}

El operador :: se llama operador de resolución de alcance. Básicamente indica que TransparentObject es miembro de TransparentObject . En segundo lugar, tiene razón al suponer que el cuerpo del constructor se encuentra en las llaves.

  

ACTUALIZACIÓN: Gracias por las respuestas. ¿Pueden ser llamados métodos? (Supongo que no) y cuál es la diferencia de llamarlos dentro del cuerpo del constructor

Hay mucha más información sobre este tema de la que podría darle aquí . El área más común donde debe usar las listas de inicializadores es cuando está inicializando una referencia o un const , ya que a estas variables se les debe dar un valor inmediatamente después de la creación.

Otros consejos

Estás bastante cerca. La primera línea es la declaración. La etiqueta a la izquierda de :: es el nombre de la clase y para que sea un constructor, el nombre de la función debe ser el mismo que el nombre de la clase.

TransparentObject::TransparentObject( int w, int x, int y, int z )

En C ++, opcionalmente, puede poner dos puntos y algunos valores iniciales para las variables miembro antes del inicio del cuerpo de la función. Esta técnica debe usarse si está inicializando cualquier variable const o pasando parámetros a un constructor de superclase.

: 
 _someMethod( 0 ),
 _someOtherMethod( 0 ),
 _someOtherOtherMethod( 0 ),
 _someMethodX( 0 )

Y luego viene el cuerpo del constructor entre llaves.

{
   int bla;
   int bla;
}

:: En realidad significa que contiene (ver comentarios para aclaraciones), sin embargo, _someMethods y demás es lo que se llama lista de inicialización . Hay mucha información en el enlace =]

EDITAR: Lo siento, mi primera oración es incorrecta; vea los comentarios.

Sí, :: es el operador de alcance de C ++ que le permite decirle al compilador a qué pertenece la función. Usando a: después de que la declaración del constructor comienza lo que se llama una lista de inicialización.

El código entre la lista de argumentos y los {} s especifica la inicialización de (algunos de) los miembros de la clase.

Inicialización en lugar de asignación --- son cosas diferentes --- así que todas estas son llamadas a constructores.

Estás en lo correcto. Es una forma de establecer los valores predeterminados para las variables de clase. No estoy muy familiarizado con la diferencia exacta entre ponerlos después: y en el cuerpo de la función.

Generalmente hay algunas buenas razones para usar una lista de inicialización. Por un lado, no puede establecer variables miembro que sean referencias fuera de la lista de inicialización del constructor. Además, si una variable miembro necesita ciertos argumentos para su propio constructor, debe pasarlos aquí. Compare esto:

class A
{
public:
  A();
private:
  B _b;
  C& _c;
};

A::A( C& someC )
{
  _c = someC; // this is illegal and won't compile. _c has to be initialized before we get inside the braces
  _b = B(NULL, 5, "hello"); // this is not illegal, but B might not have a default constructor or could have a very 
                            // expensive construction that shouldn't be done more than once
}

a esta versión:

A::A( C& someC )
: _b(NULL, 5, "hello") // ok, initializing _b by passing these arguments to its constructor
, _c( someC ) // this reference to some instance of C is correctly initialized now
{}

Sin usar la lista de inicializadores, todos los miembros de la clase simplemente tendrán su constructor predeterminado llamado, por lo que este es el único lugar donde puede controlar el constructor que se llama (para miembros no asignados dinámicamente). Lo mismo es cierto para qué constructor de clase padre se llamará.

Miembros de la clase " inicializados " dentro del cuerpo del constructor (es decir, entre las llaves {} usando el operador =) no es técnicamente una inicialización, es una asignación. Para las clases con un constructor / destructor no trivial, puede ser costoso construir por defecto y luego modificar mediante la asignación de esta manera. Para los miembros de referencia, debe usar la lista de inicializadores, ya que no se pueden cambiar a través del operador de asignación.

Si el miembro (o la clase principal) no tiene un constructor predeterminado, no especificar un constructor apropiado en la lista de inicializadores hará que el compilador genere un error. De lo contrario, el compilador insertará el constructor predeterminado que se llama a sí mismo. Para los tipos integrados, esto no hace nada, por lo que tendrá valores de basura allí.

Tenga en cuenta que el orden en el que especifica los miembros en la lista de inicializadores no afecta el orden en que se llaman . Siempre es el constructor de la clase principal (si existe) primero, luego los miembros de la clase en el orden en que se definen en la definición de clase. El orden en que los coloque en la lista de inicializadores no importa y puede ser la fuente de errores sutiles ...

En el siguiente ejemplo artificial parece que la intención es inicializar m_b con value y luego m_a con m_b , pero lo que realmente sucede es que m_a se inicializa con m_b (que aún no se ha inicializado) y luego m_b se inicializa con valor . ¡ m_b solo contendrá basura!

struct BadInitialiserListExample
{
    BadInitialiserListExample(int value) :
        m_b(value),
        m_a(m_b)      // <-- *BUG* this is actually executed first due to ordering below!
    {
    }

    int    m_a;
    int    m_b;
};
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top