Curso de C++ (Página 30)

pagina029 Principal pagina031

CAPITULO 30 Constructores

Los constructores son funciones miembro especiales que sirven para inicializar un objeto de una determinada clase cuando se declara.

Los constructores tienen el mismo nombre que la clase, no retornan ningún valor y no pueden ser heredados. Además deben ser públicos, no tendría ningún sentido declarar un constructor como privado, ya que siempre se usan desde el exterior de la clase, ni tampoco como protegido, ya que no puede ser heredado.

Añadamos un constructor a nuestra clase pareja:

#include <iostream.h> 
 
class pareja
{
   public:
      // Constructor
      pareja(int a2, int b2);
      // Funciones miembro de la clase "pareja"
      void Lee(int &a2, int &b2);
      void Guarda(int a2, int b2);
   private:
      // Datos miembro de la clase "pareja"
      int a, b; 
   public:
};

pareja::pareja(int a2, int b2)
{
   a = a2;
   b = b2;
}

void pareja::Lee(int &a2, int &b2)
{
   a2 = a;
   b2 = b;
}

void pareja::Guarda(int a2, int b2)
{
   a = a2;
   b = b2;
}

int main(int argc, char *argv[])
{
   pareja par1(12, 32);
   int x, y;
   
   par1.Lee(x, y);
   cout << "Valor de par1.a: " << x << endl;
   cout << "Valor de par1.b: " << y << endl;

   return 0;
}

Si una clase posee constructor, será llamado siempre que se declare un objeto de esa clase, y si requiere argumentos, es obligatorio suministrarlos.

Por ejemplo, las siguientes declaraciones son ilegales:

pareja par1;
pareja par1();

Y las siguientes declaraciones son válidas:

pareja par1(12,43);
pareja par1 = pareja(12,43);

Cuando no especifiquemos un constructor para una clase, el compilador crea uno por defecto sin argumentos. Por eso el ejemplo del capítulo anterior funcionaba correctamente. Cuando se crean objetos locales, los datos miembros no se inicializarían, contendrían la "basura" que hubiese en la memoria asignada al objeto. Si se trata de objetos globales, los datos miembros se inicializan a cero.

Sobrecarga de constructores:

Además, también pueden definirse varios constructores para cada clase, es decir, la función constructor puede sobrecargarse. La única limitación es que no pueden declararse varios constructores con el mismo número y el mismo tipo de argumentos.

Por ejemplo, añadiremos un constructor adicional a la clase "pareja" que simule el constructor por defecto:

class pareja
{
   public:
      // Constructor
      pareja(int a2, int b2);
      pareja();
      // Funciones miembro de la clase "pareja"
      void Lee(int &a2, int &b2);
      void Guarda(int a2, int b2);
   private:
      // Datos miembro de la clase "pareja"
      int a, b; 
   public:
};

pareja::pareja(int a2, int b2)
{
   a = a2;
   b = b2;
}

pareja::pareja()
{
   a = b = 0;
}

De este modo podemos declarar objetos de la clase pareja especificando los dos argumentos o ninguno de ellos, en este último caso se inicializarán los datos miembros con ceros.

Constructores con argumentos por defecto:

También pueden asignarse valores por defecto a los argumentos del constructor, de este modo reduciremos el número de constructores necesarios.

Para resolver el ejemplo anterior sin sobrecargar el constructor suministraremos valores por defecto nulos a ambos parámetros:

class pareja
{
   public:
      // Constructor
      pareja(int a2=0, int b2=0);
      // Funciones miembro de la clase "pareja"
      void Lee(int &a2, int &b2);
      void Guarda(int a2, int b2);
   private:
      // Datos miembro de la clase "pareja"
      int a, b; 
   public:
};

pareja::pareja(int a2, int b2)
{
   a = a2;
   b = b2;
}

Asignación de objetos:

Probablemente ya lo imaginas, pero la asignación de objetos también está permitida. Y además funciona como se supone que debe hacerlo, asignando los valores de los datos miembros.

Con la definición de la clase del último ejemplo podemos hacer lo que se ilustra en el siguiente:

int main(int argc, char *argv[])
{
   pareja par1(12, 32), par2;
   int x, y;
   
   par2 = par1;
   par2.Lee(x, y);
   cout << "Valor de par2.a: " << x << endl;
   cout << "Valor de par2.b: " << y << endl;

   return 0;
}

La línea "par2 = par1;" copia los valores de los datos miembros de par1 en par2.

Constructor copia:

Un constructor de este tipo crea un objeto a partir de otro objeto existente. Estos constructores sólo tienen un argumento, que es una referencia a un objeto de su misma clase.

En general, los constructores copia tienen la siguiente forma para sus prototipos:

tipo_clase::tipo_clase(const tipo_clase &obj);

De nuevo ilustraremos esto con un ejemplo y usaremos también "pareja":

class pareja
{
   public:
      // Constructor
      pareja(int a2=0, int b2=0);
      // Constructor copia:
      pareja(const pareja &p);

      // Funciones miembro de la clase "pareja"
      void Lee(int &a2, int &b2);
      void Guarda(int a2, int b2);
   private:
      // Datos miembro de la clase "pareja"
      int a, b; 
   public:
};

pareja::pareja(int a2, int b2)
{
   a = a2;
   b = b2;
}

pareja::pareja(const pareja &p)
{
   a = p.a;
   b = p.b;
}

Para crear objetos usando el constructor copia se procede como sigue:

int main()
{
   pareja par1(12, 32)
   pareja par2(par1); // Uso del constructor copia: par2 = par1
   int x, y;
   
   par2.Lee(x, y);
   cout << "Valor de par2.a: " << x << endl;
   cout << "Valor de par2.b: " << y << endl;

   return 0;
}

También en este caso, si no se especifica ningún constructor copia, el compilador crea uno por defecto, y su comportamiento es exactamente el mismo que el del definido en el ejemplo anterior. Para la mayoría de los casos esto será suficiente, pero en algunas ocasiones podemos necesitar redefinir el constructor copia.


pagina029 Principal pagina031