por - sobrecarga de constructores c++
¿Puedo llamar a un constructor desde otro constructor(hacer encadenamiento de constructores) en C++? (15)
¿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.
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.
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.
Creo que puedes llamar a un constructor desde un constructor. Se compilará y correrá. 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.
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.
En pocas palabras, no puedes antes de C ++ 11.
C ++ 11 introduce constructores delegantes :
Constructor delegante
Si el nombre de la clase en sí aparece como clase o identificador en la lista de inicializadores de miembros, entonces la lista debe constar de ese solo inicializador de miembros; dicho constructor se conoce como el constructor delegante, y el constructor seleccionado por el único miembro de la lista de inicializadores es el constructor de destino
En este caso, el constructor de destino se selecciona por resolución de sobrecarga y se ejecuta primero, luego el control vuelve al constructor delegante y se ejecuta su cuerpo.
Los constructores delegantes no pueden ser recursivos.
class Foo { public: Foo(char x, int y) {} Foo(int y) : Foo(''a'', y) {} // Foo(int) delegates to Foo(char,int) };
Tenga en cuenta que un constructor delegante es una propuesta de todo o nada; si un constructor delega a otro constructor, el constructor que llama no puede tener otros miembros en su lista de inicialización. Esto tiene sentido si piensas en inicializar los miembros const / reference una vez, y solo una vez.
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.
No, no puede llamar a un constructor desde 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) {}
};
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!
Sería más fácil de probar, que decidir :) Prueba esto:
#include <iostream>
class A {
public:
A( int a) : m_a(a) {
std::cout << "A::Ctor" << std::endl;
}
~A() {
std::cout << "A::dtor" << std::endl;
}
public:
int m_a;
};
class B : public A {
public:
B( int a, int b) : m_b(b), A(a) {}
public:
int m_b;
};
int main() {
B b(9, 6);
std::cout << "Test constructor delegation a = " << b.m_a << "; b = " << b.m_b << std::endl;
return 0;
}
y compílelo con 98 std: g ++ main.cpp -std = c ++ 98 -o test_1
ya verás:
A::Ctor
Test constructor delegation a = 9; b = 6
A::dtor
asi que :)
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.
C ++ 11: ¡Sí!
C ++ 11 y en adelante tiene esta misma característica (llamada constructores delegantes ).
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 manera de hacer esto en C ++ 03, pero hay dos formas de simular esto:
Puede combinar dos (o más) constructores a través de parámetros predeterminados:
class Foo { public: Foo(char x, int y=0); // combines two constructors (char) and (char, int) // ... };
Utilice un método de inicio 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) { // ... }
Vea la entrada de Preguntas frecuentes de C ++ para referencia.