Constructores delegados
Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy — lección 23 de 104.
Cuando tienes múltiples constructores que comparten una lógica de inicialización común, puedes usar constructores delegados para evitar la duplicación de código. Un constructor delegado llama a otro constructor de la misma clase en su lista de inicializadores.
class Player {
std::string name;
int health;
int level;
public:
// Constructor primario - realiza el trabajo real
Player(std::string n, int h, int lvl)
: name(n), health(h), level(lvl) {}
// Delega en el constructor primario
Player(std::string n) : Player(n, 100, 1) {}
// El constructor por defecto también delega
Player() : Player("Unknown") {}
};La delegación ocurre en la lista de inicializadores utilizando el nombre de la clase seguido de los argumentos. Cuando se llama a Player(), esta delega en Player(std::string), el cual luego delega en el constructor de tres parámetros. La cadena se ejecuta desde el constructor de destino de vuelta al original.
Hay una regla importante: un constructor delegado no puede tener otros miembros en su lista de inicialización. La delegación debe ser el único elemento:
class Item {
int id;
int value;
public:
Item(int i, int v) : id(i), value(v) {}
// ¡Error! No se puede mezclar la delegación con la inicialización de miembros
// Item(int i) : Item(i, 0), id(i) {}
// Correcto - solo delegación
Item(int i) : Item(i, 0) {}
};Los constructores delegados ayudan a centralizar la lógica de inicialización en un solo lugar, lo que hace que su código sea más fácil de mantener y menos propenso a errores por código duplicado.
Desafío
FácilConstruyamos un sistema de configuración de conexiones que demuestre cómo los constructores delegados pueden eliminar la duplicación de código encadenando las llamadas a los constructores.
Crearás dos archivos para organizar tu código:
Connection.h: Define una claseConnectionque represente una conexión de red con varias opciones de configuración. Tu clase debe tener:- Miembros privados:
host(string),port(int),timeout(int en segundos) ysecure(bool) - Un constructor primario que tome los cuatro parámetros e inicialice todos los miembros usando una lista de inicialización. Este constructor debe imprimir
"Connection configured: <host>:<port>" - Un constructor que tome solo
hostyportque delegue al constructor primario contimeoutpor defecto en30ysecurepor defecto enfalse - Un constructor que tome solo
hostque delegue al constructor de dos parámetros conportpor defecto en80 - Un constructor por defecto que delegue al constructor de un parámetro con
hostpor defecto en"localhost" - Un método
getInfo()que devuelva un string con el formato:"<host>:<port> (timeout: <timeout>s, secure: <yes/no>)"
- Miembros privados:
main.cpp: Demuestra la cadena de delegación creando conexiones usando diferentes constructores. Lee un nombre de host y un número de puerto de la entrada (cada uno en una línea separada), luego:- Crea una
Connectionusando el constructor por defecto - Crea una
Connectionusando solo el host de la entrada - Crea una
Connectionusando tanto el host como el puerto de la entrada - Crea una
Connectioncon el host de entrada, el puerto de entrada, un timeout de60y secure establecido entrue - Imprime
getInfo()para cada conexión, una por línea
- Crea una
La cadena de delegación fluye así: el constructor por defecto llama al constructor de un parámetro, el cual llama al constructor de dos parámetros, el cual llama al constructor primario de cuatro parámetros. Esto significa que el mensaje "Connection configured" se imprime una vez por cada objeto creado, siempre desde el constructor primario.
Para el estado de seguridad en getInfo(), imprime "yes" si secure es true, de lo contrario imprime "no".
Incluye tu archivo de cabecera en main.cpp usando #include "Connection.h".
Hoja de referencia
Un constructor delegado llama a otro constructor de la misma clase en su lista de inicialización para evitar la duplicación de código:
class Player {
std::string name;
int health;
int level;
public:
// Constructor primario
Player(std::string n, int h, int lvl)
: name(n), health(h), level(lvl) {}
// Delega en el constructor primario
Player(std::string n) : Player(n, 100, 1) {}
// El constructor por defecto también delega
Player() : Player("Unknown") {}
};La delegación ocurre en la lista de inicialización utilizando el nombre de la clase seguido de los argumentos. Los constructores pueden formar una cadena de delegación donde uno delega en otro.
Regla importante: Un constructor delegado no puede tener otros miembros en su lista de inicialización. La delegación debe ser el único elemento:
class Item {
int id;
int value;
public:
Item(int i, int v) : id(i), value(v) {}
// ¡Error! No se puede mezclar la delegación con la inicialización de miembros
// Item(int i) : Item(i, 0), id(i) {}
// Correcto - solo delegación
Item(int i) : Item(i, 0) {}
};Pruébalo tú mismo
#include <iostream>
#include <string>
#include "Connection.h"
using namespace std;
int main() {
// Leer la entrada
string host;
int port;
cin >> host;
cin >> port;
// TODO: Crear una Connection usando el constructor por defecto
// TODO: Crear una Connection usando solo el host de la entrada
// TODO: Crear una Connection usando tanto el host como el port de la entrada
// TODO: Crear una Connection con el host de entrada, el port de entrada, timeout=60, secure=true
// TODO: Imprimir getInfo() para cada conexión, una por línea
return 0;
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosConstrucción y compilación en C++Archivos de cabecera y archivos fuenteNamespaces y alcanceIntroducción a OOP en C++Clases vs ObjetosEl puntero 'this'Métodos (Funciones miembro)Atributos (Miembros de datos)Conceptos básicos de Ctors y DtorsResumen - Calculadora simple4Propiedades de clase
Miembros de instancia vs. estáticosGetters y SettersFunciones miembro constPalabra clave mutableMétodos y variables estáticosFunciones y clases amigasResumen - Gestor de cuentas bancarias7Herencia
Herencia básicaNiveles de acceso en la herenciaOrden de llamada de Ctor y DtorSobrescritura de métodosFunciones virtuales y VTableHerencia múltipleHerencia virtualResumen - Jerarquía de empleados2Gestión de memoria
Memoria Stack vs HeapPunteros y referenciasMemoria dinámica (new/delete)Punteros inteligentes en C++RAII en C++Resumen - Gestor de arrays dinámicos5Encapsulamiento
Especificadores de acceso en C++Especificadores de acceso en profundidadOcultamiento de informaciónStruct vs ClassClases anidadas e internasResumen - Sistema de registros de estudiantes8Polimorfismo
Polimorfismo: Compilación vs. Tiempo de ejecuciónSobrecarga de funcionesFunciones virtuales revisadasFunciones virtuales purasClases abstractasDiseño de interfaces en C++Dynamic Casting y RTTIResumen: Calculadora de figuras11Conceptos avanzados de POO
Composición vs. HerenciaMixins mediante CRTPIdioma PimplBorrado de tiposEnum Classes y tipado fuerteManejo de excepciones en POOJerarquías de excepciones personalizadas14Patrones de diseño - Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositeRAII como patrón3Constructores y Destructores
Constructor por defectoConstructor parametrizadoConstructor de copiaConstructor de movimientoListas de inicialización del constructorConstructores delegadosAnálisis profundo del destructorRegla de tres / cinco / ceroResumen - Clase String6Sobrecarga de operadores
Introducción a la sobrecarga de operadoresSobrecarga de operadores aritméticosSobrecarga de operadores de comparaciónOperadores de flujo (Stream)Sobrecarga del operador de asignaciónSobrecarga de los operadores [] y ()Operadores de conversión de tiposResumen - Clase Matrix9Plantillas
Plantillas de funcionesPlantillas de clasesEspecialización de plantillasPlantillas variádicasConceptos básicos de SFINAE y Type TraitsResumen - Contenedor genérico