LABORATORIO 3: Constructores y Destructores, Sobrecarga de
Operadores
CONTENIDOS
1 |
||
2 |
||
2.1 |
||
3.1 |
||
3.2 |
||
3 |
||
3.4 |
||
|
||
4 |
||
5 |
||
|
Tips del Ingeniero |
|
|
|
|
|
|
|
|
|
- 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++.
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. |
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!
#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;
}
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