PROGRAMACION ORIENTADA A OBJETOS

CONCEPTOS

Con la programación estructurada de lenguajes como el C o el Pascal fue por primera vez posible escribir de modo bastante simple programas relativamente complejos. Sin embargo esta programación estructurada no basta para acometer la complejidad de los programas en sistemas modernos con una confortable superficie de trabajo para el usuario. Dichos sistemas exijen una tecnología especial llamada programación orientada a objetos.
Este tipo de programación se combina con aquella de tipo estructurado y una serie de conceptos nuevos que consisten en descomponer un problema en una serie de subconjuntos que unidos con el lenguaje forman una entidad autónoma llamada objeto.
El lenguaje de programación orientada a objetos presenta tres aspectos fundamentales: objetos, polimorfismo y hereditariedad.

Objetos

Un objeto es una entidad lógica que contiene los datos así como el código que lo manipula. En la programación "clásica", se consideran los datos como un ente estático que sólo pueden ser manipulados con funciones y subroutinas externas a ellos. Un objeto es una entidad donde datos y código que los manipula están unidos y pertenecen a misma clase. Esta unión entre código y datos es lo que se llama encapsulamiento.

Polimormismo

Los lenguajes orientados a objetos admiten el llamado polimorfismo, termino que expresa la característica de poder utilizar un mismo nombre para actividades que aunque afines realizan funciones distintas. La idea es la de poder asociar un nombre a una clase genérica de acciones. Por ejemplo en un programa se podrían definir diversas funciones de ordenamiento de datos, una para valores enteros, una para valores de coma flotante y una para valores de tipo string. Gracias al polimorfismo es posible crear tres series de funciones ordena(). El compilador selecciona la rutina apropiada en base al tipo de parámetro con que se hace la llamada a la misma. En este ejemplo el concepto general sería la ordenación de datos. Las funciones sin embargo definirían el modo específico conque cada tipo de dato debería ser ordenado.

Hereditariedad

La hereditariedad es el proceso según el cual un objeto pude adquirir las propiedades de otro del cual derivan. De esta propiedad nace el concepto de clasificación según la cual se parte de una clase con características generales y de ella se pueden ir creando otras clases que además de sus propias características heredan aquellas pertenecientes a la clase general. Por ejemplo una naranja forma parte de la clase cítricos que a su vez derivan de la clase frutas las cuales forman parte de una clase aún más general que es la de alimentos. Una vez definida la jerarquía, la extensión de la misma se reduce a la definición de las características que aporta la clase nueva. Así la clase naranjas tendría las características propias de los cítricos más aquellas pertenecientes a ella misma como son color, contenido en azúcar etc.

INTRODUCCION A LAS CLASES Y A LOS OBJETOS

Para crear un objeto en C++ es indispensable definir primero la forma general del mismo por medio de la palabra llaveclass (clase).Una clase es similar a una estructura. La particularidad es que en ella se pueden definir además de variables también funciones y operadores

 

  • struct TBufferDatos
  • {

  • char nombre[LNOMBRE],
    apellidos[LAPELLIDOS],
    calle[LCALLE], distrito[LDISTRITO],
    ciudad[LCIUDAD],
    pais[LPAIS],
    telpriv[LTELPRIV],
    teltrab[LTELTRAB],
    fax[LFAX],
    observaciones[LOBSERVACIONES];
  • };
  • class TClaseAgenda
  • {

  • char *nombre_campo[N_ELEMENTOS];
    char info[INFO_LEN];
    protected:
    TBufferDatos dato;
    FILE *fp_tmp;
    int elemento_len[N_ELEMENTOS];
    char *elemento[N_ELEMENTOS];
    int modificado;
    char *name_dat,*name_tmp;
    int posx,posy,maxlen;
    int entrada_dato(void);
    int display_dato(void);
    void display_info(void);
    void display_campos(void);
    FILE * abe_archivo(char *nombre,const char *modo);
    void rebobina_archivo(FILE *fp);
    void visualiza_archivo(FILE *fp);
    int dato_vacio(void);
    void copia_datos(FILE *fp_dest, FILE *fp_source);
    public:
    TClaseAgenda( char *);
    ~TClaseAgenda();
    void informacion(const char *str);
    int entrada_datos(void);
    void almacena_datos(void);
    void visualiza_datos(void);
  • };

