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. |
|
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.
-
Reconocer los mecanismos de herencia simple como
funcionalidad clara en la Orientación a Objetos
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
};
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;
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;
}
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.
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;
}
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.
Preparemos las siguientes temáticas para la siguiente sesión:
Herencia múltiple