sábado, 7 de agosto de 2010

UNIDAD 7
ARCHIVOS

7.1 CLASES DE E/S CLASE BASE ABSTRACTA


A menudo en el diseño, se quiere la clase base para presentar sólo una interfaz para sus clases derivadas. Esto es, se puede querer que nadie pueda crear un objeto de la clase base y que ésta sirva únicamente para hacer un upcast hacia ella, y poder tener una interfaz. Se consigue haciendo a la clase abstract (abstracta), poniendo como mínimo una función virtual pura. Se puede reconocer a una función virtual pura porque usa la palabra reservada virtual y es seguida por =0. Si alguien intenta hacer un objeto de una clase abstracta, el compilador lo impide. Esta es una utilidad que fuerza a un diseño en concreto.


Cuando se hereda una clase abstracta, hay que implementar todas las funciones virtuales, o la clase que hereda se convierte en una nueva clase abstracta. Crear una función virtual pura permite poner una función miembro en una interfaz sin forzar a proveer un cuerpo con código sin significado para esa función miembro. Al mismo tiempo, una función virtual fuerza a las clases que la hereden a que implemente una definición para ellas.

La clase base abstracta Stream es compatible con bytes de lectura y escritura. Stream tiene compatibilidad asincrónica. Sus implementaciones predeterminadas definen lecturas y escrituras asincrónicas según sus correspondientes métodos asincrónicos, y viceversa.
Todas las clases que representan secuencias se derivan de la clase Stream. La clase Stream y sus clases derivadas proporcionan una visión genérica de los orígenes de datos y los repositorios, aislando al programador de los detalles específicos del sistema operativo y sus dispositivos subyacentes.

7.2 REALIZAR ENTRADA Y SALIDA DE TEXTO


EN C:
En C un archivo puede ser cualquier cosa, desde un archivo de disco a un terminal o una impresora. Se asocia una secuencia con un archivo específico realizando una operación de apertura, una vez que está abierto, la información puede ser intercambiada entre éste y el programa. El puntero a un archivo es el hilo que unifica el sistema de E/S con buffer. Un puntero a un archivo es un puntero a una información que define varias cosas sobre él, incluyendo el nombre, el estado y la posición actual del archivo. En esencia, el puntero a un archivo identifica un archivo en disco específico y utiliza la secuencia asociada para dirigir el funcionamiento de las funciones de E/S con buffer. Para obtener una variable de tipo puntero a archivo se debe utilizar una sentencia como la siguiente:FILE *punt;

La función fopen() abre una secuencia para que pueda ser utilizada y le asocia a un archivo. Su prototipo es: FILE *fopen(const char *nombre_archivo, const char *modo); Donde nombre_archivo es un puntero a una cadena de caracteres que representan un nombre válido del archivo y puede incluir una especificación de directorio. La cadena que apunta modo determina cómo se abre el archivo.

Los modos son los siguientes:

r: Abre un archivo de texto para lectura.
w: Crea un archivo de texto par escritura
a: Abre un archivo de texto para añadir
r+: Abre un archivo de texto para lectura/escritura
w+: Crea un archivo de texto para lectura/escritura
a+: Añade o crea un archivo de texto para lectura/escritura


La función fclose() cierra una secuencia que fue abierta mediante una llamada a fopen(). Escribe toda la información que todavía se encuentre en el buffer del disco y realiza un cierre formal del archivo a nivel del sistema operativo. También libera el bloque de control de archivo asociado con la secuencia, dejándolo libre para su reutilización. A veces es necesario cerrar algún archivo para poder abrir otro, debido a la existencia de un límite del sistema operativo en cuanto al número de archivos abiertos. Su prototipo es: int fclose(FILE *fp);

La función putc() escribe caracteres en un archivo que haya sido abierto previamente para operaciones de escritura, utilizando la función fopen(). Su prototipo es: int putc(int car, FILE *pf);

La función getc() escribe caracteres en un archivo que haya sido abierto, en modo lectura, mediante fopen(). Su prototipo es: int getc(FILE *pf);La función fputs() escribe la cadena en la secuencia especificada. Su prototipo es: fputs() escribe la cadena en la secuencia especificada. Su prototipo es: int fputs(const char *cad, FILE *pf);La función fgets() lee una cadena de la secuencia especificada hasta que se lee un carácter de salto de línea o hasta que se han leído longitud-1 caracteres.

La función rewind() inicia el indicador de posición al principio del archivo indicado por su argumento. Su prototipo es: rewind() inicia el indicador de posición al principio del archivo indicado por su argumento. Su prototipo es: void rewind(FILE *pf);

Existen otras muchas funciones en la biblioteca estándar de C como pueden ser:

remove(): Borra el archivo especificado.
fflush(): Vacía el contenido de una secuencia de salida.
fread(): Lee tipos de datos que ocupan más de un byte. Permiten la lectura de bloques de cualquier tipo de datos.
fwrite(): Escribe tipos de datos que ocupan más de un byte. Permiten la escritura de bloques de cualquier tipo de datos.
fprintf(): Hace las funciones de printf() sobre un fichero.
fscanf(): Hace las funciones de scanf() sobre un fichero.
feof(): Detecta el final de un fichero.
ferror(): Detecta un error en la lectura/escritura de un fichero.
fclose(): cierra una secuencia que fue abierta mediante una llamada a fopen().
putc(): escribe caracteres en un archivo que haya sido abierto previamente para operaciones de escritura, utilizando la función fopen().
getc(): escribe caracteres en un archivo que haya sido abierto, en modo lectura, mediante fopen(). Su prototipo es:
fputs(): escribe la cadena en la secuencia especificada. Su prototipo es: int fputs(const char *cad, FILE *pf);
fgets(): lee una cadena de la secuencia especificada hasta que se lee un carácter de salto de línea o hasta que se han leído longitud-1 caracteres. Su prototipo es: fgets() lee una cadena de la secuencia especificada hasta que se lee un carácter de salto de línea o hasta que se han leído longitud-1 caracteres. Su prototipo es:int fgets(char *cad, FILE *pf);


EN C++:
Cómo podemos trabajar con un stream simultáneamente en entrada y salida. Para eso usaremos la clase fstream, que al ser derivada de ifstream y ofstream, dispone de todas las funciones necesarias para realizar cualquier operación de entrada o salida.
Hay que tener la precaución de usar la opción ios::trunc de modo que el fichero sea creado si no existe previamente.
#include
using namespace std;

int main() {
char l;
long i, lon;
fstream fich("prueba.dat", ios::in
ios::out ios::trunc ios::binary);

fich << "abracadabra" << lon =" fich.tellg();" i =" 0L;">
fich.seekg(i, ios::beg); fich.get(l); if(l == 'a')
{
fich.seekp(i, ios::beg); fich << 'e';
}
}
cout << "Salida:" << i =" 0L;">
{

fich.get(l); cout <<>

}

cout <<>
cin.get();
return 0;
}

Este programa crea un fichero con una palabra, a continuación lee todo el fichero e cambia todos los caracteres 'a' por 'e'. Finalmente muestra el resultado. Básicamente muestra cómo trabajar con ficheros simultáneamente en entrada y salida.

7.3 LEER Y ESCRIBIR ARCHIVOS

Leer archivos: Ifstream
Proporciona una interfaz para leer datos de archivos como flujos de entrada.

Los objetos de esta clase tratan de mantener internamente un puntero a un objeto filebuf que se pueden obtener llamando a rdbuf miembros.

El archivo que se asocia con la corriente puede ser especificado como un parámetro en el constructor o llamando a los miembros abierta.

Después de todas las operaciones necesarias en un archivo se han realizado, puede ser cerrado (o disociada) llamando a cerrar miembros. Una vez cerrado, el mismo objeto de secuencia de archivo puede ser usado para abrir otro archivo.

El is_open función miembro puede ser usado para determinar si el objeto de secuencia está asociada con un archivo.

Escribir archivos: Ofstream

Proporciona una interfaz para escribir datos en archivos como los flujos de salida.

Los objetos de esta clase de mantener internamente un puntero a un objeto filebuf que se pueden obtener llamando a rdbuf miembros.

El archivo que se asocia con la corriente puede ser especificado como un parámetro en el constructor o llamando a los miembros abierta.

Después de todas las operaciones necesarias en un archivo se han realizado, puede ser cerrado (o disociada) llamando a cerrar miembros. Una vez cerrado, el mismo objeto de secuencia de archivo puede ser usado para abrir otro archivo.

El is_open función miembro puede ser usado para determinar si el objeto de secuencia está asociada con un archivo.

7.4 REALIZAR ENTRADA Y SALIDA BINARIA

