LABORATORIO 3: Constructores y Destructores, Sobrecarga de Operadores

 

CONTENIDOS

 

1

Objetivos

1

2

Conceptualización

1

2.1

Constructores

1

3.1

Destructores

2

3.2

Sobrecarga de Operadores

2

3

Ejemplos

3

3.4

Ejemplo constructores, destructores

3

 

Ejemplo sobrecarga operador +

5

4

Aplicación

7

5

Consulta

8

 

Tips del Ingeniero

 

 

Tip 1

 

 

Tip 2

 

 

Tip 3

 

 

 

OBJETIVOS

 

-         Identificar la utilidad de un constructor.

-         Identificar la necesidad de un destructor.

-         Identificar la sobrecarga de operadores dentro de las capacidades extendidas del lenguaje de programación C++.

 

 

CONCEPTUALIZACIÓN

 

CONSTRUCTOR

 

Ya hemos reconocido que todas las funciones que se encuentran al interior de una clase se denominan funciones miembro o métodos, porque justamente son la forma –método- adecuado para poder acceder a cada uno de los elementos encapsulados en la parte reservada –privada- de cada objeto.

 

Un constructor es una función miembro que posee el mismo nombre de la clase y permite inicializar los atributos de la clase, se invoca automáticamente siempre que se instancia una clase.  Los constructores no pueden especificar tipos de retorno, pero si pueden recibir argumentos, adicionalmente, pueden ser nombrados múltiples métodos constructores (sobrecarga).  

 

Existen cuatro tipos de constructores:

-         Por defecto, son aquellos en los que no se reciben argumentos y dentro del cuerpo del método se realizan las asignaciones de valores a los respectivos atributos.

-         Ordinarios, cuentan con tantos argumentos como atributos, los cuales son transferidos como valores del nuevo objeto.

-         Inicialización, cuentan con un número menor de argumentos que de atributos de la clase.

-         De copia, como argumento reciben otro objeto del mismo tipo para hacer una copia de sus valores.

 

¡!

Los métodos son el conjunto de procedimientos empleados para tener acceso a cada uno de los atributos encapsulados dentro de la parte reservada (privada) de cada objeto.

Clases externas pueden ver a los métodos (siempre que estén definidos en la parte pública de la clase).

Los atributos de una clase no se pueden inicializar directamente en su interior.

 

DESTRUCTOR

 

De forma complementaria a la tarea de inicialización de un constructor, un destructor destruye los objetos creados, o dicho de una mejor manera, le retorna el espacio de memoria “prestado” al objeto a su propietario original: la memoria Ram.  Se representa con el mismo nombre de la clase, precedido del símbolo “~”.  Una función destructor no puede especificar argumentos, no devuelve valor alguno, ni puede ser sobrecargado (homonimia).  Un destructor se invoca cuando deja de existir el ámbito del respectivo objeto.  Los destructores son apropiados –necesarios- para aquellas clases cuyos objetos contienen almacenamiento dinámicamente asignado.

 

 

¡!

Dado que en un programa compilado separadamente (archivos definición, implementación e interfaz) se define una doble inclusión del archivo header, sus especificaciones podrían crearse dos o más veces por parte del preprocesador, por lo que se hace necesario forzar una compilación condicional insertando en el archivo de definición (*.h) las siguientes líneas:

 

#ifndef NOMBRE_H  //Si no se ha definido la macro que se rotulará NOMBRE_H

#define NOMBRE_H //…defínala

//definiciones normales del cuerpo del archivo, conteniendo las clases

#endif    //Fin del condicionamiento de la compilación

 

 

SOBRECARGA DE OPERADORES (homonimia en operadores)

 

La sobrecarga es un proceso por el cual se redefine el significado para un cierto método, o un cierto operador, con el objetivo de extender las capacidades propias de C++.  Se parte del principio que NO es posible crear nuevos operadores.  Para un operador sobrecargado dentro de una clase sólo será válida su redefinición para objetos de ésta.

 

Sintácticamente, la homonimia de operadores se logra como la definición de cualquier función, excepto que en el encabezado se emplea la palabra reservada operator seguida del operador a sobrecargar:

 

Tipo_retorno operator  símbolo (Argumentos) <const>{

      //cuerpo del método

}

Operadores que se pueden sobrecargar:

+

-

*

/

%

^

&

|

~

!

=

<

>

+=

-=

*=

/=

%=

^=

&=

!=

<<

>>

>>=

<<=

==

|=

<=

>=

&&

||

++

--

->*

->

[]

()

new

delete

 

Operadores que NO se pueden sobrecargar:

.

.*

::

?:

sizeof

 

 

!

La intención principal al sobrecargar un operador es cumplir con la misma funcionalidad de la operación que este implica, pero sobre los objetos instanciados a partir de cierta clase.

 

 

