Pregunta ¿Puedo llamar a un constructor desde otro constructor (do encadenamiento de constructor) en C ++?


Como un DO# desarrollador que estoy acostumbrado a ejecutar a través de constructores:

class Test {
    public Test() {
        DoSomething();
    }

    public Test(int count) : this() {
        DoSomethingWithCount(count);
    }

    public Test(int count, string name) : this(count) {
        DoSomethingWithName(name);
    }
}

¿Hay alguna manera de hacer esto en C ++?

Intenté llamar al nombre de la Clase y usar la palabra clave 'this', pero ambos fallaron.


756
2017-11-21 09:43


origen


Respuestas:


C ++ 11: ¡Sí!

C ++ 11 en adelante tiene esta misma característica (llamada delegar constructores)

La sintaxis es ligeramente diferente de C #:

class Foo {
public: 
  Foo(char x, int y) {}
  Foo(int y) : Foo('a', y) {}
};

C ++ 03: no

Desafortunadamente, no hay forma de hacerlo en C ++ 03, pero hay dos formas de simular esto:

  1. Puede combinar dos (o más) constructores a través de los parámetros predeterminados:

    class Foo {
    public:
      Foo(char x, int y=0);  // combines two constructors (char) and (char, int)
      // ...
    };
    
  2. Use un método init para compartir código común:

    class Foo {
    public:
      Foo(char x);
      Foo(char x, int y);
      // ...
    private:
      void init(char x, int y);
    };
    
    Foo::Foo(char x)
    {
      init(x, int(x) + 7);
      // ...
    }
    
    Foo::Foo(char x, int y)
    {
      init(x, y);
      // ...
    }
    
    void Foo::init(char x, int y)
    {
      // ...
    }
    

Ver la entrada de C ++ FAQ para referencia.


1020
2017-11-21 10:04



No, no puede llamar a un constructor de otro en C ++ 03 (llamado constructor delegante).

Esto cambió en C ++ 11 (también conocido como C ++ 0x), que agregó soporte para la siguiente sintaxis:
(ejemplo tomado de Wikipedia)

class SomeType
{
  int number;

public:
  SomeType(int newNumber) : number(newNumber) {}
  SomeType() : SomeType(42) {}
};

101
2017-11-21 10:00



Creo que puedes llamar a un constructor desde un constructor. Se compilará y se ejecutará. Recientemente vi a alguien hacer esto y funcionó tanto en Windows como en Linux.

Simplemente no hace lo que quieres. El constructor interno construirá un objeto local temporal que se eliminará una vez que el constructor externo regrese. Tendrían que ser diferentes constructores también o crearías una llamada recursiva.

Árbitro: https://isocpp.org/wiki/faq/ctors#init-methods


38
2017-08-12 15:31



Vale la pena señalar que poder llama al constructor de una clase padre en tu constructor, p. ej .:

class A { /* ... */ };

class B : public A
{
    B() : A()
    {
        // ...
    }
};

Pero no, no puedes llamar a otro constructor de la misma clase.


19
2017-11-22 06:36



En C ++ 11, un constructor puede llamar a otra sobrecarga de constructor:

class Foo  {
     int d;         
public:
    Foo  (int i) : d(i) {}
    Foo  () : Foo(42) {} //New to C++11
};

Además, los miembros se pueden inicializar así también.

class Foo  {
     int d = 5;         
public:
    Foo  (int i) : d(i) {}
};

Esto debería eliminar la necesidad de crear el método de ayuda de inicialización. Y todavía se recomienda no llamar a ninguna función virtual en los constructores o destructores para evitar el uso de cualquier miembro que no se pueda inicializar.


17
2017-09-22 20:07



Si quieres ser malo, puedes usar el operador "nuevo" in situ:

class Foo() {
    Foo() { /* default constructor deliciousness */ }
    Foo(Bar myParam) {
      new (this) Foo();
      /* bar your param all night long */
    } 
};

Parece funcionar para mi.

editar

Como @ElvedinHamzagic señala, si Foo contenía un objeto que asignó memoria, ese objeto podría no ser liberado. Esto complica las cosas aún más.

Un ejemplo más general:

class Foo() {
private:
  std::vector<int> Stuff;
public:
    Foo()
      : Stuff(42)
    {
      /* default constructor deliciousness */
    }

