LABORATORIO 6: This, Friend, Memoria Dinámica, Parámetros Implícitos, Herencia Simple

 

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

Herencia

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.

-         Reconocer los mecanismos de herencia simple como funcionalidad clara en la Orientación a Objetos

 

 

 

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

                                   (*this).p=f.p;

                        }

            //Demás métodos y/o atributos

};

 

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

 

 

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

 

 

 

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;

}

 

 

Herencia

 

La herencia es la relación es-un entre dos clases, en las que una clase hija (clase derivada) se deriva de una clase padre (clase base): “Un primíparo es un estudiante”.

 

Notación:

 

class base{

….

};

 

class derivada: especificador_de_acceso nombre_clase_base{

};

 

Por defecto, la herencia es private.  Cuando se define el especificador de acceso se debe tener en cuenta la siguiente correspondencia:

 

Especificador de Acceso

Corresponde a …

public:

Todos los miembros públicos de la clase base se tratarán como miembros públicos de clase derivada.

Los miembros protegidos de la clase base, se tratan como protegidos de la clase derivada.

Los miembros privados en la clase base no son accesibles desde la clase derivada.

protected:

Todos los miembros públicos de la clase base se tratarán como miembros protegidos de clase derivada.

Los miembros protegidos de la clase base, se tratan como protegidos de la clase derivada.

Los miembros privados en la clase base no son accesibles desde la clase derivada.

private:

Todos los miembros públicos de la clase base se tratarán como miembros privados de clase derivada.

Los miembros protegidos de la clase base, se tratan como privados de la clase derivada.

Los miembros privados en la clase base no son accesibles desde la clase derivada.

 

El lenguaje de programación C++ emplea dos tipos de derivación:

 

-         Simple cuando se hereda de una sola clase base.

-         Múltiple cuando se hereda de más de una clase base.

Nota.  Esta práctica empleará la herencia simple.

 

 

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;

}

 

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: Herencia simple

 

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 herencia.h

      Extraido de C++ guia de autoense¤anza de Herbert Schildt

      INFORMACIàN ADMINISTRATIVA

 

*/

#ifndef THIS_H

#define THIS_H

class base{

            int x;

      public:

            void setx(int);

            int showx();

};

 

class derivada:public base{

            int y;

      public:

            void sety(int);

            int showy();

};

#endif

 

 

/*Archivo herencia.cpp*/

#include"herencia.h"

void base::setx(int n){

      x=n;

}

 

int base::showx(){

      return x;

}

 

void derivada::sety(int n){

      y=n;

}

 

int derivada::showy(){

      return y;

}

 

/*Archivo principa.cpp*/

#include"herencia.h"

#include<iostream.h>

int main(){

      derivada obj;

      obj.setx(10);           //Metodo de clase base

      obj.sety(20);           //Metodo de clase derivada

      cout<<endl<<"Valor de x: "<<obj.showx();  //Metodo clase base

      cout<<endl<<"Valor de y: "<<obj.showy(); //Metodo clase derivada

      cin.get();

      return 0;

}

 

 

APLICACIÓN

 

1.  Implemente cada uno de los ejemplos denotados previamente.

 

2.  Cree una nueva carpeta y en ella copie el ejemplo3

            a.  Modifique el especificador de acceso de la clase derivada a protected.  Relate       su análisis en el archivo analisis.txt de esta carpeta.

            b.  Modifique el especificador de acceso de la clase derivada a private.  Relate           su análisis en el archivo analisis.txt de esta carpeta.

 

3.  Cree una nueva carpeta y en ella copie el ejemplo3

            a.  Cree una nueva clase derivada2 que derive de la clase derivada.  Especifique         un atributo para cada nivel de visibilidad junto a su correspondiente método get         y set.

            b.  Defina el especificador de acceso de la clase derivada2 a protected.  Relate          su análisis en el archivo analisis.txt de esta carpeta.

            c.  Modifique el especificador de acceso de la clase derivada a private.  Relate           su análisis en el archivo analisis.txt de esta carpeta.

 

4.  Cree una nueva carpeta y en ella copie el ejemplo3

            a.  Especifique para cada clase métodos con igual nombre y por medio de ellos          opere los atributos, relate su análisis en el archivo analisis.txt.

 

5.  Diseñe un archivo analisisgeneral.txt sobre la raíz de esta práctica y concluya sobre herencia simple.

 


 

 

 

CONSULTAS

 

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

Herencia múltiple