EJEMPLOS

 

Defina la carpeta Laboratorio4 y dentro de esta genere una subcarpeta por cada ejemplo y ejercicio.  Tenga en cuenta la compilación condicionada del archivo de definición (*.h) y la información administrativa.

 

Ejemplo1: Manejo de Constructores y Destructores

 

Implementemos el siguiente código:

 

/*ARCHIVO COMPU.H

      INFORMACION ADMINISTRATIVA*/

#ifndef COMPU_H

#define COMPU_H

class Compu{

            char *procesador;

            float precio;

            unsigned int dd;

            unsigned int ram;

      public:

            Compu();    //Constructor por Defecto

            Compu(char *,float,unsigned,unsigned);   //Constructor Ordinario

            acim(char *);           //Constructor de Inicializacion

            void leeDatos();

            void muestraDatos();

            ~Compu(){

                  delete[] procesador;

            }

};

#endif

 

/*********** ARCHIVO COMPU.CPP *************/

#include”compu.h”

#include<iostream.h>

//#include<string.h>

Compu::Compu(){

      procesador=new char[10];

      procesador=”“;

      precio=0.0;

      dd=ram=0;

}

 

Compu::Compu(char *cadena,float p,unsigned dd,unsigned ram):precio(p),dd(dd),ram(ram){

      procesador=new char[10];

      procesador=cadena;

}

 

Compu::Compu (char *cadena){

      procesador=new char[10];

      procesador=cadena;

      precio=150;

      dd=100;

      ram=512;

}

 

void Compu::leeDatos(){

      cout<<endl<<”USTED DIGITARA LOS DATOS PARA EL OBJETO”<<endl;

      cout<<”PROCESADOR: “;

      procesador=new char[10];

      cin>>procesador;

      cout<<endl<<”PRECIO: U$D”;

      cin>>precio;

      cout<<endl<<”DISCO DURO(Gigas): “;

      cin>>dd;

      cout<<endl<<”RAM(Megas): “;

      cin>>ram;

}

 

void Compu::muestraDatos(){

      cout<<endl<<”DATOS PARA EL OBJETO”<<endl;

      cout<<”PROCESADOR: “<<procesador;

      cout<<endl<<”PRECIO: U$D”<<precio;

      cout<<endl<<”DISCO DURO(Gigas): “<<dd;

      cout<<endl<<”RAM(Megas): “<<ram<<endl;

}

 

/*************** ARCHIVO PRINCIPA.CPP *************/

#include”compu.h”

int main(){

      Compu pc0;

      pc0.muestraDatos();

      acim pc1(“PentiumIV”,110,100,1024);

      pc1.muestraDatos();

      Compu pc2(“Athlon”);

      pc2.muestraDatos();

      Compu pc3;

      pc3.leeDatos();

      pc3.muestraDatos();

      return 0;

}

 

Analicemos!!!!

-         ¿Qué tipos de constructores se emplearon?, identifíquelos plenamente!

-         ¿Tiene cabida el destructor?

-         ¿Siempre es necesario un constructor?, ¿siempre lo es un destructor?

-         ¿Hay alguna otra forma de inicializar, diferente al constructor?

 

 


 

Ejemplo2: Manejo de Sobrecarga en Operadores.

Supongamos que deseamos crear la operación de concatenación de caracteres.

 

Implementemos el siguiente programa:

 

/*ARCHIVO CARACTER.H

                INFORMACION ADMINISTRATIVA*/

#ifndef CARACTERES_H

#define CARACTERES_H

class Caracteres{

                               char *caracter;

                public:

                               Caracteres(char *c);

                               Caracteres operator+(Caracteres);

                               void muestraDatos();

                 /*          ~Caracteres(){

                                               delete[ ] caracter;

                               }*/

};

#endif

 

/*ARCHIVO CARÁCTER.CPP*/

#include” caracter.h”

#include<iostream.h>

#include<string.h>

Caracteres::Caracteres(char *c){

                caracter=new char [strlen(c)];

                strcpy(caracter,c);

}

Caracteres Caracteres::operator +(Caracteres cad){

                strcat((*this).caracter,cad.caracter);

                return *this;

}

void Caracteres::muestraDatos(){

                char f ;

                cout<<caracter ;

                cin>>f ;

}

 

/*ARCHIVO PRINCIPA.CPP*/

#include”caracter.h”

int main(){

                Caracteres P1(“Programación“);

                Caracteres P2(“Orientada “);

                Caracteres P3(“a “);

                Caracteres P4(“Objetos C”);

                P1=P1+P2;

                P1=P1+P3;

                P1=P1+P4;

                P1.muestraDatos();

                return 0;

}


Analicemos!!!!

-         ¿Por qué se considera sobrecargado el operador +?

-         ¿El operador = se sobrecargó?