En el ejemplo las constantes en mayúsculas como LNOMBRE etc estarían definidas en otra parte del programa con instrucciones #include.
Una clase puede contener partes privadas (private), protegidas (protected) y públicas (public). Por defecto, todos los elementos definidos en la clase son privados.

Por ejemplo la variable:
char *nombre_campo[N_ELEMENTOS];
char info[INFO_LEN];
sería privada con lo cual ninguna función que no sea elemento de la clase puede acceder a ella.
Las funciones
.
.
void visualiza_archiv(FILE *fp);
void rebobina_archivo(FILE *fp);
int dato_vacio();
son protegidas lo que así mismo implica que ninguna función que no sea elemento de la clase puede acceder a ellas.
Además de elementos privados y protegidos se pueden definir elementos públicos a los cuales se tiene acceso desde otros puntos del programa por medio de variables cuyo tipo es la clase a la que pertenecen dichas funciones.
Así por ejemplo las funciones:

void informacion(const char *str);
int entrada_datos(void);
void almacena_datos(void);
void visualiza_datos(void);

son públicas pues aparecen debajo de la palabra llave public:. Estas tienen acceso desde cualquier punto del programa por medio de una variable cuyo tipo es TClaseAgenda.
La sintaxis para declarar una clase es como sigue:

  • class nombre_clase
    {
    [private]
    datos y funciones privados
    protected:
    datos y funciones protegidas
    public:
    datos y funciones publicas
    }lista objetos;

La lista objetos puede estar vacía.

Codificación de funciones elemento

En el momento de codificar una función que forma parte de una clase es indispensable indicar al compilador la clase a la que pertenece. La sintaxis es como sigue:

  • tipo nombre_clase::funcion(lista de Parámetros)
    {
    instrucciones;
    }

 

  • void TClaseAgenda::visualiza_datos(void)
  • {

  • display_campos();
    display_info();
    visualiza_archivo(fp_tmp);
  • }
 

El signo :: se llama operador de determinación de visibilidad (resolution operator).

En el ejemplo anterior la función visualiza_datos(void) hace una llamada a las funciones display_campos() y display_info() a las cuales, a pesar de ser privadas, tiene acceso por pertenecer ella misma a la clase "TClaseAgenda"

Llamada de funciones elemento

Para llamar a una función elemento de una clase desde una parte del programa que no es parte de la clase es necesario servirse del nombre de la variable (objeto) cuyo tipo es el de dicha clase.

 

  • void main()
  • {

  • TClaseAgenda a("agenda1"),b("agenda2");
    a.entrada_datos();
    b.visualiza_datos();
  • }
 

Es importante señalar que "a" y "b" son dos variables (objetos) cuyo tipo es TClaseAgenda y que son independientes. Ello significa que el cambio de cualquier elemento perteneciente al objeto "a" no afecta a ningún elemento del objeto "b".
Las partes privadas de un objeto no pueden ser llamadas por el mismo. Así:

  • a.display_campos();

no estaría permitida.

CONSTRUCTORES

