LABORATORIO 3: This, Friend, Memoria Dinámica, Parámetros Implícitos, Relación de
Composición
Antes de comenzar reflexionemos…
“Cada guía que diseño exige mucho trabajo por cuenta de mis
estudiantes, qué noble sería que se reconociera que producirla demanda un
tiempo multiplicado en 4”
Miguel Angel Mendoza Moreno
1. |
|
2. |
|
2.1 |
|
2.2 |
|
2.3 |
|
2.4 |
|
2.5 |
|
2.6 |
|
3. |
|
3.1 |
|
3.2 |
|
3.3 |
|
4. |
|
5. |
OBJETIVOS
-
Afianzar los conocimientos de temáticas indicadas como
el apuntador this, funciones amigas, memoria dinámica y parámetros implícitos.
-
Conceptuar y aplicar las Relaciones entre clases, en
primera instancia la Composición.
Aclaración: Los temas aquí
relatados son puntuales y pretenden complementar las consultas y trabajos de
conceptualización adelantados por cada estudiante.
Dentro de una función miembro, la palabra reservada this es el nombre de un puntero implícito al objeto que actualmente se está tratando. Cuando se desea utilizar el puntero oculto en el código de la función miembro, se utiliza la palabra reservada this. La mayoría de las veces, el uso de this es innecesario, ya que C++ supone el uso de this siempre que se refiere a miembros dato (atributos).
¿Cuándo usarlo? R/ Cuando haya la posibilidad que se presente ambigüedad con respecto a los atributos propios de la clase.
Ejemplo
Segmento de Código:
class X{
int
p;
public:
X(X
&f){ //Note que este es un constructor de copia!
(*this).p=f.p;
}
//Demás
métodos y/o atributos
};
Una amiga (friend) de una clase X es una función o clase que, aunque no es miembro de esa clase, tiene acceso completo a cada una de las áreas de visibilidad (private, public, protected).
Una amiga es una función normal en términos de ámbito, declaraciones y definiciones.
Si una función Y es amiga de una clase X, utiliza Y todos los miembros de X como si fueran públicos.
La amistad es concedida y no tomada, es decir, para que la clase B sea una amiga de la clase A, la clase A debe declarar que la clase B es su amigo. También, la amistad no es simétrica ni transitiva, es decir, si la clase A es un amigo de la clase B y la clase B es un amigo de la clase C, no se puede inferir que la clase B sea un amigo de la clase A, que la clase C sea amigo de la clase B o que la clase A sea un amigo de la clase C.
NOTA. La amistad entre clases y funciones son una
mala práctica en Orientación a Objetos, ya que quebranta cualquier regla de
ocultamiento, por lo tanto, es bueno conocerlas, pero no orientar los diseños a
su uso.
Notación Función amiga:
class
X{
//Atributos y Métodos
friend prototipo de la función;
};
//implementación
de la función amiga
Una clase puede ser amiga de otra clase:
Notación Clase amiga:
class
X{
//Atributos y Métodos
friend class nombre_de_clase;
};
//Definición
de la clase amiga
Memoria Estática y Memoria Dinámica
Dentro de la operatividad de un programa, muchas veces se definen ciertos espacios de almacenamiento para variables que nunca lo emplean, o en su caso complementario el espacio predefinido no es suficiente para albergar todos los datos necesarios, desperdicio en el primer caso y escasez en el segundo, situaciones que resulta costosas en cuanto a la complejidad algorítmica.
Cuando todos los espacios de almacenamiento que empleará el programa se encuentran predefinidos dentro de la codificación de las fuentes, se dice que se han dimensionados los recursos espaciales en tiempo de compilación (ligadura estática), ya que al momento de compilar, el sistema operativo tendrá certidumbre en los espacios requeridos para tratar la información derivada del programa.
En la siguiente definición: char vector[20]; el compilador sabrá que requerirá 20 bytes para almacenar la estructura y en compilación ese valor ya se definirá.
Ahora bien, ¿para qué desperdiciar espacio de almacenamiento cuando esta situación puede ser controlada?, sería mejor relacionar ciertas variables que permitieran que los espacios de almacenamientos crezcan de acuerdo a las características denotadas por el propio cliente del programa, esas variables son apuntadores (variables cuya finalidad exclusiva es almacenar direcciones de memoria) y precisamente como la dimensión espacial no se conoce al momento de compilar, sino mientras se corra y finalice el programa, se dice que los espacios de almacenamiento se definen en tiempo de ejecución (ligadura dinámica).
Para solicitar espacio de almacenamiento se emplea la palabra reservada new, cuyo resulto debe asignarse a un apuntador:
int *p;
p=new
int;
La anterior sentencia se lee: “Al
apuntador p se le asignará un espacio de almacenamiento proporcional a un
entero”.
El manejo de memoria dinámica resulta eficiente con respecto a las características denotadas con anterioridad, pero debe ser cuidadoso, ya que siempre que se abra un espacio (por medio de new), luego de utilizarlo se deberá retornar al control propio del sistema operativo por medio de la palabra clave delete.
delete p;
Parámetros Implícitos
Un parámetro implícito es aquel que asigna un valor por defecto al atributo en caso que no sea recepcionado.
Los parámetros implícitos siempre se denotarán al extremo derecho de los no implícitos.
void funcimplicita(int x,int y=2,int z=5);
Se debe tener en cuenta no redefinir las inicializaciones entre el prototipo y la implementación propia de la función implícita (o método implícito).
Es aquella que garantiza que no se modificará en estado del objeto de la clase útil.
class X{
//atributos
public:
X(int x, int
y);
int getDato() const;
};
int X::getDato()
const{
return x;
}
Las clases en el paradigma orientado a objetos se pueden relacionar de diversas maneras, entre las que se cuenta:
- Herencia (Especialización/Generalización):
Indica que una subclase hereda los métodos y atributos especificados por una Super Clase, por ende la Subclase además de poseer sus propios métodos y atributos, poseerá las características y atributos visibles de la Super Clase (public y protected).
Generalización es la relación jerárquica que se establece desde la Clase Derivada hacia la Super Clase, “El tesista es un tipo de estudiante”.
Especialización es la relación jerárquica que se establece desde la Super Clase hacia la Clase Derivada, “Una especiacialización de estudiante es el tesista”.
-
Agregación:
Es una relación por Referencia. Es un tipo de relación dinámica, en donde el tiempo de vida del objeto incluido es independiente del que lo incluye. Este tipo de relación es comúnmente llamada Agregación (el objeto base utiliza -agrega- al incluido para su funcionamiento). “Un programa académico es una agregación de asignaturas, pero si no se ofrece una asignatura, no indica que el programa ya no se pueda ofrecer y de forma recíproca, si el programa académico desaparece, la asignatura puede seguir vigente para otros programas”.
- Composición:
Es un tipo de relación estática, en donde el tiempo de vida del objeto incluido está condicionado -depende- por el tiempo de vida del que lo incluye. Este tipo de relación es comúnmente llamada Composición (el Objeto base se construye a partir del objeto incluido, es decir, es "parte/todo"). “Una empresa posee sucursales por todo el país, si se acaba la empresa, no pueden seguir operando las sucursales independientemente”.
NOTA. No olvide crear las respectivas subcarpetas para cada ejemplo.
Ejemplo 1: Manejo operador this, constructor de copia, funciones inline, parámetros implícitos.
Trabajito: Al término redacte un archivo analisis.txt
en esta misma carpeta, en el que analice el manejo de cada una de las temáticas
propias del ejemplo.
Nota. Observe que ahora todos los
streams se manejan solo en la interfaz
/* Archivo this.h
INFORMACIàN ADMINISTRATIVA
*/
#ifndef THIS_H
#define THIS_H
class A{
int x,y;
public:
A():x(0),y(0){}; //constructor
como funcion inline
A(A &);
//Constructor de Copia
int getX();
//obtiene el dato que est en en atruibuto x
int getY();
void setX(int); //fija
el valor para el atributo x
void setY(int);
void inicializa(int
x,int y=5); //Parametros implicitos
};
#endif
/*Archivo this.cpp*/
#include"this.h"
A::A(A &copia){ //Implementacion
constructor de copia
(*this).x=copia.x;
(*this).y=copia.y;
}
int A::getX(){ return x;}
int A::getY(){return y;}
void A::setX(int x){ //¿como distinguir entre el x recibido como
(*this).x=x;
//parámetro y el x atributo?R./ Puntero this
}
void A::setY(int y){
(*this).y=y;
}
void A::inicializa(int x,int
y){ //fun. De
parámetros implicitos
this->x=x; //o se utiliza (*this).x o this->x
(*this).y=y;
}
/*archivo principa.cpp*/
#include"this.h"
#include<iostream.h>
//#include<conio.h>
int main(){
// clrscr();
int x,y;
A obj1;
obj1.inicializa(3);
//Observese que se invoca a inicializa con //un
solo parámetro cuando realmente //esperan
dos. No es error!
cout<<"Objeto1 se inicializo con x="
<<obj1.getX()<<",
y="<<obj1.getY()<<endl;
cin.get();
cout<<"Ahora creemos un segundo objeto
"<<endl;
A obj2(obj1);
//Empleamos el constructor de copia
cout<<"Objeto2 se inicializo con x="
<<obj2.getX()<<",
y="<<obj2.getY()<<endl;
cin.get();
cout<<"Ahora modifiquemos al
objeto2"<<endl;
cout<<"Digite dos enteros separados por espacios
";
cin>>x>>y;
obj2.setX(x);
obj2.setY(y);
cout<<"Veamos lo que quedo en
objeto2"<<endl;
cout<<endl<<"Objeto2 se inicializo con
x="
<<obj2.getX()<<",
y="<<obj2.getY()<<endl;
cin.ignore();
cin.get();
return 0;
}
Ejemplo 2: friends, memoria dinámica, funciones const.
Trabajito: Al término redacte un archivo analisis.txt
en esta misma carpeta, en el que analice el manejo de cada una de las temáticas
propias del ejemplo.
/*Archivo friend.h
INFORMACIàN ADMINISTRATIVA
*/
#ifndef FRIEND_H
#define FRIEND_H
class A{
int x,y;
friend void fijaDatos(A &,int,int); //Funcion fijaDatos Amiga de clase A
public:
int getX();
//obtiene el dato que est en en atributo x
int getY();
friend class B; //Clase
B sera amiga de clase A
};
class B{
char *palabra;
public:
char
*getPalabra()const; //funcion constante
void setPalabra(char *,int,int,A &);
~B(){
delete [] palabra;
} //retorno del
espacio a la memoria ram
};
#endif
/*Archivo friend.cpp*/
#include"friend.h"
#include<string.h>
int A::getX(){ return x;}
int A::getY(){return y;}
void B::setPalabra(char
*pal,int a,int b,A &oj){
palabra=new char[strlen(pal)];//Solicitud de espacio dinamico
strcpy(palabra,pal);
oj.x=a;oj.y=b;
}
char* B::getPalabra() const{
return palabra;
}
void fijaDatos(A &ob,int
a,int b){ //Note que esta funcion no esta
en //el cuerpo de clase alguna
ob.x=a; //note
que esto normalmente no es posible por //ocultamiento
ob.y=b;
}
/*Archivo principa.cpp*/
#include"friend.h"
#include<iostream.h>
#include<conio.h>
int main(){
clrscr();
char palab[30];
int x,y;
B obj1;
A obj2;
cout<<endl<<"Digite una palabra ";
cin>>palab;
cout<<endl<<"Digite dos enteros separados por
espacio ";
cin>>x>>y;
obj1.setPalabra(palab,x,y,obj2);
cout<<endl<<"La palabra en el objeto1 es"
<<obj1.getPalabra()<<endl;
// cin.ignore();
cin.get();
return 0;
}
Ejemplo 3: Relación de Composición
Trabajito: Al término redacte un archivo analisis.txt
en esta misma carpeta, en el que analice el manejo de cada una de las temáticas
propias del ejemplo.
Recordemos el parcial del Laboratorio, a continuación se presenta su solución…
La sección de deportes de
la Universidad quiere conformar las
selecciones (masculina y femenina) de baloncesto, para ello se tiene la
información de 50 personas con todos los datos básicos como son: código,
nombre, edad, estatura y sexo. Se
necesita encontrar aquellas personas que cumplan con los siguientes requisitos: Para el equipo femenino, mujeres, entre los
17 y 24 años, con estatura igual o superior a 170 cm.; para el equipo
masculino, hombres entre los 16 y 25 años con más de 180 cm. Es necesario separar la información de
admitidos en dos grupos (hombres y mujeres), de tal manera que el entrenador
tenga los datos básicos a mano, adicionalmente, si no se consigue un número
superior a 8 personas por equipo, se deberá dar un mensaje de cancelación del
equipo buscado.
Diseñe un programa
Orientado a Objetos, tales que permita extraer de un vector de aspirantes cada
una de las personas y desde allí rescatar a dos vectores (hombres y mujeres)
los datos de los seleccionados. Realice
la abstracción de los respectivos atributos y métodos de clase y presente los
respectivos módulos de programa.
/*
Archivo seleccio.h
Extraido de C++ guia de autoense¤anza de Herbert Schildt
INFORMACIàN ADMINISTRATIVA
*/
#ifndef SELECCION_H
#define SELECCION_H
class Persona{
unsigned codigo,edad,estatura;
char nombre[30];
int sexo;
public:
unsigned getCodigo();
void setCodigo(unsigned);
unsigned getEdad();
void setEdad(unsigned);
unsigned getEstatura();
void
setEstatura(unsigned);
char* getNombre();
void setNombre(char[]);
int getSexo();
void setSexo(int);
int selecciona();
};
#endif
/*Archivo seleccio.cpp*/
#include"seleccio.h"
#include<string.h>
unsigned Persona::getCodigo(){
return codigo;
}
void Persona::setCodigo(unsigned Cod){
codigo=Cod;
}
unsigned Persona::getEdad(){
return edad;
}
void
Persona::setEdad(unsigned Ed){
edad=Ed;
}
unsigned Persona::getEstatura(){
return estatura;
}
void
Persona::setEstatura(unsigned Est){
estatura=Est;
}
char* Persona::getNombre(){
return nombre;
}
void Persona::setNombre(char
n[30]){
strcpy(nombre,n);
}
int Persona::getSexo(){
return sexo;
}
void Persona::setSexo(int se){
sexo=se;
}
int
Persona::selecciona(){ //Si es hombre
seleccionado retorna 1, mujer seleccionada 0, no seleccionado -1
if(sexo==0 && edad>=17 && edad <=24
&& estatura>=170)
return 0; //mujer
seleccionada
else
if(sexo==1 && edad>=16 && edad <=25
&&estatura>=180)
return 1; //hombre
seleccionado
else
return -1; //estudiante
no seleccionado
}
/*Archivo principa.cpp*/
#include<iostream.h>
#include"seleccio.h"
#include<conio.h>
#define TAM 3
int main(){
unsigned int tmp,hombcont=0,mujercont=0;
int se;
char nom[30];
Persona estud[TAM],hombres[TAM],mujeres[TAM];
clrscr();
cout<<"\t\tBALONCESTO UCAUCA"<<endl;
for(int i=0;i<TAM;i++){
cout<<endl<<"CODIGO\t\t";
cin>>tmp;
estud[i].setCodigo(tmp);
cout<<"NOMBRE\t\t";
cin>>nom;
estud[i].setNombre(nom);
cout<<"EDAD\t\t";
cin>>tmp;
estud[i].setEdad(tmp);
cout<<"ESTATURA\t";
cin>>tmp;
estud[i].setEstatura(tmp);
do{
cout<<endl<<"SEXO
1:HOMBRE; 0:MUJER\t";
cin>>se;
}while(!(se==0
||se==1));
estud[i].setSexo(se);
switch(estud[i].selecciona()){
case 1:hombres[hombcont++]=estud[i];
break;
case 0:mujeres[mujercont++]=estud[i];
break;
default:cout<<endl<<"Estudiante No
Seleccionado";
}
}
cout<<endl<<endl<<"\tHOMBRES
SELECCIONADOS";
for(i=0;i<hombcont;i++){
cout<<endl<<hombres[i].getCodigo();
}
cout<<endl<<"\tMUJERES SELECCIONADAS";
for(i=0;i<mujercont;i++){
cout<<endl<<mujeres[i].getCodigo();
}
if(hombcont<=1)
cout<<endl<<"NO SE PUEDE ARMAR EQUIPO
MASCULINO ( Menos de 8)";
if(mujercont<=1)
cout<<endl<<"NO SE PUEDE ARMAR EQUIPO
FEMENINO ( Menos de 8)";
for(i=0;i<TAM;i++){
cout<<endl<<"TODOS
LOS ESTUDIANTES\t\t";
cout<<endl<<"CODIGO\t\t"<<estud[i].getCodigo();
cout<<endl<<"NOMBRE\t\t"<<estud[i].getNombre();
cout<<endl<<"EDAD\t\t"<<estud[i].getEdad();
cout<<endl<<"ESTATURA\t"<<estud[i].getEstatura();
if(estud[i].getSexo()==0)
cout<<endl<<"SEXO\t\t MUJER";
else
cout<<endl<<"SEXO\t\t
HOMBRE";
}
cin.get();
cin.get();
return 0;
}
¿Cómo podemos aplicar la composición para
este ejemplo?
Requeriremos crear una clase que se componga de otra clase, para
nuestro caso particular, crearemos una clase Control que se encargue de
gestionar la información de los estudiantes a seleccionar para cada uno de los
equipos de baloncesto (masculino y femenino).
Veamos los cambio que se generarían en el archivo de Definición (el
archivo .h).
/*Archivo seleccio.h*/
/*
INFORMACIàN
ADMINISTRATIVA
*/
#ifndef
SELECCION_H
#define
SELECCION_H
#define
TAM 50
class
Persona{
unsigned
codigo,edad,estatura;
char nombre[30];
int sexo;
public:
unsigned getCodigo();
void setCodigo(unsigned);
unsigned getEdad();
void setEdad(unsigned);
unsigned getEstatura();
void setEstatura(unsigned);
char* getNombre();
void setNombre(char[]);
int getSexo();
void setSexo(int);
int selecciona();
};
class Control{
Persona
estud[TAM],hombres[TAM],mujeres[TAM];
public:
lecturaEstudiantes();
seleccionaEquipos();
muestraResultados();
};
#endif
Obsérvese que en la Clase Control
se han Agregado instancias de la clase Persona, a saber, los vectores estud,
hombres y mujeres. Y nótese que al dejar
de existir una instancia de la Clase Control, cada una de las instancias de
Persona desaparecerán, pues existe una relación dependiente.
APLICACIÓN
Implemente cada uno de los ejemplos denotados previamente.
Preparemos las siguientes temáticas para la siguiente
sesión:
Herencia simple