Muchos sistemas operativos distinguen entre ficheros de texto y ficheros binarios. Por ejemplo, en MS-DOS, los ficheros de texto sólo permiten almacenar caracteres. En otros sistemas no existe tal distinción, todos los ficheros son binarios. En esencia esto es más correcto, puesto que un fichero de texto es un fichero binario con un rango limitado para los valores que puede almacenar. En general, usaremos ficheros de texto para almacenar información que pueda o deba ser manipulada con un editor de texto. Un ejemplo es un fichero fuente C++.
Los ficheros binarios son más útiles para guardar información cuyos valores no estén limitados. Por ejemplo, para almacenar imágenes, o bases de datos. Un fichero binario permite almacenar estructuras completas, en las que se mezclen datos de cadenas con datos numéricos. En realidad no hay nada que nos impida almacenar cualquier valor en un fichero de texto, el problema surge cuando se almacena el valor que el sistema operativo usa para marcar el fin de fichero en un archivo de texto. En MS-DOS ese valor es 0x1A. Si abrimos un fichero en modo de texto que contenga un dato con ese valor, no nos será posible leer ningún dato a partir de esa posición. Si lo abrimos en modo binario, ese problema no existirá. Los ficheros que hemos usado en los ejemplos anteriores son en modo texto, veremos ahora un ejemplo en modo binario:

#include
#include
struct tipoRegistro {
char nombre[32];
int edad;
float altura;
};

int main() {
tipoRegistro pepe;
tipoRegistro pepe2;
ofstream fsalida("prueba.dat",
ios::out ios::binary);

strcpy(pepe.nombre, "Jose Luis");
pepe.edad = 32;
pepe.altura = 1.78;

fsalida.write(reinterpret_cast(&pepe),
sizeof(tipoRegistro));
fsalida.close();

ifstream fentrada("prueba.dat",
ios::in ios::binary);

fentrada.read(reinterpret_cast(&pepe2),
sizeof(tipoRegistro));
cout <<>fentrada.close();

cin.get();
return 0;
}

Al declarar streams de las clases ofstream o ifstream y abrirlos en modo binario, tenemos que añadir el valor ios::out e ios::in, respectivamente, al valor ios::binary. Esto es necesario porque los valores por defecto para el modo son ios::out e ios:in, también respectivamente, pero al añadir el flag ios::binary, el valor por defecto no se tiene en cuenta.


Cuando trabajemos con streams binarios usaremos las funciones write y read. En este caso nos permiten escribir y leer estructuras completas.


En general, cuando usemos estas funciones necesitaremos hacer un casting, es recomendable usar el operador "reinterpret_cast".


BIBLIOGRAFIA
http://www.tutoriales.itsa.edu.mx/programacion1/index.php?mod=clasesdees&ban=0











UNIDAD 6
POLIMORFISMO

6.1 CONCEPTO DE POLIMORFISMO


Permite al programador generar componentes reutilizables de alto nivel que puedan adaptarse a diferentes aplicaciones mediante el cambio de sus partes de bajo nivel.


En programación orientada a objetos se denomina polimorfismo a la capacidad del código de un programa para ser utilizado con diferentes tipos de datos u objetos. También se puede aplicar a la propiedad que poseen algunas operaciones de tener un comportamiento diferente dependiendo del objeto (o tipo de dato) sobre el que se aplican.


El concepto de polimorfismo se puede aplicar tanto a funciones como a tipos de datos. Así nacen los conceptos de funciones polimórficas y tipos polimórficos. Las primeras son aquellas funciones que pueden evaluarse o ser aplicadas a diferentes tipos de datos de forma indistinta; los tipos polimórficos, por su parte, son aquellos tipos de datos que contienen al menos un elemento cuyo tipo no está especificado.


Se puede clasificar el polimorfismo en dos grandes clases:


Polimorfismo dinámico: (o polimorfismo ad hoc) es aquél en el que el código no incluye ningún tipo de especificación sobre el tipo de datos sobre el que se trabaja. Así, puede ser utilizado a todo tipo de datos compatible.


•Polimorfismo estático: (o polimorfismo paramétrico) es aquél en el que los tipos a los que se aplica el polimorfismo deben ser explicitados y declarados uno por uno antes de poder ser utilizados. El polimorfismo dinámico unido a la herencia es lo que en ocasiones se conoce como programación genérica.


También se clasifica en herencia por redefinición de métodos abstractos y por método sobrecargado. El segundo hace referencia al mismo método con diferentes


6.2 CLASE BASE ABSTRACTA
En C++ es posible definir clases abstractas. Una clase abstracta, o clase base abstracta (ABC), es una que está diseñada sólo como clase padre de las cuales se deben derivar clases hijas. Una clase abstracta se usa para representar aquellas entidades o métodos que después se implementarán en las clases derivadas, pero la clase abstracta en sí no contiene ninguna implementación solamente representa los métodos que se deben implementar. Por ello, no es posible instanciar una clase abstracta, pero sí una clase concreta que implemente los métodos definidos en ella.


Las clases abstractas son útiles para definir interfaces, es decir, un conjunto de métodos que definen el comportamiento de un módulo determinado. Estas definiciones pueden utilizarse sin tener en cuenta la implementación que se hará de ellos.


En C++ los métodos de las clases abstractas se definen como funciones virtuales puras:

class Abstracta
{
public:
virtual int metodo() = 0;
};