A menudo es necesario inicializar los objetos a fin de que cuando se vayan a utilizar por primera vez tengan ciertos elementos con un valor predefinido o que efectúen operaciones como la de reservar memoria comprobar la hardware etc. Esto se consigue por medio de los llamados constructores los cuales son una función elemento cuyo nombre es el mismo que el de la clase a que pertenecen.

 

  • #include <stdio.h>
    #include <conio.h>
    #include <m_lib.h>
    #include <string.h>
    #include <ctype.h>
    #include "ag_04.h"
    #include <stdlib.h>
    //----------------------------------------
    TClaseAgenda::TClaseAgenda( char *name_data)
  • {

  • int i,len;
    FILE * fp_dat;
    elemento[I_NOMBRE]=dato.nombre;
    elemento[I_APELLIDOS]=dato.apellidos;
    elemento[I_CALLE]=dato.calle;
    elemento[I_DISTRITO]=dato.distrito;
    elemento[I_CIUDAD]=dato.ciudad;
    elemento[I_PAIS]=dato.pais;
    elemento[I_TELPRIV]=dato.telpriv;
    elemento[I_TELTRAB]=dato.teltrab;
    elemento[I_FAX]=dato.fax;
    elemento[I_OBSERVACIONES]=dato.observaciones;

    elemento_len[I_NOMBRE]=LNOMBRE;
    elemento_len[I_APELLIDOS]=LAPELLIDOS;
    elemento_len[I_CALLE]=LCALLE;
    elemento_len[I_DISTRITO]=LDISTRITO;
    elemento_len[I_CIUDAD]=LCIUDAD;
    elemento_len[I_PAIS]=LPAIS;
    elemento_len[I_TELPRIV]=LTELPRIV;
    elemento_len[I_TELTRAB]=LTELTRAB;
    elemento_len[I_FAX]=LFAX;
    elemento_len[I_OBSERVACIONES]=LOBSERVACIONES;

    nombre_campo[I_NOMBRE]=newStr("Nombre: ");
    nombre_campo[I_APELLIDOS]=newStr("Apellidos: ");
    nombre_campo[I_CALLE]=newStr("calle: ");
    nombre_campo[I_DISTRITO]=newStr("Distrito: ");
    nombre_campo[I_CIUDAD]=newStr("Ciudad: ");
    nombre_campo[I_PAIS]=newStr("Pais: ");
    nombre_campo[I_TELPRIV]=newStr("Telpriv: ");
    nombre_campo[I_TELTRAB]=newStr("Teltrab: ");
    nombre_campo[I_FAX]=newStr("Fax: ");
    nombre_campo[I_OBSERVACIONES]=newStr("Observaciones: ");
    maxlen=0;
    /* La extesión del archivo temporal debe de ser transformada en "tmp" y la del arcivo datos en "dat" */
    for(i=0;i<N_ELEMENTOS;i++)

  • {
    if(strlen(nombre_campo[i])>maxlen)
    maxlen=strlen(nombre_campo[i]);
    }

  • posx=20; posy=3;
    len=strlen(name_data);
    for(i=len;i>0 && name_data[i-1]!='.' &&
    name_data[i-1]!=' ';i--); // busca extension
    len+=5; //+5: cuatro para la extension y uno para el `\0'
    name_dat = new char[len]; // reserva espacio
    name_tmp = new char[len];
    strcpy(name_dat,name_data);
    strcpy(name_tmp,name_data);
    if(name_dat[i-1]=='.')name_dat[i-1]=name_tmp[i-1]='0';
    strcat(name_dat,".dat");
    strcat(name_tmp,".tmp");

    /* apertura de los archivos "tmp" y "dat" */

    fp_tmp=abre_archivo(name_tmp,"w+");
    fp_dat=abre_archivo(name_dat,"a+");
    copia_datos(fp_tmp,fp_dat);
    fclose(fp_dat);

    /* pone las variables dato e info a 0 */
    memset(&dato,0,sizeof(TBufferDatos));
    info[0]='\0';
    modificado=1;
  • };

El constructor TClaseAgenda inicializa la variable dato cargando todos sus bytes con el valor `\0' y reserva memoria con la función newStr definida en <m_lib> para todos y cada uno de los punteros del vector nombre_campo. Además configura el nombre del archivo que almacena los datos con la extensión ".dat" y el nombre de un archivo temporal con la extensión ".tmp". sus respectivas variables son:
name_dat, name_tmp
El constructor de un objeto es llamado en el momento de ser declarado, es decir, en su creación.

DESTRUCTORES

El complemento del constructor es el destructor. En numerosas circunstancias, un objeto, en el momento de ser destruido, debe efectuar varias acciones. Como se recordará, el compilador reserva, al principio de cada bloque, memoria para variables y objetos en general. Dicha memoria es liberada cuando la secuencia del programa sale del bloque en el que se encontraban esas variables u objetos. En C++ existe la posibilidad de indicar al compilador qué acciones debe de seguir para liberar la memoria o poner el programa en el estado deseado.
El destructor tiene el mismo nombre que el constructor precedido de una tilde (~).

 

  • TClaseAgenda::~TClaseAgenda( )
  • {

  • fclose(fp_tmp);
    delete name_dat;
    delete name_tmp;
    for(int i=0;i<N_ELEMENTOS;i++)delete nombre_campo[i]
  • }

 

En el ejemplo el destructor deja libre por medio de la instrucción del C++ delete, a toda la memoria a la que apuntaban los punteros del array nombre_campo.

 

  • void main()
  • {

  • TClaseAgenda a("agenda");
    a.entrada_datos();
    b.visualiza_datos();
  • }
 

En el ejemplo anterior al declarar la variable "a" se hace una llamada al constructor el cual inicializa todos los datos. Al acabar el programa s hace automáticamente una llamada al destructor el cual deja libre la memoria reservada para los datos.