c++ - sobrecarga - tipos de constructores en java




¿Puedo llamar a un constructor desde otro constructor(hacer encadenamiento de constructores) en C++? (10)

Como desarrollador de C# estoy acostumbrado a ejecutar constructores:

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

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

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

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

Intenté llamar el nombre de la Clase y usar la palabra clave 'this', pero ambas fallan.


¿Si entiendo su pregunta correctamente, me pregunta si puede llamar a varios constructores en C ++?

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

Ciertamente, puede tener varios constructores, cada uno con firmas de argumentos únicos, y luego llamar al que desea cuando crea una instancia de un nuevo objeto.

Incluso puedes tener un constructor con argumentos predeterminados al final.

Pero es posible que no tenga varios constructores y luego llame a cada uno de ellos por separado.


Al llamar a un constructor, en realidad asigna memoria, ya sea desde la pila o desde el montón. Así que llamar a un constructor en otro constructor crea una copia local. Así que estamos modificando otro objeto, no el que estamos enfocando.


En C++11 , un Wikipedia :

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

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

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 aún se recomienda no llamar a ninguna función virtual en los constructores o destructores para evitar el uso de miembros que no puedan inicializarse.


En Visual C ++ también puede usar esta notación dentro del constructor: this-> Classname :: Classname (parámetros de otro constructor). Vea 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 lugar, solo lo probé en Visual C ++ 2003 y 2008. También podría llamar a varios constructores de esta manera, supongo, al igual que en Java y C #.

PD: Francamente, me sorprendió que esto no haya sido mencionado anteriormente.


Este enfoque puede funcionar para algunos tipos de clases (cuando el operador de asignación se comporta "bien"):

Foo::Foo()
{
    // do what every Foo is needing
    ...
}

Foo::Foo(char x)
{
    *this = Foo();

    // do the special things for a Foo with char
    ...
}

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

  • Sobrecarga el constructor, utilizando diferentes firmas.
  • Use 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 realice toda la inicialización, y deje que el constructor se ocupe principalmente del manejo de argumentos.


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 causar problemas si tiene muchos constructores que deben considerar su contraparte de "siguiente nivel", pero para un puñado de constructores, debería ser viable.


Propondría el uso de un método de private friend que implementa la lógica de 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 implementa lo que se supone que debe hacer el primer constructor:

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 varias copias 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 que un constructor llame a otro, ¡ambos llaman a un amigo privado!


Si quieres ser malo, puedes usar el operador "nuevo" en el lugar:

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

Parece funcionar para mi.

editar

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

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.


Vale la pena señalar que puede llamar al constructor de una clase padre en su constructor, por ejemplo:

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

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

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





constructor