Análisis
y Diseño de Algoritmos
Prof:
Ing. Victor Garro
Asistente:
Marco Elizondo Vargas
PROGRAMACION
EN C++
Dicen que vale mas una
imagen que 1000 palabras, así que vamos a empezar directamente con un ejemplo:
Programa
en C, con estructuras:
#include “stdio.h” typedef struct { double x; double y; }
Vector; void
SumarVector (Vector* v1, Vector* v2) { v1->x
+= v2->x; v1->y
+= v2->y; } void
ImprimirVector (Vector* v) { printf
(“(%f,%f)”,v->x,v->y); } void main(void) { Vector a,b; a.x = 1; a.y = 2; b.x = 8; b.y = 10; SumarVector
(&a,&b); ImprimirVector
(&a); } |
Programa
en C++, con clases:
#include “stdio.h” class Vector { public: double x; double y ; void
Sumar (Vector* v2); void Imprimir (void); }; void
Vector::Sumar (Vector* v2) { x
+= v2->x; y
+= v2->y; } void
Vector::Imprimir (void) { printf
(“(%f,%f)”,x,y); } void main (void) { Vector a,b; a.x = 1; a.y = 2; b.x = 8; b.y = 10; a.Sumar (&b); a.Imprimir
(); } |
Ambos programas son
semejantes, hacen exactamente lo mismo. El primero usa las estructuras de C, y
el segundo usa las clases de C++.
Una clase es básicamente
un struct, que tiene además funciones declaradas. Una class tiene el concepto
de campos públicos y privados: por defecto son privados, así que para que se
comporte como un struct, hay que poner public: al principio, antes de los
campos que vayamos a declarar.
Las funciones definidas
dentro de una class tienen las siguientes particularidades:
-
Al implementarlas, hay que anteponer a su
nombre el nombre de la clase y dos dos-puntos.
-
Para llamarlas, siempre hay que hacerlo
referenciando una variable de la clase, como si fueran un campo normal.
-
Cuando en el interior de la funcion de una
clase de usan los campos de la misma, se está accediendo en realidad a los
campos de la variable que ha invocado a la función de la clase.
La función definida
anteriormente y su llamada eran las siguientes:
void Vector::Sumar
(Vector* v2) { x += v2->x; y += v2->y; } void main (void) { Vector
a,b; a.Sumar (&b); } |
Mirado desde el punto de
vista de C (intentando emular lo que hace C++) tendríamos lo siguiente:
void Vector_Sumar (Vector*
this,Vector* v2) { this->x += v2->x; this->y += v2->y; } void main (void) { Vector
a,b; Vector_Sumar (&a,&b); } |
En realidad, el parámetro this existe y se puede utilizar, pero no figura en la lista de parámetros, su declaración y envío son automáticos al invocar al método de la variable en cuestión.
El constructor y el
destructor de una clase.
El constructor de una
clase es una función que SE EJECUTA de forma automática al declarar una
variable de la clase.
El destructor de una
clase es una función que SE EJECUTA de forma automática al perder el ámbito una
variable de una clase (al ser destruida).
En nuestra clase
anterior, podríamos tener:
#include “stdio.h” class Vector { public: double x; double y ; Vector
(); // Constructor ~Vector
(); // Destructor void Sumar (Vector* v2); void Imprimir (void); }; Vector::Vector
() { x = 0; y = 0; } Vector::~Vector () { } void Vector::Sumar
(Vector* v2) { x += v2->x; y += v2->y; } void Vector::Imprimir
(void) { printf (“(%f,%f)”,x,y); } void main (void) { Vector
a,b; a.x = 1; a.y = 2; b.x = 8; b.y = 10; a.Sumar
(&b); a.Imprimir
(); } |
Al declarar las
variables a y b de tipo vector, se ejecuta el constructor para cada una de ellas,
con lo cual después de declararlas podemos estar seguros de que a.x vale 0, al
igual que a.y, b.x y b.y
En este caso, el
destructor no hace nada, pero aquí podríamos poner código para destruir la
variable.
Si el tipo fuera
dinámico (tuviera que allocar memoria para funcionar) en el constructor
podríamos los malloc correspondientes, y en el destructor los free
correspondientes. De este modo, no tendríamos que llamar a una función
Inicializar desde el programa principal, ni a una función Liberar antes de
acabar.
Cuando se usan clases,
en lugar de usar malloc y free se utilizar new y delete:
Vector* a;
a = new Vector;
. . . . . . .
delete a;
new Vector es semejante
a malloc (sizeof(Vector)), pero además de allocar la memoria necesaria llama de
forma automática al constructor.
delete a es semejante a free (a), pero antes de llamar
a free, se llama de forma automática al destructor.
Disponer de varios
constructores:
#include “stdio.h” class Vector { public: double x; double y ; Vector
(); // Constructor Vector
(int a, int b); // Constructor ~Vector
(); // Destructor void Sumar (Vector* v2); void Imprimir (void); }; Vector::Vector
() { x = 0; y = 0; } Vector::Vector
(int a, int b) { x = a; y = b; } Vector::~Vector () { } void main (void) { Vector
a,b; Vector
z(10,9); // a y b son vectores que tienen de
coordenadas 0 (constructor por defecto called) // z tiene como coordenadas 10,9 porque
se ha llamado al constructor adecuado. } |
Conclusiones.
Todo esto no ha hecho
más que empezar. Aquí está la información mínima para EMPEZAR A ENTENDER lo que
es la orientación al objeto y las clases en C++. Sin embargo, para empezar a
hacer alguna cosa es suficiente, y enseguida se notarán las mejoras en los
diseños si se aplica con sentido común.
Es importante entender que ya no se habla de: “..... una función SumarVector que recibe dos punteros a vector y le suma al primero el segundo.....”, sino que se habla de: “..... la clase Vector tiene un método Sumar, que permite sumar al vector otro que se le pasa por parámetro.... “. No tenemos funciones, sino métodos de clases.
1)
Quedan perfectamente definidas las
estructuras: están claros sus datos y sus operaciones.
2)
Los programas no pueden acceder a los
miembros internos de las estructuras, solo a las operaciones (siempre que se pongan
en el apartado private:).
3)
No hay conflictos entre nombres de
funciones: una clase Vector puede tener la operación Imprimir, y una clase
Matriz puede tener la operación Imprimir, y no hay problema, ya que una se
llama Vector::Imprimir (invocada por una variable de tipo Vector, y la otra se
llama Matriz::Imprimir (invocada por una variable de tipo Matriz).
4)
Mediante sobrecarga de operadores y otras
técnicas más complejas, podemos escribir programas como el siguiente:
void main (void) { Matriz
a,b,c; a.Llenar
(); b.Llenar (); c = a + b; // Sobrecarga del operador +, para
sumar matrices. c.Imprimir (); } |
5)
La implementación de una clase queda totalmente
oculta a los programas y a otras clases que las puedan usar, y esto permite
cambiar la implementación, con el único cuidado de conservar las cabeceras de
las operaciones definidas, pudiendo añadir nuevas. C++ tiene incluso
declaración de parámetros con valores por defecto, de tal modo que si se usa
una función y no se le envía ese parámetro, tomará el valor por defecto.
6)
Mediante capas de transformación de
entorno, estas clases de pueden usar en diferentes plataformas. Las capas de
transformación no han de hacer otra cosa que adaptar los formatos de las
llamadas para que todo sea correcto, pero en ningún caso cambiar ni manipular
la implementación.
7)
Conseguimos hacer código que sea:
-
Fácil de diseñar. (Obliga a diseñar antes
de empezar a programar).
-
Fácil de entender. (Sabemos donde encontrar
las cosas).
-
Fácil de modificar. (Se puede modificar la
implementación y mantener la definición)
-
Seguro de usar. (Se pueden incorporar
tratamientos de errores internos).
-
Escalable (mediante la herencia y el polimorfismo
de clases).