-         Revisemos las implicaciones de usar memoria dinámica y recabemos este concepto!

 

 

APLICACIÓN

 

  1. Recordemos la aplicación del laboratorio No. 3 “Nómina para la empresa Belleza Salvaje”:
    1.  Creemos cuatro diferentes constructores, que correspondan a las diferentes tipificaciones entregadas en esta guía.
    2. Sobrecargue el operador + para adicionar las deducciones de dos empleados.

 

  1. Recordemos el ejercicio para la clasificación de la información de 20 estudiantes, para determinar en estructuras diferentes quienes aprobaron y quienes no, visto desde el Laboratorio 0.  La solución estructurada se muestra a continuación.  Realice la respectiva implementación Orientada a objetos donde sea claro y comentariado el manejo de:
    1. Constructores y su uso desde la instanciación de objetos
    2. Destructores sobre objetos dinámicos
    3. Sobrecarga de operadores (ejemplo ++ o – a la nota de un estudiante).

 

#include<stdio.h>

#define TAM 5

 

struct fecha{

      int dia,mes,annio;

};

 

struct persona{

      int cod;

      char nombre[30] ;

      float nota;

      struct fecha nacimiento;

} ;

//PROTOTIPOS

void lectura(struct persona est[TAM]) ;

void calculo(struct persona est[TAM],struct persona apr[TAM],struct persona rep[TAM],int *,int *);

void impresion(struct persona apr[TAM],struct persona rep[TAM],int ,int);

 

//Implementaci¢n

void lectura(struct persona est[TAM]){

      int cont;

      float q;

      for(cont=0;cont<TAM;cont++){

            printf(“\nPor favor digite el codigo del estudiante %d “,cont+1);

            scanf(« %d »,&est[cont].cod) ;

            printf(“\nPor favor digite el nombre del estudiante %d “,cont+1);

            scanf(« %s »,est[cont].nombre) ;

            do{

                  printf(“\nPor favor digite la nota del estudiante %d “,cont+1);

                  scanf(“%f”,&q);

            }while(q<0 ||q>5);

            est[cont].nota=q;

            printf(“\nPor favor digite el dia de nacimiento del  estudiante %d “,cont+1);

            scanf(« %d »,&est[cont].nacimiento.dia) ;

            printf(“\nPor favor digite el mes de acimiento del estudiante %d “,cont+1);

            scanf(« %d »,&est[cont].nacimiento.mes) ;

            printf(“\nPor favor digite el año de nacimiento del estudiante %d “,cont+1);

            scanf(« %d »,&est[cont].nacimiento.annio) ;

      }

}

 

void calculo(struct persona est[TAM],struct persona apr[TAM],struct persona rep[TAM],int *ca,int *cr){

      int cont ;

      for(cont=0 ;cont<TAM ;cont++){

            if(est[cont].nota < 3.0){

                  rep[(*cr)++]= est[cont] ;

            }else

                  apr[(*ca)++]=est[cont] ;

      }

}

 

void impresion(struct persona apr[TAM],struct persona rep[TAM],int ca,int cr){

      int cont;

      printf(“\Nmi PRIMER PROGRAMITA DE INFOTAM\n\n”);

      for(cont=0;cont<ca;cont++){

            printf(“\\INFORMACION ESTUDIANTES APROBADOS %d”,cont+1);

            printf(« \nCodigo : %d « ,apr[cont].cod) ;

            printf(« \nNombre : %s « ,apr[cont].nombre) ;

            printf(“\nNota:   %f “,apr[cont].nota);

            printf(“\nFecha: %d/%d/%d “,apr[cont].nacimiento.dia,apr[cont].nacimiento.mes,apr[cont].nacimiento.annio);

            getchar();

      }

      for(cont=0;cont<cr;cont++){

            printf(“\\INFORMACION ESTUDIANTES REPROBADOS %d”,cont+1);

            printf(“\nCodigo: %d “,rep[cont].cod);

            printf(“\nNombre: %s “,rep[cont].nombre);

            printf(“\nNota:   %f “,rep[cont].nota);

            printf(“\nFecha: %d/%d/%d “,rep[cont].nacimiento.dia,rep[cont].nacimiento.mes,rep[cont].nacimiento.annio);

            getchar();

            getchar();

      }

}

 

//Interfaz

int main(){

      int ca,cr;

      ca=cr=0;

      struct persona estudiante[TAM],aprobado[TAM],reprobado[TAM];     lectura(estudiante);

      calculo(estudiante,aprobado,reprobado,&ca,&cr);

      impresion(aprobado,reprobado,ca,cr);

      return 0;

}

 

CONSULTAS

 

Preparemos las siguientes temáticas:

-         Hemos visto en esta guía un trabajo con memoria dinámica, recabemos en esta temática.

-          Jerarquía de Clases, Relaciones y Herencia