    Foo(Bar myParam)
    {
      this->~Foo();
      new (this) Foo();
      /* bar your param all night long */
    } 
};

Parece un poco menos elegante, seguro. La solución de @JohnIdol es mucho mejor.


11
2018-03-24 16:53



No, en C ++ no puedes llamar a un constructor desde un constructor. Lo que puedes hacer, como Warren señaló, es:

  • Sobrecargar el constructor, usando diferentes firmas
  • Use los valores predeterminados en los argumentos, para hacer que una versión "más simple" esté disponible

Tenga en cuenta que en el primer caso, no puede reducir la duplicación de código llamando a un constructor desde otro. Por supuesto, puede tener un método separado, privado / protegido, que haga toda la inicialización, y deje que el constructor se ocupe principalmente del manejo de argumentos.


7
2017-11-21 09:56



En Visual C ++ también puede usar esta notación dentro del constructor: this-> Classname :: Classname (parámetros de otro constructor). Ver un ejemplo a continuación:

class Vertex
{
 private:
  int x, y;
 public:
  Vertex(int xCoo, int yCoo): x(xCoo), y(yCoo) {}
  Vertex()
  {
   this->Vertex::Vertex(-1, -1);
  }
};

No sé si funciona en otro lado, solo lo probé en Visual C ++ 2003 y 2008. También puede llamar varios constructores de esta manera, supongo, al igual que en Java y C #.

P.S .: Francamente, me sorprendió que esto no se haya mencionado antes.


5
2018-02-20 10:00



Si entiendo su pregunta correctamente, ¿está preguntando si puede llamar a múltiples constructores en C ++?

Si eso es lo que estás buscando, entonces no, eso no es posible.

Ciertamente puede tener múltiples constructores, cada uno con firmas de argumento únicas, y luego llamar al que desea cuando crea una instancia de un nuevo objeto.

Incluso puede tener un constructor con argumentos predeterminados al final.

Pero puede que no tenga múltiples constructores, y luego llame a cada uno de ellos por separado.


2
2017-11-21 09:49



Otra opción que aún no se ha mostrado es dividir su clase en dos, envolviendo una clase de interfaz liviana alrededor de su clase original para lograr el efecto que está buscando:

class Test_Base {
    public Test_Base() {
        DoSomething();
    }
};

class Test : public Test_Base {
    public Test() : Test_Base() {
    }

    public Test(int count) : Test_Base() {
        DoSomethingWithCount(count);
    }
};

Esto podría complicarse si tiene muchos constructores que deben llamar a su contraparte de "siguiente nivel", pero para un puñado de constructores, debería ser viable.


2
2017-11-25 00:54



Yo propondría el uso de un private friend método que implementa la lógica de la aplicación del constructor y es el llamado por los diversos constructores. Aquí hay un ejemplo:

Supongamos que tenemos una clase llamada StreamArrayReader con algunos campos privados:

private:
    istream * in;
      // More private fields

Y queremos definir los dos constructores:

public:
    StreamArrayReader(istream * in_stream);
    StreamArrayReader(char * filepath);
    // More constructors...

Donde el segundo simplemente hace uso del primero (y por supuesto no queremos duplicar la implementación del primero). Idealmente, a uno le gustaría hacer algo como:

StreamArrayReader::StreamArrayReader(istream * in_stream){
    // Implementation
}

StreamArrayReader::StreamArrayReader(char * filepath) {
    ifstream instream;
    instream.open(filepath);
    StreamArrayReader(&instream);
    instream.close();
}

Sin embargo, esto no está permitido en C ++. Por esa razón, podemos definir un método de amigo privado de la siguiente manera que implemente lo que el primer constructor debe hacer:

private:
  friend void init_stream_array_reader(StreamArrayReader *o, istream * is);

Ahora este método (porque es un amigo) tiene acceso a los campos privados de o. Entonces, el primer constructor se convierte en:

StreamArrayReader::StreamArrayReader(istream * is) {
    init_stream_array_reader(this, is);
}

Tenga en cuenta que esto no crea copias múltiples para las copias recién creadas. El segundo se convierte en:

StreamArrayReader::StreamArrayReader(char * filepath) {
    ifstream instream;
    instream.open(filepath);
    init_stream_array_reader(this, &instream);
    instream.close();
}

Es decir, en lugar de tener un constructor llamando a otro, ¡ambos llaman a un amigo privado!


2
2017-11-02 00:19