Es posible sobrecargar la función constructora de una clase, en cambio, no lo es con una función destructora. La necesidad de esta sobrecarga se fundamenta en tres razones: ganar flexibilidad, permitir arrays y crear constructores de copia.
Uno de los usos más frecuentes de las funciones constructoras sobrecargadas es el de ofrecer la opción de inicializar o no un objeto. Por ejemplo, en el siguiente ejemplo, o1 recibe un valor inicial y o2 no. Si se elimina el constructor que tiene la lista de argumentos vacía, el programa no se compilará porque no hay ningún constructor que coincida con un objeto sin inicializar de tipo ?muestra???. Lo contrario también es cierto: Si se elimina un constructor parametrizado, el programa no compilará porque no hay un modelo para el objeto inicializado. Se necesitan ambos para que el programa compile correctamente.
using namespace std;
class miclase {
int x;
public:
// dos formas de sobrecarga de un constructor
miclase() {x=0;} // sin inicializador
miclase(int n) {x=n;} // inicializador
int obtenerx() {return x;}
};
int main(int argc, char *argv[])
{
miclase o1(10); //declaración con valor inicial
miclase o2; // declaración sin inicializador
cout << «o1: « << o1.obtenerx() << endl;
cout << «o2: « << o2.obtenerx() << endl;
system(«PAUSE»);
return EXIT_SUCCESS;
}
Otra razón típica por la que se sobrecarga un constructor es para permitir que tanto objetos como arrays de objetos aparezcan dentro de un programa. Para inicializar arrays de objetos sin inicializar, junto con objetos inicializados, debe incluirse un constructor que permita la inicialización y otro no.
Por ejemplo, partiendo de la clase anterior, estas dos declaraciones son válidas:
Miclase ob(10);
Miclase ob(5);
El hecho de proporcionar constructores de inicialización y de no inicialización da lugar a que las variables puedan inicializarse o no, según se necesite. Por ejemplo, este programa declara dos arrays del tipo miclase: uno inicializado y otro no:
#include <iostream>
using namespace std;
class miclase {
int x;
public:
// dos formas de sobrecarga de un constructor
miclase() {x=0;} // sin inicializador
miclase(int n) {x=n;} // inicializador
int obtenerx() {return x;}
};
int main(int argc, char *argv[])
{
miclase o1[10]; //declaración del array sin inicializadores
// declaración con inicializadores
miclase o2[10] = {1,2,3,4,5,6,7,8,9,10};
int i;
for (i=0; i<10; i++){
cout << «o1[« << i << «]: « << o1[i].obtenerx() << endl;
cout << «o2[« << i << «]: « << o2[i].obtenerx() << endl;
}
system(«PAUSE»);
return EXIT_SUCCESS;
}
En este ejemplo, la función constructora pone a cero todos los elementos de o1. Los elementos de o2 se inicializan según se muestra en el programa.
Otra razón para sobrecargar los constructores es permitir que el programador seleccione el método más conveniente de inicializar un objeto. Para ver cómo se lleva a cabo, primero veamos el siguiente ejemplo, que crea una clase que contiene las fechas de un calendario. El constructor fecha() se sobrecarga de dos formas: en una de ellas, aceptando como una cadena de caracteres la fecha y en la otra, pasándola como tres enteros.
#include <iostream>
#include <stdio.h> // Para usar sscanf
using namespace std;
class fecha {
int dia, mes, anyo;
public:
fecha(char *cad);
fecha(int d, int m, int a) {
this->dia=d;
this->mes=m;
this->anyo=a;
}
void mostrar(){
cout << dia << ‘/’ << mes << ‘/’ << anyo << endl;
}
};
fecha::fecha(char *cad)
{
sscanf(cad, «%d%*c%d%*c%d»,&dia, &mes, &anyo);
}
int main(int argc, char *argv[])
{
// construcción del objeto fecha usando una cadena
fecha sfecha(«1/11/95»);
// construcción del objeto fecha usando enteros
fecha ifecha(1,11,95);
sfecha.mostrar();
ifecha.mostrar();
system(«PAUSE»);
return EXIT_SUCCESS;
}
Aunque es posible sobrecargar un constructor tantas veces como queramos, realizarlo en exceso tiene un efecto destructivo sobre la clase. Desde el punto de vista del estilo, lo más adecuado es sobrecargar un constructor para mejorar solamente aquellas situaciones con alta probabilidad de ocurrir frecuentemente. Por ejemplo, sobrecargar fecha() una tercera vez de manera que se pueda introducir la fecha como enteros en octal, no tiene mucho sentido. Sin embargo, si lo tiene sobrecargarla para aceptar un objeto time_t, lo que podría ser muy útil.
Existe otra situación donde será necesario sobrecargar el constructor: cuando se asigna un array dinámico de esa clase. Ya que un array dinámico no puede inicializarse, si la clase contiene un constructor que tiene un inicializador, debe incluirse una versión sobrecargada que no tenga inicializador. Por ejemplo, a continuación vemos un programa que asigna un array de objetos dinámicamente:
#include <iostream>
using namespace std;
class miclase {
int x;
public:
// dos formas de sobrecargar un constructor
miclase() {x=0;}
miclase(int n){x=n;}
int obtenerx() {return x;}
void ajustarx(int n){x=n;}
};
int main(int argc, char *argv[])
{
miclase *p;
miclase ob(10); // inicialización de una única variable
p = new miclase[10]; // aquí no pueden usarse inicializadores
if (!p){
cout << «Error de asignación\n»;
return 1;
}
int i;
// inicialización de todos los elementos de ob
for(i=0;i<10;i++) p[i]=ob;
for (i=0;i<10;i++){
cout << «p[« << i << «]: « <<p[i].obtenerx() << endl;
}
system(«PAUSE»);
return EXIT_SUCCESS;
}
Sin la versión sobrecargada de miclase() que no tiene inicializador, la sentencia new habría generado un error en tiempo de compilación.
* Transcrito del libro de Herbert Schildt sobre Programación en C++