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.

Objetivos

2.

Conceptualización

2.1

El puntero This

2.2

Funciones y Clases Amigas

2.3

Memoria Estática vs. Dinámica

2.4

Parámetros implícitos

2.5

Funciones miembro tipo const

2.6

Relación entre Clases

3.

Ejemplos

3.1

Ejemplo1

3.2

Ejemplo2

3.3

Ejemplo3

4.

Aplicación

5.

consulta

 

 

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.

 

 

Regresar>>>

CONCEPTUALIZACIÓN

 

Aclaración: Los temas aquí relatados son puntuales y pretenden complementar las consultas y trabajos de conceptualización adelantados por cada estudiante.

 

 

El Puntero This

 

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

};

 

Regresar>>>

 

Funciones y Clases Amigas

 

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

 

 

Regresar>>>

 

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;

 

 

 

Regresar>>>

 

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).

 

 

Regresar>>>

 

Función Miembro Constante

 

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;

}

 

 

Regresar>>>

 

Relaciones entre Clases

 

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”.

 

Regresar>>>

 

 

EJEMPLOS

 

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;

}

Regresar>>>

 

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;

}

 

 

Regresar>>>

 

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.

 

 

 

 

CONSULTAS

 

Preparemos las siguientes temáticas para la siguiente sesión:

Herencia simple