class ConcretaA : public Abstracta
{

6.3 SUBPROGRAMAS VIRTUALES


Las funciones virtuales permiten que clases derivadas de una misma base (clases hermanas) puedan tener diferentes versiones de un método. Se utiliza la palabra-clave virtual para avisar al compilador que un método será polimórfico y que en las clases derivadas existen distintas definiciones del mismo.


Para declarar que un método de una clase base es virtual, su prototipo se declara como siempre, pero anteponiendo la palabra-clave virtual, que indica al compilador algo así como: "Será definido más tarde en una clase derivada". Ejemplo:
Sintaxis: virtual void dibujar();

Una función virtual o método virtual es una función cuyo comportamiento, al ser declarado "virtual", es determinado por la definición de una función con la misma cabecera en alguna de sus subclases. Este concepto es una parte muy importante del polimorfismo en la POO.


El concepto de función virtual soluciona los siguientes problemas:
En POO, cuando una clase derivada hereda de una clase base, un objeto de la clase derivada puede ser referido tanto como del tipo de la clase base como del tipo de la clase derivada. Si hay funciones de la clase base redefinidas por la clase derivada, aparece un problema cuando un objeto derivado ha sido cohercionado como del tipo de la clase base. Cuando un objeto derivado es referido como del tipo de la base, el comportamiento de la llamada a la función deseado es ambiguo.


Distinguir entre virtual y no virtual sirve para resolver este problema. Si la función en cuestión es designada "virtual", se llamará a la función de la clase derivada (si existe). Si no es virtual, se llamará a la función de la clase base.


6.4 DESTRUCTORES VIRTUALES


Como cualquier otra función miembro, los destructores pueden ser declarados virtuales. El destructor de una clase derivada de otra cuyo destructor es virtual, también es virtual.

La existencia de un destructor virtual permite que un objeto de una subclase pueda ser correctamente destruido por un puntero a su clase-base.

Ejemplo:
class B { // Superclase (polimórfica)
...
virtual ~B(); // Destructor virtual
};

class D : public B { // Subclase (deriva de B)
...
~D(); // destructor también virtual
};

void func() {
B* ptr = new D; // puntero a superclase asignado a objeto de subclase
delete ptr; // Ok: delete es necesario siempre que se usa new
}

En el ejemplo anterior el mecanismo de llamada de las funciones virtuales permite que el operador delete invoque al destructor correcto, es decir, al destructor ~D de la subclase, aunque se invoque mediante el puntero ptr a la superclase B*. Si el destructor no hubiese sido virtual no se hubiese invocado el destructor derivado ~D, sino el de la superclase ~B, dando lugar a que los miembros privativos de la subclase no hubiesen sido desasignados. Tendríamos aquí un caso típico de "misteriosas" pérdidas de memoria, tan frecuentes en los programas C++ como difíciles de depurar.



BIBLIOGRAFIA
http://www.tutoriales.itsa.edu.mx/programacion1/index.php?mod=concpolimorfismo&ban=0

UNIDAD 5 HERENCIA
5.1 IMPORTANCIA DE LA HERENCIA EN LA POO

La herencia es la última de las propiedades relativas a la OOP, Consiste en la propagación de los atributos y las operaciones a través de distintas sub-clases definidas a partir de una clase común.
Introduce, por tanto, una posibilidad de refinamiento sucesivo del concepto de clase. Nos permite definir una clase principal y , a través de sucesivas aproximaciones, cualquier característica de los objetos. A partir de ahora definiremos como sub-clases todas aquellas clases obtenidas mediante refinamiento de una (o varias) clases principales.
La herencia nos permite crear estructuras jerárquicas de clases donde es posible la creación de sub-clases que incluyan nuevas propiedades y atributos. Estas sub-clases admiten la definición de nuevos atributos, así como crear, modificar o inhabilitar propiedades.
Para pensarlo de manera más fácil podemos abstraernos al siguiente ejemplo:
Pensemos en los distintos sub-modelo s asociados a un modelo básico de automóvil. A partir de este modelo básico, los fabricantes introducen distintas características (aire acondicionado, ABS, distintas tapicerías, acabados, etc.) que crean sub - clases. Todas estas sub-clases tienen en común la estructura básica (chasis, dirección, etc.) u varían sólo en algunos de sus componentes.
Asociemos a este tipo básico una clase cuyos atributos representen las piezas que componen el coche. Las sub-clases aportarán sus propios atributos (en el caso de vehículos con aire acondicionado, todos aquellas piezas que lo componen), permitiendo la definición de todos los posibles modelos.


Además, es posible que una sub-clase herede atributos y propiedades de más de una clase. Este proceso se denomina herencia múltiple y lo veremos con más detalle en capítulos posteriores.
La herencia es, sin duda alguna, una de las propiedades más importantes de la OOP, ya que permite, a través de la definición de una clase básica, ir añadiendo propiedades a medida que sean necesarias y, además, en el sub-conjunto de objetos que sea preciso.

La herencia permite que los objetos pueden compartir datos y comportamientos a través de las diferentes sub-clases, sin incurrir en redundancia. Más importante que el ahorro de código, es la claridad que aporta al identificar que las distintas operaciones sobre los objetos son en realidad una misma cosa.

La herencia es una propiedad esencial de la Programación Orientada a Objetos que consiste en la creación de nuevas clases a partir de otras ya existentes. Este término ha sido prestado de la Biología donde afirmamos que un niño tiene la cara de su padre, que ha heredado ciertas facetas físicas o del comportamiento de sus progenitores.
La herencia es la característica fundamental que distingue un lenguaje orientado a objetos, como el C++ o Java, de otro convencional como C, BASIC, etc. Java permite heredar a las clases características y conductas de una o varias clases denominadas base. Las clases que heredan de clases base se denominan derivadas, estas a su vez pueden ser clases bases para otras clases derivadas. Se establece así una clasificación jerárquica, similar a la existente en Biología con los animales y las plantas.

La herencia ofrece una ventaja importante, permite la reutilización del código. Una vez que una clase ha sido depurada y probada, el código fuente de dicha clase no necesita modificarse. Su funcionalidad se puede cambiar derivando una nueva clase que herede la funcionalidad de la clase base y le añada otros comportamientos. Reutilizando el código existente, el programador ahorra tiempo y dinero, ya que solamente tiene que verificar la nueva conducta que proporciona la clase derivada.


La herencia es la última de las propiedades relativas a la OOP, Consiste en la propagación de los atributos y las operaciones a través de distintas sub-clases definidas a partir de una clase común.
Introduce, por tanto, una posibilidad de refinamiento sucesivo del concepto de clase. Nos permite definir una clase principal y , a través de sucesivas aproximaciones, cualquier característica de los objetos. A partir de ahora definiremos como sub-clases todas aquellas clases obtenidas mediante refinamiento de una (o varias) clases principales.

La herencia nos permite crear estructuras jerárquicas de clases donde es posible la creación de sub-clases que incluyan nuevas propiedades y atributos. Estas sub-clases admiten la definición de nuevos atributos, así como crear, modificar o inhabilitar propiedades.

Para pensarlo de manera más fácil podemos abstraernos al siguiente ejemplo:
Pensemos en los distintos sub-modelo s asociados a un modelo básico de automóvil. A partir de este modelo básico, los fabricantes introducen distintas características (aire acondicionado, ABS, distintas tapicerías, acabados, etc.) que crean sub - clases. Todas estas sub-clases tienen en común la estructura básica (chasis, dirección, etc.) u varían sólo en algunos de sus componentes.
Asociemos a este tipo básico una clase cuyos atributos representen las piezas que componen el coche. Las sub-clases aportarán sus propios atributos (en el caso de vehículos con aire acondicionado, todos aquellas piezas que lo componen), permitiendo la definición de todos los posibles modelos.


Además, es posible que una sub-clase herede atributos y propiedades de más de una clase. Este proceso se denomina herencia múltiple y lo veremos con más detalle en capítulos posteriores.
La herencia es, sin duda alguna, una de las propiedades más importantes de la OOP, ya que permite, a través de la definición de una clase básica, ir añadiendo propiedades a medida que sean necesarias y, además, en el sub-conjunto de objetos que sea preciso.

La herencia permite que los objetos pueden compartir datos y comportamientos a través de las diferentes sub-clases, sin incurrir en redundancia. Más importante que el ahorro de código, es la claridad que aporta al identificar que las distintas operaciones sobre los objetos son en realidad una misma cosa.
La herencia es una propiedad esencial de la Programación Orientada a Objetos que consiste en la creación de nuevas clases a partir de otras ya existentes. Este término ha sido prestado de la Biología donde afirmamos que un niño tiene la cara de su padre, que ha heredado ciertas facetas físicas o del comportamiento de sus progenitores.

La herencia es la característica fundamental que distingue un lenguaje orientado a objetos, como el C++ o Java, de otro convencional como C, BASIC, etc. Java permite heredar a las clases características y conductas de una o varias clases denominadas base. Las clases que heredan de clases base se denominan derivadas, estas a su vez pueden ser clases bases para otras clases derivadas. Se establece así una clasificación jerárquica, similar a la existente en Biología con los animales y las plantas.
La herencia ofrece una ventaja importante, permite la reutilización del código. Una vez que una clase ha sido depurada y probada, el código fuente de dicha clase no necesita modificarse. Su funcionalidad se puede cambiar derivando una nueva clase que herede la funcionalidad de la clase base y le añada otros comportamientos. Reutilizando el código existente, el programador ahorra tiempo y dinero, ya que solamente tiene que verificar la nueva conducta que proporciona la clase derivada.


5.2 JERARQUIA DE HERENCIA


La Jerarquía es una propiedad que permite la ordenación de las abstracciones.
Las dos jerarquías más importantes de un sistema complejo son:
• Estructura de clases (jerarquía “es-un” (is-a): generalización/especialización).
• Estructura de objetos (jerarquía “parte-de” (part-of): agregación).


Las jerarquías de generalización/especialización se conocen como herencia. Básicamente, la herencia define una relación entre clases, en donde una clase comparte la estructura o comportamiento definido en una o más clases (herencia simple y herencia múltiple, respectivamente).
La agregación es el co

ncepto que permite el agrupamiento físico de estructuras relacionadas lógicamente. Así, un camión se compone de ruedas, motor, sistema de transmisión y chasis; en consecuencia, camión es una agregación, y ruedas, motor, transmisión y chasis son agregados de camión

Las jerarquías de herencia son más fáciles de implementar que de diseñar, por lo que es arriesgado comenzar a codificar antes de identificar claramente las necesidades. La corrección de errores de diseño en una jerarquía de clases tras la implementación puede requerir cambios que deshabilitan las aplicaciones existentes. En esta sección se explican consideraciones del diseño de jerarquía de herencia y se proporciona información que ayuda a evitar estos errores.



5.2.1 CONCEPTOS DE HERENCIA SIMPLE Y MULTIPLE

Herencia Simple
En C++ es un mecanismo de abstracción creado para poder facilitar y mejorar el diseño de las clases de un programa. Con ella se pueden crear nuevas clases a partir de clases ya hechas, siempre y cuando tengan un tipo de relación especial.

En la herencia, las clases derivadas "heredan" los datos y las funciones miembro de las clases base, pudiendo las clases derivadas redefinir estos comportamientos (polimorfismo) y añadir comportamientos nuevos propios de las clases derivadas. Para no romper el principio de encapsulamiento (ocultar datos cuyo conocimiento no es necesario para el uso de las clases), se proporciona un nuevo modo de visibilidad de los datos/funciones: "protected". Cualquier cosa que tenga visibilidad protected se comportará como pública en la clase Base y en las que componen la jerarquía de herencia, y como privada en las clases que NO sean de la jerarquía de la herencia.

Antes de utilizar la herencia, nos tenemos que hacer una pregunta, y si tiene sentido, podemos intentar usar esta jerarquía:
Si la frase ES-UN tiene sentido, entonces estamos ante un posible caso de herencia donde clase A será la clase base y clase B la derivada.
Ejemplo: clases Barco, Acorazado, Carguero, etc. un Acorazado ES-UN Barco, un Carguero ES-UN Barco, un Trasatlántico ES-UN Barco, etc.

En este ejemplo tendríamos las cosas generales de un Barco (en C++)
class Barco
{
protected:
char* nombre;
float peso;
public:
//Constructores y demás funciones básicas de barco
};

y ahora las características de las clases derivadas, podrían (a la vez que heredan las de barco) añadir cosas propias del subtipo de barco que vamos a crear, por ejemplo:


class Carguero: public Barco { // Esta es la manera de especificar que hereda de Barco
private:
float carga;
//El resto de cosas
};

class Acorazado: public Barco {
private:
int numeroArmas;
int Soldados;
// Elresto de cosas
};

Por último, hay que mencionar que existen 3 clases de herencia que se diferencian en el modo de manejar la visibilidad de los componentes de la clase resultante:
Herencia pública (class Derivada: public Base ) : Con este tipo de herencia se respetan los comportamientos originales de las visibilidades de la clase Base en la clase Derivada.
• Herencia privada (clase Derivada: private Base) : Con este tipo de herencia todo componente de la clase Base, será privado en la clase Derivada (ojo! siempre será privado aunque ese dato fuese público en la clase Base)
Herencia protegida (clase Derivada: protected Base) : Con este tipo de herencia, todo componente público y protegido de la clase Base, será protegido en la clase Derivada, y los componentes privados, siguen siendo privados.

Herencia Multiple


Es el mecanismo que permite al programador hacer clases derivadas a partir, no de una sola clase base, sino de varias. Para entender esto mejor, pongamos un ejemplo: Cuando ves a quien te atiende en una tienda, como persona que es, podrás suponer que puede hablar, comer, andar, pero, por otro lado, como empleado que es, también podrás suponer que tiene un jefe, que puede cobrarte dinero por la compra, que puede devolverte el cambio, etc.
Si esto lo trasladamos a la programación sería herencia múltiple (clase empleado_tienda):
class Persona {
...
Hablar();
Caminar();
...
};

class Empleado {
Persona jefe;
int sueldo;
Cobrar();
...
};

class empleado_tienda: public Persona, Empleado {
...
AlmacenarStock();
ComprobarExistencias();
...
};
Por tanto, es posible utilizar más de una clase para que otra herede sus características.


5.2.2 PRINCIPIOS GENERALES DE DISEÑO DE JERARQUIAS

Incluso las jerarquías de clases bien diseñadas necesitan evolucionar con el tiempo. Las opciones iniciales que elija en el momento de diseñar una jerarquía de clases pueden simplificar su trabajo posteriormente.
Extender jerarquías de clases
En la siguiente lista se incluyen sugerencias para simplificar la extensión de las jerarquías de clases:
• Las jerarquías se definen desde lo general a lo específico. Defina las clases en cada nivel de una jerarquía de herencia de la forma más genérica posible. Las clases derivadas pueden heredar, reutilizar y extender métodos de clases base.
Por ejemplo, suponga que está diseñando una jerarquía de clases que modela el hardware del equipo. Al comenzar a modelar los dispositivos de salida, podría definir clases denominadas Display, Printer y File. Después podría definir las clases que implementan los métodos definidos en las clases base. Por ejemplo, la clase LCD Display? puede haber derivado de Display e implementar un método denominado Enter Power Save Mode?.
• Defina los tipos de datos y el almacenamiento con generosidad a fin de evitar cambios difíciles posteriormente. Por ejemplo, podría considerar el uso de una variable de tipo Long aunque los datos actuales sólo requieran una variable estándar Integer.
• Exponga sólo los elementos que las clases derivadas necesiten. Los campos y métodos Private reducen los conflictos de denominación y protegen a otros usuarios del uso de elementos que pueden necesitar cambios posteriormente.
• Los miembros que sólo sean necesarios para las clases derivadas deben marcarse como Protected. Esto garantiza que sólo las clases derivadas dependen de estos miembros y facilita la actualización de estos miembros durante el desarrollo.
• Asegúrese de que los métodos de clase base no dependen de miembros Overridable cuya funcionalidad pueden cambiar las clases herederas. Existen dos tipos de jerarquizar: La primera consiste en derivar una clase de otra, es el caso mas habitual y se llama “Herencia”. La otra consiste en encapsular un objeto como miembro de otro, a esta se le llama “Encapsulamiento”.
La unica regla que tenemos que tener en mente es: Si un objeto se relaciona con otro de la forma “es un” hay que usar la herencia publica. Si por el contrario la relacion se describe mejor de la forma “tiene un” hay que usar el encapsulamiento.
Una forma sencilla de ver la diferencia entre los dos tipos es con este ejemplo:
• La clase C Mercedes? “es un” tipo de la clase C Car?
• La clase C Mercedes “tiene un” tipo de case C Volante?

5.2.3 ESPECIFICADORES DE ACCESO A JERARQUIA DE CLASES

Creamos un objeto c de la clase Círculo situado en el punto (0, 0) y de 5.5 unidades de radio. Calculamos y mostramos el valor de su área:
Circulo c=new Circulo(0, 0, 5.5);
System.out.println("Area del círculo "+c.area());
Creamos un objeto r de la clase Rectangulo situado en el punto (0, 0) y de dimensiones 5.5 de anchura y 2 unidades de largo. Calculamos y mostramos el valor de su área:
Rectangulo r=new Rectangulo(0, 0, 5.5, 2.0);
System.out.println("Area del rectángulo "+r.area());
Veamos ahora, una forma alternativa, guardamos el valor devuelto por new al crear objetos de las clases derivadas en una variable f del tipo Figura (clase base):
Figura f=new Circulo(0, 0, 5.5);
System.out.println("Area del círculo "+f.area());
f=new Rectangulo(0, 0, 5.5, 2.0);
System.out.println("Area del rectángulo "+f.area());


5.3 DEFINICION DE UNA CLASE BASE

Los programadores crean clases base:


1. Cuando se dan cuenta que diversos tipos tienen algo en común, por ejemplo en el juego del ajedrez peones, alfiles, rey, reina, caballos y torres, son piezas del juego. Creamos, por tanto, una clase base y derivamos cada pieza individual a partir de dicha clase base.

2. Cuando se precisa ampliar la funcionalidad de un programa sin tener que modificar el código existente.

Ejemplo que simule la utilización de librerías de clases para crear un interfaz gráfico de usuario como Windows 3.1 o Windows 95.

Supongamos que tenemos una clase que describe la conducta de una ventana muy simple, aquella que no dispone de título en la parte superior, por tanto no puede desplazarse, pero si cambiar de tamaño actuando con el ratón en los bordes derecho e inferior.
La clase Ventana tendrá los siguientes miembros dato: la posición x e y de la ventana, de su esquina superior izquierda y las dimensiones de la ventana: ancho y alto.
public class Ventana {
protected int x;
protected int y;
protected int ancho;
protected int alto;
public Ventana(int x, int y, int ancho, int alto) {
this.x=x;
this.y=y;
this.ancho=ancho;
this.alto=alto;
}
//...
}


5.4 DEFINICION DE UNA CLASE DERIVADA

Incrementamos la funcionalidad de la clase Ventana definiendo una clase derivada denominada VentanaTitulo. Los objetos de dicha clase tendrán todas las características de los objetos de la clase base, pero además tendrán un título, y se podran desplazar (se simula el desplazamiento de una ventana con el ratón).



La clase derivada heredará los miembros dato de la clase base y las funciones miembro, y tendrá un miembro dato más, el título de la ventana.
public class VentanaTitulo extends Ventana{
protected String titulo;
public VentanaTitulo(int x, int y, int w, int h, String nombre) {
super(x, y, w, h);
titulo=nombre;
}
extends es la palabra reservada que indica que la clase VentanaTitulo deriva, o es una subclase, de la clase Ventana.
La primera sentencia del constructor de la clase derivada es una llamada al constructor de la clase base mediante la palabra reservada super. La llamada
super(x, y, w, h);
Inicializa los cuatro miembros dato de la clase base Ventana: x, y, ancho, alto. A continuación, se inicializa los miembros dato de la clase derivada, y se realizan las tareas de inicialización que sean necesarias. Si no se llama explícitamente al constructor de la clase base Java lo realiza por nosotros, llamando al constructor por defecto si existe.
La función miembro denominada desplazar cambia la posición de la ventana, añadiéndoles el desplazamiento.

public void desplazar(int dx, int dy){
x+=dx;
y+=dy;
}
Redefine la función miembro mostrar para mostrar una ventana con un título:
public void mostrar(){
super.mostrar();
System.out.println("titulo : "+titulo);
}

En la clase derivada se define una función que tiene el mismo nombre y los mismos parámetros que la de la clase base. Se dice que redefinimos la función mostrar en la clase derivada. La función miembro mostrar de la clase derivada VentanaTitulo hace una llamada a la función mostrar de la clase base Ventana, mediante
super.mostrar();
De este modo aprovechamos el código ya escrito, y le añadimos el código que describe la nueva funcionalidad de la ventana por ejemplo, que muestre el título.
Si nos olvidamos de poner la palabra reservada super llamando a la función mostrar, tendríamos una función recursiva. La función mostrar llamaría a mostrar indefinidamente.
public void mostrar(){ //¡ojo!, función recursiva
System.out.println("titulo : "+titulo);
mostrar();
}
La definición de la clase derivada VentanaTitulo, será la siguiente:
package ventana;
public class VentanaTitulo extends Ventana{
protected String titulo;
public VentanaTitulo(int x, int y, int w, int h, String nombre) {
super(x, y, w, h);
titulo=nombre;
}
public void mostrar(){
super.mostrar();
System.out.println("titulo : "+titulo);
}
public void desplazar(int dx, int dy){
x+=dx;
y+=dy;
}
}

C++ utiliza un sistema de herencia jerárquica. Es decir, se hereda una clase de otra, creando nuevas clases a partir de las clases ya existentes. Sólo se pueden heredar clases, no funciones ordinarias n variables, en C++. Una clase derivada hereda todos los miembros dato excepto, miembros dato estático, de cada una de sus clases base. Una clase derivada hereda la función miembro de su clase base. Esto significa que se hereda la capacidad para llamar a funciones miembro de la clase base en los objetos de la clase derivada.




5.4.1 CONSTRUCTORES Y DESTRUCTORES DE CLASES DERIVADAS

Constructores en clases derivadas
Al instanciar objetos de clases derivadas se inicia una cadena de invocaciones a constructores en las cuales el constructor de la clase derivada, antes de realizar sus propias tareas, invoca (ya sea implícita o explícitamente) al constructor de su clase base. Similarmente, si la clase base fue derivada de otra clase, el constructor de la clase base debe invocar al constructor de la clase ubicada en el siguiente nivel superior de la jerarquía, y así sucesivamente. El último constructor invocado en la cadena es el constructor de la clase Object, cuyo cuerpo se ejecuta primero. El cuerpo del constructor de la clase derivada se ejecuta al final. El constructor de cada clase base inicializa las variables de instancia que el objeto de la clase derivada hereda.
Destructores en clases derivadas
Cuando remueve de la memoria un objeto de una clase derivada, el recolector de basura invoca al destructor del objeto. Esto inicia una cadena de invocaciones a destructores, en donde el destructor de la clase derivada y los destructores de las clases bases directas e indirectas se ejecutan en orden inverso al que se ejecutaron los constructores, esto es, primero se ejecuta el destructor de la clase derivada y al final se ejecuta el destructor de la clase base ubicada en el nivel superior de la jerarquía. La ejecución de los destructores debe liberar todos los recursos que el objeto adquirió, antes de que el recolector de basura reclame la memoria de ese objeto. Cuando el recolector de basura invoca al destructor de un objeto de una clase derivada, ese destructor realiza su tarea y después invoca al destructor de la clase base. El proceso se repite hasta que se invoca al destructor de la clase Object.


Ejemplo:
// Destruct Derivadas?.cs :
Destructores en clases derivadas. using C = System.Console;
class Animal { ~ Animal( )
{ C.Write Line?(“Muere mi parte Animal …”); } }
class Mamífero : Animal { ~ Mamífero( )
{ C.Write Line(“Muere mi parte Mamífero …”); } }
class Perro : Mamífero { ~ Perro( )
{ C.Write Line(“Muere mi parte Perro …”); } }
public class Principal { static void Main( ) { Perro Fido = new Perro ( ); } }


5.4.2 CONVERSION IMPLICITA DE OBJ. DE CLASE DERIBADA A OBJ. DE CLASE BASE

La programación orientada a objetos extiende los tipos abstractos de datos permitiendo relaciones tipo-subtipo. Esto es alcanzado a través de un mecanismo denominado herencia. Más que reimplementar características compartidas, una clase puede heredar datos y funciones miembros de otras clases. En C++, este mecanismo es implementado a través de la derivación de clases.
Especificación de derivación
Veamos el siguiente ejemplo:



// clases que sirven como clases base
class Animal{...};
class EnExtincion{...};
class Carnivoro{...};
class Herbivoro{...};
// Derivación simple
class Oso:public Animal { ...};

// Derivaciones múltiples
class Gato : public Animal, Carnivoro {...};
class Panda : private EnExtincion, public Oso, private Herbivoro {...};

Si se omite un atributo de público,privado o protegido a la clase base, esta es manipulada como privada. En el caso de la clase gato, Carnivoro es una clase base privada.
Una clase derivada puede servir como clase base para subsecuentes derivaciones.

La sintaxis para definir clases bases es igual a definir clases ordinarias, con las siguientes dos excepciones:
• Miembros que se quiere sean heredados por clases derivadas, pero no públicos son declarados miembros protegidos.
• Funciones miembro cuya implementación depende de detalles de representación de subsecuentes derivaciones y que son desconocidas al tiempo del diseño de la clase base son declaradas como funciones virtuales.
El acceso desde clases derivadas a miembros heredados es como si fueran miembros propios (depende del tipo de herencia realizada). Tambien puede usarse el class scope operator es decir, para acceder al miembro A de la clase ClassA desde el objeto Obj de la clase ClassB sería Obj.ClassA::A o bien Obj.A.
Existen dos casos que requieren el uso de la notación extendida:
• Cuando el nombre del miembro heredado es reusado.
• Cuando 2 o más clases bases definen un miembro heredado con el mismo nombre.
class ObjetoGrafico
{
protected:
int Visible;
int X,Y;
public:
ObjetoGrafico(int A, int B): X(A), Y(B), Visible(0){};
virtual void Draw() = 0;
virtual void Hide() = 0;
virtual ~ObjetoGrafico(){}
};
class Circulo: public ObjetoGrafico
{
private:
int Radio;
public:
Circulo(int A, int B, int R):ObjetoGrafico(A,B), Radio(R){};
void Draw(){...};
void Hide(){...};
~ObjetoGrafico(){Hide();}
};
class CirduloLleno: public Circulo
{
private:
int Patron;
public:
CirculoLleno(int A, int B, int R, int P):Circulo(A,B,R), Patron(P){...}
void Draw(){Circulo::Draw();...};
void Hide(){...};
};


Clases bases públicas y derivadas


Los miembros heredados de una clase base pública mantienen su nivel de acceso dentro de la clase derivada. En general, en una jerarquía de derivación pública, cada clase derivada subsecuentemente tiene acceso combinado a miembros públicos y protegidos de las clases bases previas en una rama.
Los miembros públicos y protegidos heredados a través de una derivación protegida, se vuelen miembros protegidos de la clase derivada.


Los miembros públicos y protegidos heredados a traves de una derivación privada se vuelven miembros privados de la clase derivada. Esto lleva a:


• Los miembros públicos de una clase base no pueden ser accedidos a través de un objeto de una clase derivada.
• Los miembros públicos y protegidos de la clase base ya no seran más visibles en subsecuentes derivaciones.
Conversiones estándar bajo derivación
Existen 4 conversiones predefinidas que son aplicables entre una clase derivada y su clase base pública:
• Un objeto de la clase derivada se convierte en forma implícita en un objeto de la clase base pública.
• Una referencia a una clase derivada se convierte implícitamente en una referencia a la clase base pública.
• Un puntero a la clase derivada se convertirá implícitamente en un puntero a la clase base.
• Un puntero a un miembro de una clase base se convertirá en forma implícita en un puntero a miembro de la clase derivada.




5.5 HERENCIA MULTIPLE

Es una propiedad que permite que los objetos sean creados a partir de otros ya existentes, obteniendo características (métodos y atributos) similares a los ya existentes. Es la relación entre una clase general y otra clase más específica. Es un mecanismo que nos permite crear clases derivadas a partir de clase base, nos permite compartir automáticamente métodos y datos entre clases, subclases y objetos.
Por ejemplo: Si declaramos una clase párrafo derivada de una clase texto, todos los métodos y variables asociadas con la clase texto, son automáticamente heredados por la subclase párrafo.
La herencia múltiple es uno de los mecanismos de la programación orientada a objetos, por medio del cual una clase se deriva de otra, llamada entonces superclase, de manera que extiende su funcionalidad. Una de sus funciones más importantes es la de proveer Polimorfismo y late binding.
La idea es la de partir de las situaciones más generales e ir derivando hacia las más particulares, creando categorías, de la misma forma en que piensa el ser humano.
Ventajas:
• Ayuda a los programadores ahorrar código y tiempo, ya que si tiene una clase lista es solo de implementarla y listo todo el código de esta se resume a solo un llamado.
• Los objetos pueden ser construidos a partir de otros similares. Para ello es necesario que exista una clase base y una jerarquía (relacionamiento) de clases.
• La clase derivada puede heredar código y datos de la clase base, añadiendo código o modificando lo heredado.
• Las clases que heredan propiedades de otra clase pueden servir como clase base de otras.



BIBLIOGRAFIA

http://www.tutoriales.itsa.edu.mx/programacion1/index.php?mod=importancia&ban=0





UNIDAD 5.

HERENCIA

La herencia es la última de las propiedades relativas a la OOP, Consiste en la propagación de los atributos y las operaciones a través de distintas sub-clases definidas a partir de una clase común.
Introduce, por tanto, una posibilidad de refinamiento sucesivo del concepto de clase. Nos permite definir una clase principal y , a través de sucesivas aproximaciones, cualquier característica de los objetos. A partir de ahora definiremos como sub-clases todas aquellas clases obtenidas mediante refinamiento de una (o varias) clases principales.

La herencia nos permite crear estructuras jerárquicas de clases donde es posible la creación de sub-clases que incluyan nuevas propiedades y atributos. Estas sub-clases admiten la definición de nuevos atributos, así como crear, modificar o inhabilitar propiedades.
Para pensarlo de manera más fácil podemos abstraernos al siguiente ejemplo:
Pensemos en los distintos sub-modelo s asociados a un modelo básico de automóvil.


A partir de este modelo básico, los fabricantes introducen distintas características (aire acondicionado, ABS, distintas tapicerías, acabados, etc.) que crean sub - clases. Todas estas sub-clases tienen en común la estructura básica (chasis, dirección, etc.) u varían sólo en algunos de sus componentes.

Asociemos a este tipo básico una clase cuyos atributos representen las piezas que componen el coche. Las sub-clases aportarán sus propios atributos (en el caso de vehículos con aire acondicionado, todos aquellas piezas que lo componen), permitiendo la definición de todos los posibles modelos.


viernes, 30 de julio de 2010

UNIDAD 4


UNIDAD 4. CLASES Y OBJETOS
4.1 DEFINICION DE UNA CLASE
Aquí tenemos la sintaxis para definir una clase:
ModifAcceso modifClase class nombreClase [extends nombreBase] [implements listaInterfaces] {
Atributo 1
Atributo N
Método 1
Método N
}
Donde nombreClase es el nombre de la clase, cualquier nombre, pero respetando las reglas de nomenclatura del lenguaje.
ModifAcceso puede ser uno de los siguientes valores: Public indica que la clase es pública, y por tanto que puede ser utilizada desde cualquier otra clase, con independencia de si están en el mismo paquete o no.
Sin especificar indica que la clase tiene visibilidad de paquete, es decir, sólo la pueden usar las clases que se encuentren en el mismo paquete que dicha clase.
ModifClase indica características específicas de la clase que estamos construyendo, los posibles
valores son:
abstract indica que a la clase le falta, al menos uno, el código de algún método. Posee el método (abstracto), pero no tiene el código de ese método, siendo responsabilidad de las clases derivadas proporcionar el código de dicha clase. Una clase abstracta no se puede instanciar. Final se emplea para evitar que esta clase pueda ser derivada.
Extends se utiliza para indicar que la clase hereda de nombreBase, en java sólo se permite heredar de una única clase base. En caso de no incluir la cláusula extends, se asumirá que se está heredando directamente de la clase java.lang.Object Implements indica que esta clase es de los tipos de interfaz indicados por listaInterfaces, pudiendo existir tantos como queramos separados por comas. Esta cláusula es opcional.
La definición de una clase especifica cómo serán los objetos de dicha clase, esto es, de que variables y de que métodos constarán. La siguiente es la definición más simple de una clase:
class nombreClase /* Declaración de la clase */
4.2 DECLARACION DE CLASES
La declaración de una clase define la estructura de la misma. Dicho de otra forma, la declaración de una clase informa de los elementos que la conforman. Posteriormente a ser declarada, una clase debe ser implementada convenientemente, es decir, se debe escribir el código correspondiente a los procedimientos y funciones que determinan el funcionamiento de esa clase.
Indica el nombre de la clase precedido por la palabra clave class.
Para declarar una clase, todo lo que se necesita es escribir una definición de estructura y sustituir la palabra reservada struct por class. Por ejemplo, una clase empleado con campos como el nombre, el departamento, la posición, el una función que nos imprima la información de este.
Quedaría así:
class Empleado {
char* m_nombre;
char* m_departamento;
char* m_posicion;
long m_salario;
void Imprimir( Empleado infoEmpleado);
}
Cuando usted declara una clase en C++, no se reserva memoria para la clase hasta que usted crea un objeto de la clase. Crear un objeto de una clase se llama instanciar un objeto. Un objeto creado de una clase de denomina instancia de una clase. Por ejemplo, yo puedo tener una instancia de empleado con el valor en m_nombre=Jose, m_departamento=Sistemas, m_posicion=programador y m_salario=3000000.
4.3 MIEMBROS DE UNA CLASE
Los miembros (datos miembros y funciones miembro) de una clase puede ser calificado como miembro estable de la declaración anterior con la palabra clave estática. Puede haber miembros de los datos estáticos y funciones miembro de una clase.
Los miembros de Datos Un miembro de datos estáticos de una clase es como una variable global para su clase. Es decir, los datos disponibles a nivel mundial es miembro de todos los objetos de ese tipo de clase. Los datos estáticos son generalmente mantenidos para almacenar los valores comunes a toda la clase. Por ejemplo, una clase puede tener un miembro de mantenimiento de los datos de seguimiento de su número de objetos existentes.
Un miembro de los datos es diferente de los miembros ordinarios de datos de una clase en varios
aspectos:
• Sólo hay una copia de un miembro de los datos mantenida por la totalidad de la clase que es compartida por todos los objetos de esa clase.
• Es visible sólo dentro de la clase, sin embargo, su duración (el tiempo para el que permanece en la memoria) es el programa entero. Dos cosas son necesarias para la fabricación de un miembro
de datos estáticos:
• Declaración en la definición de la clase
• Definición fuera de la definición de la clase
Los miembros de función Un miembro de la función que accede a la estática sólo los miembros de una clase puede ser declarada como estática. Esto puede hacerse mediante la palabra clave estática antes de la declaración de la función en la definición de la clase como se muestra a continuación:
La clase X (static int count; static void mostrar (void) (tribunal <
Sólo por el prefijo palabra clave estática antes de la declaración de la función en la definición de la clase, le han declarado la función como estática.
Una función miembro estática es diferente de los otros miembros de funciones en varios aspectos:
• Una función miembro puede tener acceso sólo los miembros estáticos (funciones o variables) de la misma clase.
• Una función miembro se invoca utilizando el nombre de la clase en lugar de sus objetos como se muestra a continuación:
clase name.function-nombre ();
Es decir, para llamar a la función estática show () se define por encima de la clase X, se escribe X.show ();
4.4 AMBITO REFERENTE A UNA CLASE

Una clase actúa como cualquier otro tipo de dato con respecto al ámbito. Todos los miembros de una clase se dice que están en el ámbito de esa clase; cualquier miembro de una clase puede referenciar a cualquier otro miembro de la misma clase.
Las funciones miembro de una clase tienen acceso no restringido a los miembros dato de esa clase. El acceso a los miembros dato y funciones de una clase fuera del ámbito de la clase está controlado por el programador. La idea es encapsular la estructura de datos y funcionalidad de una clase, de modo que el acceso a la estructura de datos de la clase desde fuera de las funciones miembro de la clase, sea limitada o innecesaria.
El nombre de la clase tiene que ser único dentro de su ámbito.Especificadores de acceso a los miembros de una clase
En una definición de clase, un especificador de acceso se utiliza para controlar la visibilidad de los miembros de una clase fuera del ámbito de la clase.
Los miembros de una clase pueden ser públicos, privados o protegidos. Las palabras reservadas public, private y protected se utilizan para controlar el modo de acceso a la clase.
Dentro de una declaración de clase, cada una de estas palabras se puede utilizar para preceder a una o más declaraciones de los miembros de una clase:- Acceso protegido.
Los miembros protegidos significan que sólo se puede acceder a ellos por funciones miembro dentro de la misma clase y por funciones miembro de clases derivadas de esta clase.- Acceso público. Los miembros públicos son accesibles por cualquier parte del programa.- Acceso privado. Los miembros privados sólo pueden ser utilizados por las funciones miembro de la clase y las funciones amigas de la clase.
Funciones miembro Son miembros de una clase y son funciones diseñadas para implementar las operaciones permitidas sobre los tipos de datos de una clase. Para declarar una función miembro hay que situar su prototipo en el cuerpo de la clase. No hay que definir la función dentro de la clase; dicha definición puede estar fuera de la clase e incluso en un archivo independiente, aunque también pueden ser definidas en línea dentro de la clase.
Las funciones miembro son necesarias para acceder a los datos privados. En general, son públicas; si no lo fueran, ninguna otra función podría llamarlas. Se pueden declarar para devolver valores con tipos incluyendo objetos de clases, punteros o referencias. Pueden ser declaradas también para aceptar cualquier número y tipo de argumentos. Los argumentos por defecto están permitidos, así como la notación de puntos suspensivos.

4.5 ESPECIFICADORES DE ACCESO

C++ introduce tres nuevas palabras clave para establecer las fronteras de una estructura: public, private y protected. Su uso y significado es bastante claro. Los especificadores de acceso se usan solo en la declaración de las estructuras, y cambian las fronteras para todas las declaraciones que los siguen. Cuando use un especificador de acceso, debe ir seguido de “:” .
4.6 CREACION DE OBJETOS

Para disponer de espacio en la memoria para guardar la información de uno de esos alumnos tendremos que crear un objeto con el patrón (clase) alumno. Los objetos se crean con la palabra reservada new.
En la siguiente instrucción:
uno = new alumno();
Creamos un objeto de la clase alumno que referenciamos con la variable uno

La variable tendrá que haberse declarado antes, y la declaramos como una variable del tipo alumno, ya que la vamos a usar para guardar información de objetos del tipo alumno.
Un programa con la declaración de la variable y la creación del objeto sería:
public class p2t1p5{
public static void main(String[] args){
alumno uno; // 1
uno = new alumno(); // 2
uno.nombre = "Joan"; // 3
uno.apellido = "Rosell Mas"; // 4
System.out.println(uno.nombre + " " + uno.apellido);
}
}
4.7 PUNTERO THIS
This es un puntero que se pasa automáticamente a cualquier miembro cuando se invoca. Es un puntero al objeto que genera la llamada, por tanto la función recibe automáticamente un puntero al objeto. A este puntero se referencia como this y solo se pasa a los miembros punteros this.
objeto.funcion(); // a la función recibe automáticamente el puntero this.
This es un puntero al objeto invocante. Este puntero es pasado automáticamente por el compilador como argumento en todas las llamadas a funciones miembro (no estáticas). Como su inclusión es automática y transparente para el programador, es frecuente referirse a él como argumento implícito u oculto.
El resultado es que cuando el compilador encuentra una invocación (con o sin argumentos) del tipo x.pow2(), calcula la dirección del objeto (&x), y realiza una invocación del tipo pow2(&x), utilizando esta dirección como valor del argumento oculto this. Por supuesto, el compilador añade por su cuenta el argumento correspondiente en la definición de la función X::pow2().
This es un puntero muy especial que no puede ser declarado explícitamente, por lo que la definición void pow2(X* this) { /* ... */ } no sería válida. Tampoco puede tomarse su dirección o ser utilizado como Rvalue para una asignación del tipo this = x (si puede en cambio ser utilizado como Lvalue).
this es una variable local
Como ocurre con todos los parámetros de funciones, resulta que this es una variable local (puntero) presente en el cuerpo de cualquier función miembro no estática. this no necesita ser declarado, y es raro que sea referenciado explícitamente en la definición de alguna función, lo que no es obstáculo para que sea utilizado dentro de la propia función para referenciar a los miembros.
Según lo anterior resulta evidente que, como tal variable local, esta palabra clave no puede ser usada fuera del cuerpo de un método de una clase.
En el siguiente ejemplo se definen dos clases, idénticas salvo en la forma de referenciar a sus miembros; en una se utiliza el puntero this de forma explícita, en otra de forma implícita, ambas son equivalentes, aunque es más normal utilizar la forma implícita.
Ejemplo:
#include
class X {
int x;
public:
int getx() { return x; }
void putx (int i) { x = i;
}};
class Y
{
4.8 CONSTRUCTORES Y DESTRUCTORES.
Constructores
Los constructores son unos métodos especiales que se ejecutan automáticamente al crear un objeto de la clase. En su declaración no se especifica el tipo de dato que devuelven, y poseen el mismo nombre que la clase a la que pertenecen. Al igual que otros métodos, puede haber varios constructores sobrecargados, aunque no pueden existir constructores virtuales.
Como característica especial a la hora de implementar un constructor, justo después de la declaración de los parámetros, se encuentra lo que se llama "lista de inicializadores". Su objetivo es llamar a los constructores de los atributos que conforman el objeto a construir.
Cabe destacar que no es necesario declarar un constructor al igual que un destructor, pues el compilador lo puede hacer, aunque no es la mejor forma de programar.Tomando el ejemplo de la Clase Punto, si deseamos que cada vez que se cree un objeto de esta clase las coordenadas del punto sean igual a cero podemos agregar un constructor.
Existen varios tipos de constructores en C++:
Constructor predeterminado: Es el constructor que no recibe ningún parámetro en la función. Si no se definiera ningún constructor, el sistema proporcionaría uno predeterminado. Es necesario para la construcción de estructuras y contenedores de la STL.
Constructor de copia: Es un constructor que recibe un objeto de la misma clase, y realiza una copia de los atributos del mismo. Al igual que el predeterminado, si no se define, el sistema proporciona uno.
Constructor de conversión: Este constructor, recibe como único parámetro, un objeto o variable de otro tipo distinto al suyo propio. Es decir, convierte un objeto de un tipo determinado a otro objeto del tipo que estamos generando.
Destructores
Los destructores son funciones miembro especiales llamadas automáticamente en la ejecución del programa, y por tanto no tienen por qué ser llamadas explícitamente por el programador. Su cometido es liberar los recursos computacionales que el objeto de dicha clase haya adquirido en tiempo de ejecución al expirar este.
Los destructores son invocados automáticamente al alcanzar el flujo del programa el fin del ámbito en el que está declarado el objeto.
Existen dos tipos de destructores pueden ser públicos o privados, según si se declaran:
si es público se llama desde cualquier parte del programa para destruir el objeto.
si es privado no se permite la destrucción del objeto por el usuario.
El destructor es muy similar al constructor, excepto que es llamado automáticamente cuando cada objeto sale de su ámbito de validez. Recordemos que las variables automáticas tienen un tiempo de vida limitado, ya que dejan de existir cuando se sale del bloque en que han sido declaradas.
Cuando un objeto es liberado automáticamente, su destructor, si existe, es llamado automáticamente.Un destructor tiene el mismo nombre que la clase a la que pertenece, pero precedido con una tilde (~). Igual que el constructor, un destructor no devuelve nada.
Si algún bloque de memoria fue reservado dinámicamente en un objeto, se puede utilizar el destructor para liberarlo antes de que se pierdan los punteros a esas variables.

martes, 27 de julio de 2010

UNIDAD 3 PUNTEROS, REFERENCIAS Y ARREGLOS


UNIDAD 3

PUNTEROS, REFERNCIAS Y ARREGLOS

3.1 CREACION
En C++ se puede crear un puntero genérico que puede recibir la dirección de cualquier tipo de dato.
La declaración de punteros utiliza un asterisco *, que en este caso actúa como calificador de tipo, en una sintaxis muy parecida a la utilizada en la declaración de objetos normales. En este capítulo nos referimos exclusivamente a declaración de punteros a objetos; la declaración de punteros a función será comentada en otro apartado. Cuando los punteros que señalan a miembros de clase (propiedades o métodos) presentan ciertas características especiales por lo que también serán tratados aparte.

En C++ se puede crear un puntero genérico que puede recibir la dirección de cualquier tipo de dato.
La declaración de punteros utiliza un asterisco *, que en este caso actúa como calificador de tipo, en una sintaxis muy parecida a la utilizada en la declaración de objetos normales. En este capítulo nos referimos exclusivamente a declaración de punteros a objetos; la declaración de punteros a función será comentada en otro apartado. Cuando los punteros que señalan a miembros de clase (propiedades o métodos) presentan ciertas características especiales por lo que también serán tratados aparte.
La sintaxis general de la declaración de puntero a objeto es: * [= ]
void main(){void *p;int a=1;double x=2.4;p=&a;p=&x; }
No se puede desreferenciar un puntero void.void main (){void *p;double x=2.5;p=&x;*p=3.6; // error: se desreferencia a un puntero void}
Los punteros siguen las reglas de creación y destrucción del resto de las variables, sin embargo hay que recordar que los objetos tienen duración independiente de los posibles que los señalan, de forma que cuando un puntero-a-objeto sale de ámbito, no se invoca implícitamente ningún destructor para el objeto señalado. A la inversa, la destrucción del objeto señalado no supone necesariamente la destrucción de los punteros que los referencian.
En la práctica pueden presentarse ambas circunstancias, ocasionando situaciones potencialmente peligrosas o erróneas. Por ejemplo, el primer caso puede ocurrir con objetos persistentes creados con los operadores new o new.

Este tipo de objetos son accesibles a través de un puntero, generalmente un objeto automático que es destruido automáticamente al salir de ámbito. Pero el objeto señalado permanece ocupando memoria, por lo que se hace necesaria una invocación explícita al operador delete para destruirlo antes que sea destruido el puntero, pues de lo contrario se produciría una perdida irrecuperable de memoria.
El segundo caso, la destrucción de un objeto sin que sean destruidos los punteros que lo señalan, también es bastante frecuente, dando lugar a los denominados punteros descolgados (“Dangling pointers”), cuyo uso inadvertido es especialmente .

3.2 OPERACIONES CON PUNTEROS

Un puntero es un tipo de dato similar a un entero, y hay un conjunto de operaciones definidas para punteros:
La suma o resta de un entero produce una nueva localización de memoria.
Se pueden comparar punteros, utilizando expresiones lógicas, para ver si están apuntando o no a la misma dirección de memoria.
La resta de dos punteros da como resultado el número de variables entre las dos direcciones.

Veamos cómo trabaja este programa:
princPunt es la dirección del primer elemento de vector, y finPunt la dirección del último elemento. int princPunt = vector; es una combinación de declaración y definición.
La expresión *(princPunt+2) incrementa el valor del puntero princPunt en dos y devuelve el número guardado en esa localización, es decir, apunta a la tercera localización de memoria y su valor es 15.
Se pueden utilizar también enteros en formato hexadecimal. Así,
cout << *(princPunt + 17 ) y cout << *(princPunt + 0x11 ) producen la misma salida.

La expresión princPunt == finPunt comprueba si los dos punteros son iguales. Esto sólo puede ser verdad si los dos punteros apuntan a la misma variable.
Siempre que se realiza una operación aritmética sobre un puntero, sumando o restando un entero, el puntero se incrementa o decrementa un número apropiado de sitios tal que el nuevo valor apunta a la variable que está n elementos (no n bytes) antes o después que el dado.

De la misma forma, al restar dos punteros se obtiene el número de objetos entre las dos localizaciones. Finalmente, dos punteros son iguales si y sólo si apuntan a la misma variable (el valor de las direcciones es el mismo). No son necesariamente iguales si sus valores indirectos son los mismos, ya que estas variables podrían estar en diferentes localizaciones de memoria.

3.3 REFERENCIAS
Las referencias son un tipo de dato C++ estrechamente relacionado con los punteros. Una referencia de un objeto no es un objeto, en el sentido que no tiene su propio espacio de almacenamiento como ocurre con los punteros, y en consecuencia no pueden realizarse con ellas muchas de las operaciones que se relacionan con objetos. Por ejemplo obtener su dirección, crearlas con el operador new, o crear matrices de referencias.
Una referencia es una especie de alias o "alter ego" del objeto. Como se verá a continuación, este concepto, que también existe en otros lenguajes, es un recurso de C++ para pasar argumentos a funciones permitiendo que los argumentos no sean simples variables locales de la función, sino objetos del ámbito que realiza la invocación, lo que permite que la función pueda modificar objetos externos a ella.
Sintaxis:
La declaración de una variable de este tipo se realiza mediante
el declarador de referencia &. La sintaxis general es:
& [ = ]
Ejemplo:
int x;
int & z = x; // decimos que x es el 'iniciador' y que z es la 'referencia'
Estas sentencias declaran e inicia la variable z como referencia-a-entero,
y la asocia con la variable x (que es un entero). En adelante z actúa como un alias de x,
de forma que cualquier operación sobre z equivale a hacerla sobre x. En realidad puede considerarse que z es "casi" un sinónimo de x (como si fuesen la misma variable). , si hacemos:
int x = 4;

Declaración: Las referencias no pueden ser declaradas aisladas, de forma que tienen que estar indefectiblemente unidas a un objeto en su propia definición (deben ser inicializadas en la declaración). Además, una vez declaradas no pueden ser reasignadas a otro objeto (como los punteros), por lo que resultan unidas de por vida al objeto inicial. Por ejemplo:Por la razón anterior, puesto que tienen que estar unidas a un objeto, no pueden referenciar a void:
int& z = void; // Error.
Sí pueden ser inicializadas a otra referencia del mismo tipo, en cuyo caso señalan al objeto inicial. Ejemplo:int& max
(int& a, int& b) {

3.4 ARREGLOS UNIDIMENSIONALES, BIDIMENSIONALES Y MULTIDIMENSIONALES
Arreglos Unidimensionales
Un arreglo unidimensional es un tipo de datos estructurado que está formado de una colección finita y ordenada de datos del mismo tipo. Es la estructura natural para modelar listas de elementos iguales. El tipo de acceso a los arreglos unidimensionales es el acceso directo, es decir, podemos acceder a cualquier elemento del arreglo sin tener que consultar a elementos anteriores o posteriores, esto mediante el uso de un índice para cada elemento del arreglo que nos da su posición relativa. Para implementar arreglos unidimensionales se debe reservar espacio en memoria, y se debe proporcionar la dirección base del arreglo, la cota superior y la inferior.
Representación en Memoria

Los arreglos se representan en memoria de la forma siguiente:
x : array[1..5] of integer

Arreglos Bidimensionales
Este tipo de arreglos al igual que los anteriores es un tipo de dato estructurado, finito ordenado y homogéneo. El acceso a ellos también es en forma directa por medio de un par de índices.Los arreglos bidimensionales se usan para representar datos que pueden verse como una tabla con filas y columnas. La primera dimensión del arreglo representa las columnas, cada elemento contiene un valor y cada dimensión representa una relación La representación en memoria se realiza de dos formas: almacenamiento por columnas o por renglones.

Arreglos Multidimensionales
Los arreglos multidimensionales tienen más de una dimensión. En C#, las dimensiones se manejan por medio de un par de corchetes, dentro de los que se escriben los valores de cada dimensión, separados por comas.Este también es un tipo de dato estructurado, que está compuesto por n dimensiones. Para hacer referencia a cada componente del arreglo es necesario utilizar n índices, uno para cada dimensión.
3.5 CADENAS DE CARACTERES
En programación, una cadena de caracteres, palabra, ristra de caracteres o frase (string en inglés) es una secuencia ordenada de longitud arbitraria (aunque finita) de elementos que pertenecen a un cierto alfabeto. En general, una cadena de caracteres es una sucesión de caracteres (letras, números u otros signos o símbolos).
Siguiendo en el ámbito de la informática, al considerar las cadenas como un tipo de datos, hay que definir (o conocer) cuales son las operaciones que podemos hacer con ellas, en principio éstas podrían ser muchas y llegar a ser muy sofisticadas.
Aquí se exponen algunas de ellas:
• Asignación: Consiste en asignarle una cadena a otra.
• Concatenación: Consiste en unir dos cadenas o más (o una cadena con un carácter) para formar una cadena de mayor tamaño.
• Búsqueda: Consiste en localizar dentro de una cadena una subcadena más pequeña o un carácter.

• Extracción: Se trata de sacar fuera de una cadena una porción de la misma según su posición dentro de ella.

• Comparación

3.6 ASIGNACION DINAMICA DE MEMORIA
Es la asignación de almacenamiento de memoria para utilización por parte de un programa de computador durante el tiempo de ejecución de ese programa. Es una manera de distribuir la propiedad de recursos de memoria limitada entre muchas piezas de código y datos. Un objeto asignado dinámicamente permanece asignado hasta que es desasignado explícitamente, o por el programador o por un recolector de basura; esto es notablemente diferente de la asignación automática de memoria y de la asignación estática de memoria (la de las variables estáticas). Se dice que tal objeto tiene tiempo de vida dinámico.
La asignación dinámica de memoria es una característica de C. Le permite al usuario crear tipos de datos y estructuras de cualquier tamaño de acuerdo a las necesidades que se tengan en el programa.

Se revisarán dos de las aplicaciones más comunes:
• Arreglos dinámicos
• Estructuras dinámicas de datos.
En C la asignación dinámica de memoria se manipula con las funciones malloc() y free(). En C++ se define un método de hacer asignación dinámica utilizando los operadores new y delete.

El operador new está disponible directamente en C++, de modo que no se necesita utilizar ningún archivo de cabecera; new se puede utilizar con dos formatos:new tipo // asigna un único elemento new tipo[num_eltos] // signa un arraySi la cantidad de memoria solicitada no está disponible, el operador new proporciona el valor 0. El operador delete libera la memoria signada con new.delete variabledelete [n] variable.

new es superior a malloc por tres razones:
1.- new conoce cuánta memoria se asigna a cada tipo de variable.
2.- malloc() debe indicar cuánta memoria asignar.
3.- new hace que se llame a un constructor para el objeto asignado y malloc no puede.
delete produce una llamada al destructor en este orden:
1. Se llama al destructor

3.7 USO DE CLASES DEFINIDAS PARA ARREGLOS

En el lenguaje de programación C una estructura(struct) es lo mismo que una clase, en este caso se explica referente a un struct.

Se puede crear un array de estructuras tal como se crea un array de otros tipos. Los arrays de estructuras son idóneos para almacenar un archivo completo de empleados, un archivo de inventario, o cualquier otro conjunto de datos que se adapte a un formato de estructura.

Mientras que los arrays proporcionan un medio práctico de almacenar diversos valores del mismo tipo, los arrays de estructuras le permiten almacenar juntos diversos valores de diferentes tipos, agrupados como estructuras.

Muchos programadores de C utilizan arrays de estructuras como un método para almacenar datos en un archivo de disco. Se pueden introducir y calcular sus datos de disco en arrays de estructuras y a continuación almacenar esas estructuras en memoria. Los arrays de estructura proporcionan también un medio de guardar datos que se leen del disco.La declaración de un array de estructuras info_libro se puede hacer de un modo similar a cualquier array es decir

struc info_libro libros [100];
asigna un array de 100 elementos denominado libros. Para acceder a los miembros de cada uno de los elementos estructura se utiliza una notación de array. Para inicializar el primer elemento de libros, por ejemplo, su código debe hacer referencia a los miembros de libros [0] de la forma siguiente:
strcpy (libros [0].titulo, "C++ a su alcance");
strcpy (libros [0].autor, "Luis Joyanes");

strcpy (libros [0].editorial, "McGraw-Hill");
libros [0].anyo=1999;

Tambien puede inicializarse un array de estructuras en el punto de la declaración encerrando la lista de inicializadores entrellaves, {}.

Por ejemplo:
struct info_libro libros [3] = { "C++ a su alcance", "Luis Joyanes", "McGraw-
Hill", 1999, "Estructura de datos", "Luis Joyanes", "McGraw-Hill", 1999,
"Problemas en pascal", "Angel Hermoso", "McGraw-Hill", 1997};