«De un constructor a otro»: la eterna pregunta del desarrollador de C++
\n\nImagina la situación: estás escribiendo una clase con varios constructores. Uno acepta tres parámetros, otro dos, otro uno. Y en todos se repite la misma lógica de inicialización. Te pica la mano escribir algo como MyClass() : MyClass(42, "default") {} — y olvidarte de la duplicación. Pero el compilador de C++ (hasta cierto punto) lo mira con recelo. ¿Se puede realmente llamar a un constructor desde otro constructor? Y si es así, ¿cómo?
Esta pregunta es una de las más populares en Stack Overflow (decenas de miles de visitas). No es de extrañar: el tema de las cadenas de constructores (constructor chaining) es un verdadero escollo para principiantes e incluso desarrolladores experimentados que migran a C++ desde Java o Python. En este artículo analizaremos tres enfoques para resolver el problema, desde «parches» hasta soluciones elegantes de C++11, y descubriremos cuál vale la pena usar en código real.
\n\n¿Por qué no se puede simplemente llamar a un constructor desde otro?
\n\nEl enfoque ingenuo se ve así:
\n\nclass MyClass {\npublic:\n MyClass() {\n // algún código\n }\n MyClass(int x) {\n MyClass(); // ERROR: ¡no se puede!\n }\n};\n\nEn C++, un constructor no es una función normal. Inicializa el objeto una sola vez. Si intentas llamar explícitamente a otro constructor dentro del cuerpo, el compilador lo interpretará como la creación de un objeto temporal — y no se inicializará nada en la instancia actual. Resultado: o un error de compilación o (peor aún) comportamiento indefinido.
\n\nAntes del estándar C++11, la única forma legal de «compartir» la lógica de inicialización era llamar a un método privado init(). Pero esto no era una verdadera delegación: los miembros de la clase se inicializaban primero con el constructor por defecto y luego se sobrescribían. Ineficiente y propenso a errores.
Enfoque n.º 1: constructores delegados (C++11 y posteriores)
\n\nA partir de C++11, el lenguaje incorporó una función muy esperada: los constructores delegados (delegating constructors). Ahora se puede llamar a un constructor desde otro directamente en la lista de inicialización:
\n\nclass MyClass {\n int value;\n std::string name;\npublic:\n // Constructor base\n MyClass(int v, const std::string& n) : value(v), name(n) {\n std::cout << "Full init: " << value << ", " << name << "\\n";\n }\n // Constructor delegado\n MyClass(int v) : MyClass(v, "default") {}\n // Otro constructor delegado\n MyClass() : MyClass(0, "default") {}\n};\n\n¿Cómo funciona? El constructor MyClass(int v) pasa el control al constructor MyClass(int, string). Este realiza toda la inicialización, y solo después el control regresa. Regla importante: en la lista de inicialización no se puede mezclar la llamada a otro constructor con la inicialización directa de miembros. O delegación o inicialización propia, una de las dos.
Ventajas y desventajas
\n- \n
- ✅ Elegancia: el código queda limpio, sin duplicación. \n
- ✅ Rendimiento: el objeto se inicializa una sola vez, sin operaciones innecesarias. \n
- ⚠️ Limitación: la delegación solo funciona en un solo «nivel»: la cadena no puede ser recursiva (provocarías un desbordamiento de pila). \n
- ⚠️ Compatibilidad: requiere C++11 o superior. Si estás atascado con un compilador antiguo, esta opción no es para ti. \n
Enfoque n.º 2: método privado init()
\n\nLa forma clásica «pre-C++11» es extraer la lógica común a un método privado. Se ve así:
\n\nclass MyClass {\n int value;\n std::string name;\n void init(int v, const std::string& n) {\n value = v;\n name = n;\n std::cout << "Init called\\n";\n