Sie sind auf Seite 1von 130

ACTIVIDADES

Tópicos Selectos De Programación


proyecto de 3º semestre

Ing. Ivette Stephany Pacheco Farfán


ITS de Escárcega
5/12/2009
Tópicos Selectos De Programación Actividades 2009

TEMAS

GTK
GTK#
WINDOWS FORMS
QT

LIBRERIAS DINAMICAS
PUNTEROS
CLASES
RECURSIVIDAD

LPP O PUERTO PARALELO


LOM O PUERTRO SERIAL
RS-232 PUERTO SERIAL
FIREWIRE

LICENCIAS
COPYRIGHT
GPL
GNU
LGPL
VREATIVE CAMMONS
NIT

CAMPOS DE BIT
HILOS
SYSTEM OBJECT
SET (#)
GET (#)

VECTOR
LISTAS
PILAS
WX WIDGETS
CIN
COUT
TEMPLATES

2
Tópicos Selectos De Programación Actividades 2009

MATERIAL

TEMAS

GTK
GTK#
WINDOWS FORMS
QT

GTK

GTK+ o The GIMP Toolkit es un conjunto de bibliotecas


multiplataforma para desarrollar interfaces gráficas de usuario
(GUI), principalmente para los entornos gráficos GNOME, XFCE y
ROX aunque también se puede usar en el escritorio de Windows,
MacOS y otros.

Inicialmente fueron creadas para desarrollar el programa de edición


de imagen GIMP, sin embargo actualmente se usan mucho por
muchos otros programas en los sistemas GNU/Linux. Junto a Qt es
uno de las bibliotecas más populares para X Window System.

GTK+ se ha diseñado para permitir programar con lenguajes como


C, C++, C#, Java, Ruby, Perl, PHP o Python.

Licenciado bajo los términos de LGPL, GTK+ es software libre y es


parte del proyecto GNU.

GTK#

Gtk # es una interfaz gráfica de usuario Toolkit para mono y. Net. El


proyecto se une la GTK + (http://www.gtk.org/) kit de herramientas y

3
Tópicos Selectos De Programación Actividades 2009
una variedad de GNOME (http://www.gnome.org/) las bibliotecas,
permitiendo plenamente nativas de desarrollo de GNOME gráfica de
aplicaciones utilizando el Mono y. Net marcos de desarrollo.

WINDOWS FORMS

Windows Forms es el nombre dado a la interfaz gráfica de


programación de aplicaciones (API), incluido como una parte de.
NET Framework de Microsoft, proporcionando acceso a los
elementos nativos interfaz de Microsoft Windows envolviendo la API
de Windows existentes en el código administrado. Si bien es visto
como un reemplazo de la anterior y más complejo de C + + basados
en Microsoft Foundation Class Library, que no ofrece un paradigma
comparable al modelo-vista-controlador. Algunos después de
mercado y las bibliotecas de terceros han sido creadas para
proporcionar esta funcionalidad. El más utilizado de ellos es el
Proceso de Interfaz de Usuario bloque de aplicación, que es
liberado por los patrones y prácticas Microsoft grupo como una
descarga gratuita que incluye el código fuente de ejemplos de inicio
rápido.

QT

Qt o biblioteca Qt es una herramienta de programadores para


desarrollar interfaces gráficas de usuario. Es un sistema integral de
desarrollo para aplicaciones multi-plataforma.

Qt (que se pronuncia como la palabra Inglés "lindo" [2]), es una


aplicación marco de la plataforma de desarrollo, ampliamente
utilizado para el desarrollo de los programas de interfaz gráfica de
usuario (en cuyo caso se conoce como un Widget Toolkit), y
también se utiliza para el desarrollo no programas de gráficas, tales
como herramientas de la consola y servidores. Qt es principalmente
usado en KDE, Google Earth, Skype, Qt Extended, Adobe
Photoshop Album, VirtualBox y OPIE.
4
Tópicos Selectos De Programación Actividades 2009

TEMAS:

LIBRERIAS DINAMICAS
PUNTEROS
CLASES
RECURSIVIDAD

LIBRERÍAS DINÁMICAS

Conocidas como DLLs, acrónimo de su nombre en inglés


("Dynamic Linked Library"). Estas librerías se utilizan mucho en la
programación para el SO Windows. Este Sistema contiene un gran
número de tales librerías de terminación .DLL, aunque en realidad
pueden tener cualquier otra terminación .EXE, .FON, .BPI, .DRV
etc. Cualquiera que sea su terminación, de forma genérica nos
referiremos a ellas como DLLs, nombre por el que son más
conocidas.

Una de las grandes ventajas del uso de librerías dinámicas, aparte


de tener ficheros ejecutables más pequeños, es que podemos
modificar la implementación de las librerías sin tener que recompilar
los programas.

PUNTEROS
Cada variable de un programa tiene una dirección en la memoria
del ordenador. Esta dirección indica la posición del primer byte que
la variable ocupa. En el caso de una estructura es la dirección del
primer campo. En los ordenadores actuales la dirección de inicio se
considera la dirección baja de memoria. Como en cualquier caso las
variables son almacenadas ordenadamente y de una forma
predecible, es posible acceder a estas y manipularlas mediante otra
5
Tópicos Selectos De Programación Actividades 2009
variables que contenga su dirección. A este tipo de variables se les
denomina punteros.
Los punteros C son el tipo más potente y seguramente la otra clave
del éxito del lenguaje. La primera ventaja que obtenemos de los
punteros es la posibilidad que nos dan de poder tratar con datos de
un tamaño arbitrario sin tener que moverlos por la memoria. Esto
puede ahorrar un tiempo de computación muy importante en
algunos tipos de aplicaciones. También permiten que una función
reciba y cambie el valor de una variable. Recordemos que todas las
funciones C únicamente aceptan parámetros por valor. Mediante un
puntero a una variable podemos modificarla indirectamente desde
una función cualquiera.
Un puntero se declara de la forma: tipo *nombre;
float *pf;
PLANETA *pp;
char *pc;
Para manipular un puntero, como variable que es, se utiliza su
nombre; pero para acceder a la variable a la que apunta se le debe
preceder de *. A este proceso se le llama indirección. Accedemos
indirectamente a una variable. Para trabajar con punteros existe un
operador, &, que indica 'dirección de'. Con él se puede asignar a un
puntero la dirección de una variable, o pasar como parámetro a una
función.
void prueba_puntero ( void ) {
long edad;
long *p;
p = &edad;
edad = 50;
printf("La edad es %ld\n", edad );
*p = *p / 2;
printf("La edad es %ld\n", edad );
}
void imprimir_string ( char string[] ) {
char *p;
for ( p = string; *p != '\0'; p++ )

6
Tópicos Selectos De Programación Actividades 2009
imprimir_char(*p);
}
Definimos un vector de N_PLA componentes de tipo PLANETA.
Este tipo está formado por un registro. Vemos que en la función de
inicialización del vector el puntero a la primera componente se
inicializa con el nombre del vector. Esto es una característica
importante de C. La dirección de la primera componente de un
vector se puede direccionar con el nombre del vector. Esto es
debido a que en la memoria del ordenador, los distintos elementos
están ordenados de forma ascendente. Así, SSolar se puede utilizar
como &SSolar[0]. A cada iteración llamamos a una función que nos
inicializará los datos de cada planeta. A esta función le pasamos
como argumento el puntero a la componente en curso para que,
utilizando la notación ->, pueda asignar los valores adecuados a
cada campo del registro. Debemos fijarnos en el incremento del
puntero de control de la iteración, p++. Con los punteros se pueden
realizar determinadas operaciones aritméticas aunque, a parte del
incremento y decremento, no son muy frecuentes. Cuando
incrementamos un puntero el compilador le suma la cantidad
necesaria para que apunte al siguiente elemento de la memoria.
Debemos fijarnos que esto es aplicable sólo siempre que haya
distintas variables o elementos situados consecutivamente en la
memoria, como ocurre con los vectores.
De forma similar se pueden utilizar funciones que tengan como
parámetros punteros, para cambiar el valor de una variable.
Veamos:
void intercambio ( void ) {
int a, b;
a = 1;
b = 2;
swap( &a, &b );
printf(" a = %d b = %d\n", a, b );
}
void swap ( int *x, int *y ) {
int tmp;
tmp = *x;
*x = *y;
7
Tópicos Selectos De Programación Actividades 2009
*y = tmp;
}
La sintaxis de C puede, a veces, provocar confusión. Se debe
distinguir lo que es un prototipo de una función de lo que es una
declaración de una variable. Así mismo, un puntero a un vector de
punteros, etc...
 int f1(); función que devuelve un entero
 int *p1; puntero a entero
 int *f2(); función que devuelve un puntero a entero
 int (*pf)(int); puntero a función que toma y devuelve un entero
 int (*pf2)(int *pi); puntero a función que toma un puntero a
entero y devuelve un entero
 int a[3]; vector de tres enteros
 int *ap[3]; vector de tres punteros a entero
 int *(ap[3]); vector de tres punteros a entero
 int (*pa)[3]; puntero a vector de tres enteros
 int (*apf[5])(int *pi); vector de 5 punteros a función que toman
un puntero a entero y devuelven un entero.

En los programas que se escriban se debe intentar evitar


declaraciones complejas que dificulten la legibilidad del programa.
Una forma de conseguirlo es utilizando typedef para
redefinir/renombrar tipos.
typedef int *intptr;
typedef intptr (*fptr) ( intptr );
fptr f1, f2;

QUE ES UN PUNTERO
Un puntero es un tipo especial de variable, que almacena el valor
de una direccion de memoria, esta direccion puede ser la de una
variable individual, pero mas frecuentemente sera la de un elemento
de un array, una estructura u objeto de una clase. Los punteros, al
igual que una variable comun, pertenecen a un tipo (type), se dice
que un puntero 'apunta a' ese tipo al que pertenece. Ejemplos:
8
Tópicos Selectos De Programación Actividades 2009
int* pint; //Declara un puntero a entero
char* pchar; //Puntero a char
fecha* pfecha; //Puntero a objeto de clase 'fecha'

Independientemente del tamaño (sizeof) del objeto apuntado, el


valor almacenado por el puntero sera el de una unica direccion de
memoria. En sentido estricto un puntero no puede almacenar la
direccion de memoria de 'un array' (completo), sino la de un
elemento de un array, y por este motivo no existen diferencias
sintacticas entre punteros a elementos individuales y punteros a
arrays. La declaracion de un puntero a char y otro a array de char
es igual.

Al definir variables o arrays hemos visto que el tipo (type) modifica


la cantidad de bytes que se usaran para almacenar tales elementos,
asi un elemento de tipo 'char' utiliza 1 byte, y un entero 2 o 4. No
ocurre lo mismo con los punteros, el tipo no influye en la cantidad
de bytes asociados al puntero, pues todas las direcciones de
memoria se pueden expresar con solo 2 bytes (o 4 si es una
direccion de otro segmento)

Veamos los efectos de un codigo como el siguiente, en la zona de


almancenamiento de datos:

char cad[] = "hola";


char * p;
p = cad; //Puntero 'p' apunta a 'cad'

El puntero esta en la direccion 0xffee pero el valor que hay en esa


localidad de memoria es otra direccion, los bytes "F0 FF" indican
que el puntero apunta a FFF0, donde comienza la cadena de
caracteres 'cad' con el contenido 'hola' mas el cero de fin de
cadena.
En las lineas de codigo no hemos indicado a que caracter del array
apunta el puntero, pero esa notacion es equivalente a:

9
Tópicos Selectos De Programación Actividades 2009
p = &cad[0];

que indica de modo mas explicito que se trata de la direccion del


primer elemento de ese array de caracteres. El juego con las
direcciones puede ilustrarse tambien del siguiente modo:

ffee F0 <----- El puntero ocupa dos bytes para representar la


direccion FFF0, direccion a la que 'apunta'.
ffef FF <-----

fff0 61 <------ cad[0]. .Primer char del array de caracteres,


direccion apuntada por el puntero
fff1 61 <------ cad[1]
fff2 61 <------ cad[2]
fff3 61 <------ cad[3]
fff4 0 <------ cad[4] Fin del array, caracter ascii = 0 de fin de
cadena

Puesto que un puntero tiene como valor una direccion de memoria,


es logico que al llamar a funciones de impresion con un puntero
como argumento, la salida en pantalla sea la de una direccion de
memoria. Para este tipo de pruebas es interesante usar la libreria
iostream.h de C++, pues no obliga a especificar el formato (como
hace printf ). Para un puntero 'p' la salida en pantalla sera algo
similar a lo siguiente:

cout<<p; //sale: 0x8f82fff0;


printf("%p",p) //sale: FFF0

En este caso se trata de un puntero que almacena en 2 bytes una


direccion de memoria, la cual es FFF0. Porque razon la impresion
con 'cout' nos da 4 bytes? Porque agrega 2 bytes (8f y 82) para
indicar el 'segmento' donde se encuentra esa direccion. Se trata en
todo caso de una misma localidad de memoria, con distinto formato
de presentacion en pantalla.

La salida en pantalla de un puntero a char es diferente, pues es


tratado como apuntando a una cadena de caracteres, en tal caso no
sale en pantalla una direccion de memoria, sino un conjunto de
caracteres hasta encontrar el '\0'.

Un puntero puede almacenar la direccion de ("apuntar a") muy


diferentes entidades: una variable, un objeto, una funcion, un

10
Tópicos Selectos De Programación Actividades 2009
miembro de clase, otro puntero, o un array de cada uno de estos
tipos de elementos, tambien puede contener un valor que indique
que no apunta actualmente a ningun objeto (puntero nulo).

TIPOS DEFINIDOS POR EL PROGRAMADOR


Tipos como 'bool', 'int' o 'char', son "tipos predefinidos",
pertenecientes al lenguaje. En C++ al igual que otros lenguajes, es
posible definir tipos nuevos. Las enumeraciones, uniones,
estructuras y clases, son tipos nuevos que implementa el
programador.

La declaracion de un tipo no produce ningun efecto en memoria, no


hay ningun identificador donde almacenar un dato, por esa razon no
tendria sentido, dentro de la definicion de una estructura o clase ,
intentar dar un valor a sus datos, seria lo mismo que intentar dar un
valor a un tipo predefinido, por ejemplo:
long = 8;
Para asignar un valor necesitamos un objeto, pues un objeto implica
una region de memoria donde almacenar un valor.

El almacenamiento en memoria de una union, enumeracion o


estructura (C), no presenta importantes cambios respecto a los tipos
predefinidos, sus elementos se ordenaran de modo consecutivo de
acuerdo a su 'sizeof'. Respecto a C, C++ aporta un nuevo tipo
predefinido, las clases, entidad que no solo es un agregado de
datos sino tambien de funciones, y que por ello presenta novedades
de importancia respecto a los tipos anteriores.

CLASES
Una clase es basicamente un agregado de datos y funciones para
manipular esos datos. Las clases, y la programacion 'orientada a
objetos' en general, ha representado un gran avance para
produccion de software a gran escala, los recursos de herencia,
encapsulamiento, ocultacion de datos, clases virtuales, etc., estan
pensados con esa finalidad. Aqui solo nos detendremos en la

11
Tópicos Selectos De Programación Actividades 2009
nocion minima de 'clase' y el modo en que es almacenado un objeto
en memoria.

Supongamos una clase muy simple:

class gente
{
char nombre[10];
int edad;
public:
gente (char*cad, int a)
{
strcpy(nombre,cad);
edad = a;
}
};

Se trata de una clase cuyos miembros son dos datos y una sola
funcion. Una vez declarada la clase podemos definir objetos como
pertenecientes a ese tipo. Una clase no ocupa espacio, pero si un
objeto perteneciente a esa clase. El espacio ocupado en memoria
por tal objeto puede ser conocido a traves de 'sizeof'.
gente pp1;
cout<<sizeof(pp1); //saca en pantalla '12'
El valor podria ser ligeramente diferente segun el compilador, por
efecto de optimizacion. Lo importante es observar que el monto de
memoria del objeto (retornado por sizeof), esta determinado por la
suma del espacio ocupado por los datos, 'sizeof' no tiene en cuenta
a la funcion.
Cada objeto de tipo 'gente' ocupara 12 bytes, pues posee una copia
individual de los datos de clase, en cambio hay una sola copia del
miembro funcion (aqui el constructor) utilizado por todos los objetos.

Declaremos dos objetos de tipo 'gente':

gente pp1("gerardo", 33);


gente pp2("miguel",34);

Observaremos ahora que efectos producen estas entidades 'pp1' y


'pp2', en memoria. Los datos que utilizaremos se obtienen en
TurboC++ (cualquier version) posando el cursor sobre el objeto que
nos interesa (aqui 'pp1' y 'pp2') y pulsando 'Alt+f4', tambien
consultaremos los registros de la CPU (con "Windows/Registers").

12
Tópicos Selectos De Programación Actividades 2009
En un programa, que define la clase 'gente' y dos objetos (pp1 y
pp2) inicializados como muestran las lineas de codigo previas, se
puede observar lo siguiente:

El valor especifico de cada dato (como el valor de segmento) puede


variar con cada ejecucion, lo que cuenta es la relacion entre tales
valores. Interpretemos estos datos.

1- En la ventana de cada objeto (pp1 y pp2) figura en primer lugar la


direccion de memoria donde almacena sus valores, ambas
direcciones tienen el mismo valor de segmento (0x8F86), que
coincide por otra parte con el valor de DS (segmento de datos) y
de SS (segmento de stack) de la CPU. Sus direcciones difieren
ligeramente en offset, la resta de los mismos (0xFFEA - 0xFFDE) es
igual a 12, que es el espacio que ocupa (en bytes) cada objeto en
memoria.

2- Esos 12 bytes por objeto corresponden a 10 para la cadena de


caracteres ('cad') y 2 para almacenar el entero ('edad'). Estos datos
estan almacenados alli donde indica el offset, no en otro sitio, por lo
tanto un puntero al objeto 'pp1' apuntara (en este caso) a la misma
direccion de memoria que un puntero a su elemento 'cad', y otro
tanto para 'pp2'. Las datos miembros se exponen con sus nombres
a la izquierda y el valor que contienen a la derecha. La cadena de
caracteres es terminada en '\0' (seguido de caracteres aleatorios), y
el entero es mostrado en formato decimal y hexadecimal

3-Debajo, y separado por una linea, se encuentra un espacio donde


se enumeran las funciones miembro de la clase. Alli encontramos el
prototipo de la funcion miembro y al lado la direccion de memoria
donde se inicia su codigo. Ese es el valor que almacenaria un
puntero a dicha funcion. Observese que tal direccion es la misma
para ambos objetos, por la razon antes mencionada de que hay
solo una copia de funciones miembro por objeto. El segmento
donde se encuentra tal funcion se corresponde con el valor que
muestra la ventana CPU para CS (segmento de codigo).

13
Tópicos Selectos De Programación Actividades 2009
Podemos sintetizar lo visto respecto a clases del siguiente modo:
-Una clase no es un 'dato' (es un tipo), no tiene una localidad de
memoria asociada y por lo tanto no puede almacenar ningun valor.
-Un objeto de tal clase si define una region de memoria, un espacio
de almacenamiento de datos. Esta es la diferencia entre 'clase' y
'objeto'.
-Cada objeto de una misma clase posee una copia propia de cada
uno de los datos miembros de la clase, pero comparte una misma
copia de las funciones miembros.

Por otra parte, un array de objetos (instancias de clase) es


almacenado como una sucesion consecutiva, mientras que un
puntero a objeto sera (como todo puntero) un par de bytes que
apunte a una direccion de memoria donde se almacena el objeto.

14
Tópicos Selectos De Programación Actividades 2009

RECURSIÓN

Un tema fundamental para los próximos temas es el de recusrión.


La recursión es muy importante tanto en mateáticas como em
computación, pues se usa recursión para definir procedimientos
autosimilares.

Definición 6 Decimos que un objeto es recursivo si en su


definición se nombra a sí mismo.

En programación, una función es recursiva si en el ámbito de esa


función hay una llamada a sí misma, C/C++ permite esta clase de
acciones. Los algoritmos recursivos dan elegancia a las soluciones
de los problemas. Un ejemplo clásico es el factorial de un número.

Una manera de definir el factorial de un número es:

es decir, el producto de todos los números enteros menores o


guales que él, lo que se puede resolver fácilmente con una función
iterativa, esto es, una función con un ciclo que itere suficientes
veces, incrementando un valor y entonces ir almacenando en una
variable el resultado de esas multiplicaciones.

Una implementación de esta definición iterativa es:

(1) int i,n;


(2) long double valorAc;
(4) valorAc=1.0;
(5) std::cout << "Numero entero:";
(6) std::cin>> n;
(7) for(i=1; i<=n; i++) valorAc = valorAc*i;
(8) std::cout<<"El factorial de "<<n<<" es:"<<valorAc;

El ciclo principal es en la línea (7). No hay ningún truco hasta aquí.


La única observación importante es en la línea (2) en donde se
declara el tipo long double para el valor del resultado, la razón para
tal acción es que el número factorial crece muy rápido y aún con
entradas en el rango de los caracteres (hasta 255), el factorial es

15
Tópicos Selectos De Programación Actividades 2009
muy grande. Este procedimiento computacional no hace uso de
técnicas especiales empleadas para tratar números grandes.

Sin embargo una solución más elegante es usar la definición


recursiva, y esta es:

El programa en C/C++ es el que se muestra a continuación:

( 1) double factorial(double a){


( 2) if (a<=1) return 1.0;
( 3) else return (a *factorial(a-1.0)); }
( 4)
( 5) int main (int argc, char * const argv[]) {
( 6) double n;
( 7) std::cout << "Numero entero:";
( 8) std::cin>> n;
( 9) std::cout<<"El factorial de "<<n<<" es: "<< factorial(n);
(10) return 0; }

Aquí hay varias cosas que señalar, en primer lugar se ha creado


una nueva función, a diferencia de la definición iterativa en donde
era suficiente trabajar en el programa principal. Esta función se
llama factorial (como era de suponerse), y empieza su encabezado
en la línea (1).

Allí mismo en la misma línea (1), es de notar que hemos emplado


ahora el tipo double tanto para el tipo devuelto como para el tipo del
argumento, a diferencia de la versión iterativa en donde
empleábamos tipos diferentes. La razón es que al iniciar la
recursión el argumento es del tipo devuelto, asi que deben ser del
mismo tipo.

Cada llamada recursiva genera una entrada a una pila, en donde se


guardan (como elementos) los estados generales del sistema al
momento de hacer la llamada, entonces, cuando se termina la
función se recupera una entrada de la pila. En la figura 16 ilustra
cómo funciona la recursividad cuando se intenta obtener el
factorial(5).

16
Tópicos Selectos De Programación Actividades 2009

Figura 16: Recursividad cuando se ejecuta factorial(5)

RECURSIÓN

Se dice que una función es recursiva cuando se llama a sí misma.


Es clásico el ejemplo de cálculo del factorial de un número mediante
el uso de este tipo de funciones:

int factorial (int x) { return ( x > 1) ? x * factorial(x-1): 1; }

Cada invocación sucesiva de estas funciones crea un nuevo juego


de todas las variables automáticas, con independencia de cuales
sean sus valores en el conjunto previo. En cambio existe una sola
copia de las variables estáticas, que es compartida por todas las
instancias de la función. Esta circunstancia debe ser tenida en
cuenta, cuando dichas variables sean utilizadas en funciones que
puedan llamarse a sí mismas.

La recursión ocupa espacio en la pila, porque se debe guardar una


copia de cada juego de variables y tampoco es muy rápida, pero
tiene la ventaja de que resulta un código fácil de interpretar. Es
especialmente adecuada para estructuras que se definen
recursivamente (algunos miembros de estas estructuras son

17
Tópicos Selectos De Programación Actividades 2009
punteros que referencian a objetos del mismo tipo que la que se
está definiendo), como árboles y listas enlazadas (las hemos
denominado estructuras auto-referenciadas

§4 Hay que prestar atención a que en algunos casos de funciones


recursivas es difícil de manejar el valor devuelto. Como ejemplo
tomemos la función búsqueda de un valor (key) en un árbol binario
que reproducimos aquí. Esta función aparentemente devuelve un
puntero al nodo buscado, o un nulo si fracasa la búsqueda.

struct base* busca(char key, struct base* ptr) { // buscar key


if (key == ptr->let) return ptr; // Ok. encuentro!
if (key < ptr->let) {
if (ptr->izq == NULL) return NULL;
busca(key, ptr->izq);
}
if (key > ptr->let) {
if (ptr->der == NULL) return NULL;
busca(key, ptr->der);
}
return NULL;
}

El problema aquí, es que la función busca en el árbol de forma


recursiva, pero es imposible saber "a priori" cuantas invocaciones
anidadas se producirán. Además, ninguna función aprovecha el
valor devuelto por la anterior, por lo que salvo la primera, cuyo
retorno si puede ser utilizado por la función invocante, todos los
demás valores de retorno se pierden (una función puede devolver
un valor, pero es potestad
de la función invocante
aprovecharlo o no. En
este contexto no tiene
sentido manejar
directamente el valor
devuelto por busca tal
como se ha presentado.
18
Tópicos Selectos De Programación Actividades 2009
Por ejemplo, sería inútil el siguiente trozo de código para saber si la
búsqueda ha tenido éxito:

...
if (busca(key, ptr) != NULL){
...
}

La situación puede esquematizarse en la Fig.1. Cada nueva


recursión recibe parámetros de la anterior, pero esta no utiliza el
valor devuelto.

La función usuaria (que hace la primera invocación a busca)


siempre recibirá el resultado de la primera invocación, de modo que
si el resultado se ha producido a partir de la segunda, el valor que
recibe la función usuaria es irrelevante.

Para resolver el problema puede utilizarse una variable global que


sea modificada por la instancia que realiza el hallazgo, o un sistema
más refinado. Por ejemplo el manejador de excepciones, que como
se ha indicado puede utilizarse como mecanismo de return o break
multinivel, y además pasar un valor desde el origen hasta el punto
de captura.

En el primer caso, la función modificada podría tener el siguiente


diseño:

void busca(char key, struct base* ptr) { // buscar key


if (key == ptr->let){gptr = ptr; return; } // Ok. encuentro!
if (key < ptr->let) {
if (ptr->izq == NULL) return;
busca(key, ptr->izq);
}
if (key > ptr->let) {
if (ptr->der == NULL) return;
busca(key, ptr->der);

19
Tópicos Selectos De Programación Actividades 2009
}
return;
}

El cambio consiste en que la función devuelve siempre void, pero


cuando se produce el encuentro modifica adecuadamente una
constante global gptr (esta variable podría ser inicializada a NULL
antes de la primera invocación a busca).

Recursividad

Se dice que una función es recursiva cuando se define en función


de si misma. No todas la funciones pueden llamarse a si mismas,
deben estar diseñadas especialmente para que sean recursivas, de
otro modo podrían conducir a bucles infinitos, o a que el programa
termine inadecuadamente.

C++ permite la recursividad. Cuando se llama a una función, se


crea un nuevo juego de variables locales, de este modo, si la
función hace una llamada a si misma, se guardan sus variables y
parámetros en la pila, y la nueva instancia de la función trabajará
con su propia copia de las variables locales, cuando esta segunda
instancia de la función retorna, recupera las variables y los
parámetros de la pila y continua la ejecución en el punto en que
había sido llamada.

Por ejemplo:

Función recursiva para calcular el factorial de un número entero. El


factorial se simboliza como n!, se lee como "n factorial", y la
definición es:

n! = n * (n-1) * (n-2) * ... * 1

No se puede calcular el factorial de números negativos, y el factorial


de cero es 1, de modo que una función bien hecha para cálculo de
factoriales debería incluir un control para esos casos:

/* Función recursiva para cálculo de factoriales */


int factorial(int n) {
if(n < 0) return 0;
else if(n > 1) return n*factorial(n-1); /* Recursividad */
20
Tópicos Selectos De Programación Actividades 2009

return 1; /* Condición de terminación, n == 1 */


}

Veamos paso a paso, lo que pasa cuando se ejecuta esta función,


por ejemplo: factorial(4):

1a Instancia
n=4
n>1
salida ← 4 * factorial(3) (Guarda el valor de n = 4)

2a Instancia
n>1
salida ← 3*factorial(2) (Guarda el valor de n = 3)

3a Instancia
n>1
salida ← 2*factorial(1) (Guarda el valor de n = 2)

4a Instancia
n == 1 → retorna 1

3a Instancia
(recupera n=2 de la pila) retorna 1*2=2

2a instancia
(recupera n=3 de la pila) retorna 2*3=6

1a instancia
(recupera n=4 de la pila) retorna 6*4=24
Valor de retorno → 24

La función factorial es un buen ejemplo para demostrar cómo se


hace una función recursiva, sin embargo la recursividad no es un
buen modo de resolver esta función, que sería más sencilla y rápida
con un bucle "for". La recursividad consume muchos recursos de
memoria y tiempo de ejecución, y se debe aplicar a funciones que
realmente le saquen partido.

Por ejemplo: visualizar las permutaciones de n elementos.

21
Tópicos Selectos De Programación Actividades 2009
Las permutaciones de un conjunto son las diferentes maneras de
colocar sus elementos, usando todos ellos y sin repetir ninguno. Por
ejemplo para A, B, C, tenemos: ABC, ACB, BAC, BCA, CAB, CBA.

#include <iostream>
using namespace std;

/* Prototipo de función */
void Permutaciones(char *, int l=0);

int main(int argc, char *argv[]) {


char palabra[] = "ABCDE";

Permutaciones(palabra);

cin.get();
return 0;
}

void Permutaciones(char * cad, int l) {


char c; /* variable auxiliar para intercambio */
int i, j; /* variables para bucles */
int n = strlen(cad);

for(i = 0; i < n-l; i++) {


if(n-l > 2) Permutaciones(cad, l+1);
else cout << cad << ", ";
/* Intercambio de posiciones */
c = cad[l];
cad[l] = cad[l+i+1];
cad[l+i+1] = c;
if(l+i == n-1) {
for(j = l; j < n; j++) cad[j] = cad[j+1];
cad[n] = 0;
}
}
}

El algoritmo funciona del siguiente modo:

22
Tópicos Selectos De Programación Actividades 2009
Al principio todos los elementos de la lista pueden cambiar de
posición, es decir, pueden permutar su posición con otro. No se fija
ningún elemento de la lista, l = 0: Permutaciones(cad, 0)

0 1 2 3 4
A B C D /0

Se llama recursivamente a la función, pero dejando fijo el primer


elemento, el 0: Permutacion(cad,1)

0 1 2 3 4
A B C D /0

Se llama recursivamente a la función, pero fijando el segundo


elemento, el 1: Permutacion(cad,2)

0 1 2 3 4
A B C D /0

Ahora sólo quedan dos elementos permutables, así que imprimimos


ésta permutación, e intercambiamos los elementos: l y l+i+1, es
decir el 2 y el 3.

0 1 2 3 4
A B D C /0

Imprimimos ésta permutación, e intercambiamos los elementos l y


l+i+1, es decir el 2 y el 4.

0 1 2 3 4
A B /0 C D

En el caso particular de que l+i+1 sea justo el número de elementos


hay que mover hacia la izquierda los elementos desde la posición
l+1 a la posición l:

0 1 2 3 4
A B C D /0

23
Tópicos Selectos De Programación Actividades 2009
En este punto abandonamos el último nivel de recursión, y
retomamos en el valor de l=1 e i = 0.

0 1 2 3 4
A B C D /0

Permutamos los elementos: l y l+i+1, es decir el 1 y el 2.

0 1 2 3 4
A C B D /0

En la siguiente iteración del bucle i = 1, llamamos recursivamente


con l = 2: Permutaciones(cad,2)

0 1 2 3 4
A C B D /0

Imprimimos la permutación e intercambiamos los elementos 2 y 3.

0 1 2 3 4
A C D B /0

Y así sucesivamente.

24
Tópicos Selectos De Programación Actividades 2009
TEMAS:

LPP O PUERTO PARALELO


LOM O PUERTRO SERIAL
RS-232 PUERTO SERIAL
FIREWIRE

LPT O PUERTO PARALELO.

LPT o Puerto Paralelo.

Aunque utilizados con mucha anterioridad, hasta la norma IEEE1284 en el 1.994 no se


normaliza.

Teóricamente el sistema de comunicación entre la impresora y el ordenador es mas o


menos simple, aunque varía según el conector. Pero básicamente el ordenador envía
impulsos a la impresora y esta responde conforme que puede seguir enviando o no, es lo
que se suele denominar una respuesta de tipo "Low", y si no da tiempo a la impresión
(prácticamente siempre será más rápido el envío que la salida impresa) se irá
almacenando en el buffer de memoria de la impresora. En el caso de que éste se agote y
siga recibiendo datos, responderá con un "busy" y la cola de impresión del ordenador se
detendrá a la espera de recibir el nuevo "Low".

Los puertos de impresión se configuran en la Bios del ordenador y se denominan LPT


(Line PrinTer) y hay algunas en que pueden configurarse 2 y en otras 4, las direcciones
varían, y cada uno tiene tres registros: el de datos, el de estado y el de control.

Los estándares iniciales, a partir de la normativa indicada, eran los SPP (Standard
Parallel Port), que es el original, o al menos el compatible, con lo que se denominó
desde sus inicios el puerto Centronics. Uno de los motivos de que impresoras antiguas
no funcionen con BIOS nuevas es que por defecto este venga deshabilitado, con lo que
en la configuración del LPT correspondiente hay que indicar que debe de ser compatible
con estos. Este modo acepta hasta un máximo de 150 kb por segundo.

Aunque utilizados con mucha anterioridad, hasta la norma IEEE1284 en el


1.994 no se normaliza.

Teóricamente el sistema de comunicación entre la impresora y el ordenador es


más o menos simple, aunque varía según el conector. Pero básicamente el
ordenador envía impulsos a la impresora y esta responde conforme que puede
seguir enviando o no, es lo que se suele denominar una respuesta de tipo

25
Tópicos Selectos De Programación Actividades 2009
"Low", y si no da tiempo a la impresión (prácticamente siempre será más rápido
el envío que la salida impresa) se irá almacenando en el buffer de memoria de
la impresora. En el caso de que éste se agote y siga recibiendo datos,
responderá con un "busy" y la cola de impresión del ordenador se detendrá a la
espera de recibir el nuevo "Low".

Los puertos de impresión se configuran en la Bios del ordenador y se


denominan LPT (Line PrinTer) y hay algunas en que pueden configurarse 2 y
en otras 4, las direcciones varían, y cada uno tiene tres registros: el de datos, el
de estado y el de control.

Los estándares iniciales, a partir de la normativa indicada, eran los SPP


(Standard Parallel Port), que es el original, o al menos el compatible, con lo que
se denominó desde sus inicios el puerto Centronics. Uno de los motivos de que
impresoras antiguas no funcionen con BIOS nuevas es que por defecto este
venga deshabilitado, con lo que en la configuración del LPT correspondiente
hay que indicar que debe de ser compatible con estos. Este modo acepta hasta
un máximo de 150 kb por segundo.

Una PC soporta hasta tres puertos paralelo separados, por tanto puede
haber hasta tres juegos de registros en un sistema en un momento dado.
Existen tres direcciones base para el puerto paralelo asociadas con tres
posibles puertos paralelo: 0x3BCh, 0x378h y 0x278h, nos referimos a éstas
como las direcciones base para el puerto LPT1, LPT2 y LPT3,
respectivamente. El registro de datos se localiza siempre en la dirección base
de un puerto paralelo, el registro de estado aparece en la dirección base + 1, y
el registro de control aparece en la dirección base + 2. Por ejemplo, para un
puerto LPT2 localizado en 0x378h, ésta es la dirección del registro de datos, al
registro de estado le corresponde la dirección 0x379h y su respectivo registro
de control está en la dirección 0x37Ah. Cuando la PC se enciende el BIOS
ejecuta una rutina para determinar el número de puertos presentes en el
sistema asignando la etiqueta LPT1 al primer puerto localizado, si existen más
puertos entonces se asignarán consecutivamente las etiquetas LPT2 y LPT3 de
acuerdo a la siguiente tabla:

Dirección inicial Función


0000:0408 Dirección base para LPT1
0000:040A Dirección base para LPT2
0000:040C Dirección base para LPT3
0000:040E Dirección base para LPT4

Tabla 5: Direcciones base en el BIOS

Puerto paralelo Centronics

El puerto paralelo más conocido es el puerto de impresora (que cumplen más o


menos la norma IEEE 1284, también denominados tipo Centronics) que
destaca por su sencillez y que transmite 8 bits. Se ha utilizado principalmente
26
Tópicos Selectos De Programación Actividades 2009
para conectar impresoras, pero también ha sido usado para programadores
EPROM, escáneres, interfaces de red Ethernet a 10 MB, unidades ZIP,
SuperDisk y para comunicación entre dos PC (MS-DOS trajo en las versiones
5.0 ROM a 6.22 un programa para soportar esas transferencias).

El puerto paralelo de las computadoras, de acuerdo a la norma Centronics,


está compuesto por un bus de comunicación bidireccional de 8 bits de datos,
además de un conjunto de líneas de protocolo. Las líneas de comunicación
cuentan con un retenedor que mantiene el último valor que les fue escrito hasta
que se escribe un nuevo dato, las características eléctricas son:

 Tensión de nivel alto: 3,3 o 5 V.


 Tensión de nivel bajo: 0 V.
 Intensidad de salida máxima: 2,6 mA.
 Intensidad de entrada máxima: 24 mA.

Los sistemas operativos basados en DOS y compatibles gestionan las


interfaces de puerto paralelo con los nombres LPT1, LPT2 y así
sucesivamente, Unix en cambio los nombra como /dev/lp0, /dev/lp1, y demás.
Las direcciones base de los dos primeros puertos son:

 LPT1 = 0x378.
 LPT2 = 0x278

La estructura consta de tres registros: de control, de estado y de datos.

 El registro de control es un bidireccional de 4 bits, con un bit de


configuración que no tiene conexión al exterior, su dirección en el LPT1
es 0x37A.
 El registro de estado, se trata de un registro de entrada de información
de 5 bits, su dirección en el LPT1 es 0x379.
 El registro de datos, se compone de 8 bits, es bidireccional. Su
dirección en el LPT1 es 0x378.
 Inicialmente se colocó al puerto paralelo en la tarjeta del "Adaptador de
impresora de IBM", o también con la tarjeta del "monitor monocromático
y adaptador de impresora de IBM".
 Con la llegada de clones al mercado, se crea un controlador de múltiples
entradas y salidas (Multi I/O) donde se instalan controladores de discos,
puertos serie, puerto de juegos y el puerto paralelo.
 En la actualidad (2009) el puerto paralelo se incluye comúnmente
incluido en la placa madre de la computadora (MotherBoard). No
obstante, la conexión del puerto con el mundo externo no ha sufrido
modificaciones. Este puerto utiliza un conector hembra DB25 en la
computadora y un conector especial macho llamado Centronic que tiene
36 pines.

PUERTO SERIE (COM).

27
Tópicos Selectos De Programación Actividades 2009

Puerto en serie ATX

Un puerto serie o puerto serial es una interfaz de comunicaciones de datos


digitales, frecuentemente utilizado por computadoras y periféricos, en donde la
información es transmitida bit a bit enviando un solo bit a la vez, en contraste
con el puerto paralelo que envía varios bits simultáneamente. La comparación
entre la transmisión en serie y en paralelo se puede explicar con analogía con
las carreteras. Una carretera tradicional de un sólo carril por sentido sería como
la transmisión en serie y una autovía con varios carriles por sentido sería la
transmisión en paralelo, siendo los coches los bits.

Puerto serie asincrónico

El puerto serie RS-232 (también conocido como COM) es del tipo asincrónico,
utiliza cableado simple desde 3 hilos hasta 25 y que conecta computadoras o
microcontroladores a todo tipo de periféricos, desde terminales a impresoras y
módems pasando por mouses.

La interfaz entre el RS-232 y el microprocesador generalmente se realiza


mediante una UART 8250 (computadoras de 8 y 16 bits, PC XT) o 16550 (IBM
Personal Computer/AT y posteriores).

El RS-232 original tenía un conector tipo DB-25, sin embargo la mayoría de


dichos pines no se utilizaban, por lo que IBM estandarizó con su gama IBM
Personal System/2 el uso del conector DE-9 (ya introducido en el AT)
mayoritariamente usado en computadoras. Por contra, excepto en los mouses
el resto de periféricos solían presentar el DB-25

En Europa la norma RS-422, similar al RS-232, es un estándar utilizado en el


ámbito industrial.

Puertos serie modernos


Uno de los defectos de los puertos serie iniciales era su lentitud en
comparación con los puertos paralelos -hablamos de 19.2 kbits por segundo-
sin embargo, con el paso del tiempo, están apareciendo multitud de puertos
serie de alta velocidad que los hacen muy interesantes ya que utilizan las
ventajas del menor cableado y solucionan el problema de la velocidad con un
mayor apantallamiento y más barato usando la técnica del par trenzado. Por
ello, el puerto RS-232 e incluso multitud de puertos paralelos están siendo

28
Tópicos Selectos De Programación Actividades 2009
reemplazados por nuevos puertos serie como el USB, el FireWire o el Serial
ATA.

Un puerto de red puede ser puerto serie o puerto paralelo.

Tipos de comunicaciones seriales


Simplex

En este caso el transmisor y el receptor están perfectamente definidos y


la comunicación es unidireccional. Este tipo de comunicaciones se
emplean usualmente en redes de radiodifusión, donde los receptores no
necesitan enviar ningún tipo de dato al transmisor.

Duplex, half duplex o semi-duplex

En este caso ambos extremos del sistema de comunicación cumplen


funciones de transmisor y receptor y los datos se desplazan en ambos
sentidos pero no simultáneamente. Este tipo de comunicación se utiliza
habitualmente en la interacción entre terminales y un computador
central.

Full Duplex

El sistema es similar al duplex, pero los datos se desplazan en ambos


sentidos simultáneamente. Para ello ambos transmisores poseen
diferentes frecuencias de transmisión o dos caminos de comunicación
separados, mientras que la comunicación semi-duplex necesita
normalmente uno solo. Para el intercambio de datos entre computadores
este tipo de comunicaciones son más eficientes que las transmisiones
semi-duplex.

Puerto serial
Los puertos seriales (también llamados RS-232, por el nombre del estándar al
que hacen referencia) fueron las primeras interfaces que permitieron que los
equipos intercambien información con el "mundo exterior". El término serial se
refiere a los datos enviados mediante un solo hilo: los bits se envían uno detrás
del otro (consulte la sección sobre transmisión de datos para conocer los
modos de transmisión).

29
Tópicos Selectos De Programación Actividades 2009

Originalmente, los puertos seriales sólo podían enviar datos, no recibir, por lo
que se desarrollaron puertos bidireccionales (que son los que se encuentran en
los equipos actuales). Por lo tanto, los puertos seriales bidireccionales
necesitan dos hilos para que la comunicación pueda efectuarse.
La comunicación serial se lleva a cabo asincrónicamente, es decir que no es
necesaria una señal (o reloj) de sincronización: los datos pueden enviarse en
intervalos aleatorios. A su vez, el periférico debe poder distinguir los caracteres
(un carácter tiene 8 bits de longitud) entre la sucesión de bits que se está
enviando.
Ésta es la razón por la cual en este tipo de transmisión, cada carácter se
encuentra precedido por un bit de ARRANQUE y seguido por un bit de
PARADA. Estos bits de control, necesarios para la transmisión serial,
desperdician un 20% del ancho de banda (cada 10 bits enviados, 8 se utilizan
para cifrar el carácter y 2 para la recepción).
Los puertos seriales, por lo general, están integrados a la placa madre, motivo
por el cual los conectores que se hallan detrás de la carcasa y se encuentran
conectados a la placa madre mediante un cable, pueden utilizarse para
conectar un elemento exterior. Generalmente, los conectores seriales tienen 9
ó 25 clavijas y tienen la siguiente forma (conectores DB9 y DB25
respectivamente):

Un PC posee normalmente entre uno y cuatro puertos seriales.

Puerto paralelo

La transmisión de datos paralela consiste en enviar datos en forma simultánea


por varios canales (hilos). Los puertos paralelos en los PC pueden utilizarse
para enviar 8 bits (un octeto) simultáneamente por 8 hilos.

Los primeros puertos paralelos bidireccionales permitían una velocidad de 2,4


Mb/s. Sin embargo, los puertos paralelos mejorados han logrado alcanzar
velocidades mayores:

30
Tópicos Selectos De Programación Actividades 2009

 El EPP (puerto paralelo mejorado) alcanza velocidades de 8 a 16 Mbps


 El ECP (puerto de capacidad mejorada), desarrollado por Hewlett
Packard y Microsoft. Posee las mismas características del EPP con el
agregado de un dispositivo Plug and Play que permite que el equipo reconozca
los periféricos conectados.

Los puertos paralelos, al igual que los seriales, se encuentran integrados a la


placa madre. Los conectores DB25 permiten la conexión con un elemento
exterior (por ejemplo, una impresora).

El nombre de “serie” viene por el hecho de que un puerto serie “serializa” los
datos. Esto significa que coge un byte de datos y transmite los 8 bits que
contiene el byte uno a la vez. La ventaja es que los puertos serie solo necesitan
un hilo para transmitir los 8 bits, mientras que los paralelo necesitan 8. La
desventaja es que lleva 8 veces más tiempo que si tuviera 8 hilos. Los puertos
serie bajan el coste de los cables y hacen que sean más pequeños.

(serial port). Puerto para conectar un dispositivo a una computadora. La


información se transmiten por un solo conducto y por lo tanto bit a bit de forma
serial. Por esta razón los puertos seriales tradicionales son sumamente lentos y
son usados para conectar el mouse o el teclado. En cambio el puerto paralelo
puede enviar múltiples bits en forma paralela.

La mayoría de los puertos serie de las PC obedecen el estándar RS-232C o el


RS-422.

Las computadoras tienen dos puertos seriales: COM1 y COM2

Actualmente los puertos seriales modernos poseen grandes velocidades como


el puerto USB, el firewire o el SATA.

USB.

(Universal Serial Bus) Puerto de gran velocidad para comunicar computadoras


y periféricos. Soporta plug & play y conexión en caliente (hot plugging).

Soporta transferencias de 12 MBps. Un sólo puerto USB permite ser usado


para conectar más de 127 dispositivos periféricos como ratones, módems,
teclados, impresoras, etc.

Comenzó a ser utilizado en 1996, pero la popularidad llegó en las iMac en


1998.

31
Tópicos Selectos De Programación Actividades 2009

Prácticamente reemplazó a los puertos serial y puertos paralelo.

Versiones USB más populares disponibles:


* USB 1.1
* USB 2.0 (HiSpeed)
* USB 3.0 (SuperSpeed USB)
Definición de USB 2.0

USB 2.0 es un bus externo que soporta hasta 480 Mbits/s de transferencia de
datos. Se trata de una extensión del USB 1.1, por lo tanto utiliza los mismos
cables y conectores, y es compatible con éste.

Fue desarrollado por Hewlett-Packard, Intel, Lucent, Microsoft, NEC y Philips.

USB 2.0 fue lanzado en abril de 2000.

Definición de USB 3.0

(SuperSpeed USB). USB 3.0 es una de las versiones del USB, sucesora de la
versión USB 2.0. Permite transferencias teóricas de hasta 4,8 Gbps.

Se lo ha llamado SuperSpeed USB, a diferencia de su versión anterior, la 2.0,


llamada HiSpeed.

USB 3.0 consume menos electricidad, es más rápida y sus puertos son
compatibles con los puertos USB 2.0.

Su desarrollo fue terminado en noviembre de 2008.

QUE ES UN PUERTO FIREWIRE.

32
Tópicos Selectos De Programación Actividades 2009
Firewire se denomina al tipo de puerto de comunicaciones de alta
velocidad desarrollado por la compañía Apple.
La denominación real de esta interfaz es la IEEE 1394. Se trata de
una tecnología para la entrada/salida de datos en serie a alta
velocidad y la conexión de dispositivos digitales.

Esta interfaz se caracteriza principalmente por:

- Su gran rapidez, siendo ideal para su utilización en aplicaciones


multimedia y almacenamiento, como videocámaras, discos duros,
dispositivos ópticos, etc...

- Alcanzan una velocidad de 400 megabits por segundo,


manteniéndola de forma bastante estable.

- flexibilidad de la conexión y la capacidad de conectar un máximo de


63 dispositivos.

- Acepta longitudes de cable de hasta 425 cm.

- Respuesta en el momento. FireWire puede garantizar una


distribución de los datos en perfecta sincronía.

- Alimentación por el bus. Mientras el USB 2.0 permite la


alimentación de dispositivos que consuman un máximo de 5v, , los
dispositivos FireWire pueden proporcionar o consumir hasta 25v,
suficiente para discos duros de alto rendimiento y baterías de carga
rápida. En este punto hay que hacer reseña de que existe un tipo de
puerto Firewire que no suministra alimentación, tan sólo da servicio
de comunicación de datos. Estos puertos tienen sólo 4 contactos, en
lugar de los 6 que tiene un puerto Firewire alimentado.

- Conexiones de enchufar y listo, conocidas como plug & play. No


tenemos más que enchufar un dispositivo para que funcione.

- Conexión en caliente (permite conectar dispositivos con el PC


encendido sin ningún riesgo de rotura).

Firewire

33
Tópicos Selectos De Programación Actividades 2009
Bus serial de Apple; es un solo enchufe, pero pueden conectarse
hasta 63 dispositivos. Transfiere información de hasta 400 MB por
segundos.

Firewire se denomina al tipo de puerto de comunicaciones de alta velocidad


desarrollado por la compañía Apple.
La denominación real de esta interfaz es la IEEE 1394. Se trata de una
tecnología para la entrada/salida de datos en serie a alta velocidad y la
conexión de dispositivos digitales.

Esta interfaz se caracteriza principalmente por:

- Su gran rapidez, siendo ideal para su utilización en aplicaciones multimedia y


almacenamiento, como videocámaras, discos duros, dispositivos ópticos, etc...

- Alcanzan una velocidad de 400 megabits por segundo, manteniéndola de


forma bastante estable.

- flexibilidad de la conexión y la capacidad de conectar un máximo de 63


dispositivos.

- Acepta longitudes de cable de hasta 425 cm.

- Respuesta en el momento. FireWire puede garantizar una distribución de los


datos en perfecta sincronía.

- Alimentación por el bus. Mientras el USB 2.0 permite la alimentación de


dispositivos que consuman un máximo de 5v, , los dispositivos FireWire pueden
proporcionar o consumir hasta 25v, suficiente para discos duros de alto
rendimiento y baterías de carga rápida. En este punto hay que hacer reseña de
que existe un tipo de puerto Firewire que no suministra alimentación, tan sólo
da servicio de comunicación de datos. Estos puertos tienen sólo 4 contactos,
en lugar de los 6 que tiene un puerto Firewire alimentado.

- Conexiones de enchufar y listo, conocidas como plug & play. No tenemos más
que enchufar un dispositivo para que funcione.

- Conexión en caliente (permite conectar dispositivos con el PC encendido sin


ningún riesgo de rotura).

34
Tópicos Selectos De Programación Actividades 2009

Firewire

Bus serial de Apple; es un solo enchufe, pero pueden conectarse hasta 63


dispositivos. Transfiere información de hasta 400 MB por segundos.
IEEE 1394

Logotipo de FireWire. Conector FireWire de 6 pins.

Connectores de 6 y 4 pins.

El IEEE 1394 (conocido como FireWire por Apple Inc. y como i.Link por Sony)
es un estándar multiplataforma para entrada/salida de datos en serie a gran
velocidad. Suele utilizarse para la interconexión de dispositivos digitales como
cámaras digitales y videocámaras a computadoras.

Historia

El Firewire fue desarrollado por Apple Computer a mediados de los 90, para
luego convertirse en el estándar multiplataforma IEEE 1394. A principios de
este siglo fue adoptado por los fabricantes de periféricos digitales hasta
convertirse en un estándar establecido. Sony utiliza el estándar IEEE 1394 bajo
la denominación i.Link, y Texas Instruments bajo la denominación Lynx.

Versiones

Su velocidad hace que sea la interfaz más utilizada para audio y vídeo digital.
Así, se usa mucho en cámaras de vídeo, discos duros, impresoras,
reproductores de vídeo digital, sistemas domésticos para el ocio, sintetizadores
de música y escáneres.

35
Tópicos Selectos De Programación Actividades 2009
Existen tres versiones:

FireWire 400 (IEEE 1394-1995)

Lanzado en 1995. Tiene un ancho de banda de 400 Mbit/s, 30 veces mayor


que el USB 1.1 (12 Mbps) y similar a la del USB 2.0 (480 Mbps), aunque en
pruebas realizadas, en transferencias de lectura de 5000 ficheros con un total
de 300 Mb, FireWire completó el proceso con un 33% más de velocidad que
USB 2.0, debido a su arquitectura peer-to-peer mientras USB utiliza
arquitectura slave-master. La longitud máxima permitida con un único cable es
de 4,5 metros, pudiendo utilizarse hasta 16 repetidores para prolongar la
longitud (no pudiendo sobrepasar nunca la distancia de 72 metros). Su
conector está dotado de 6 pines, dos de ellos destinados a la alimentación del
dispositivo (excepto en la versión distribuida por sony, iLink, que carece de
estos dos pines de alimentación) ofreciendo un consumo de unos 7 u 8 W por
puerto a 25 V (nominalmente).

Revisión IEEE 1394a-1995

En 2000 se implementó una revisión de IEEE 1394-1995, añadiéndole


características como difusión asíncrona, una reconfiguración de bus más
rápida, concatenación de paquetes, y ahorro de energía en modo suspensión.

FireWire 800 (IEEE 1394b-2000)

Lanzado en 2000. Duplica aproximadamente la velocidad del FireWire 400,


hasta 786.5 Mbps con tecnología full-duplex, cubriendo distancias de hasta 100
metros por cable. Firewire 800 reduce los retrasos en la negociación, utilizando
para ello 8b10b (código que codifica 8 bits en 10 bits, que fue desarrollado por
IBM y permite suficientes transiciones de reloj, la codificación de señales de
control y detección de errores. El código 8b10b es similar a 4B/5B de FDDI
(que no fue adoptado debido al pobre equilibrio de corriente continua), que
reduce la distorsión de señal y aumenta la velocidad de transferencia. Así, para
usos que requieran la transferencia de grandes volúmenes de información,
resulta muy superior al USB 2.0. Posee compatibilidad retroactiva con Firewire
400 utilizando cables híbridos que permiten la conexión en los conectores de
Firewire400 de 6 pines y los conectores de Firewire800, dotados de 9 pines. No
fue hasta 2003 cuando Apple lanzó el primer uso comercial de Firewire800.

FireWire s1600 y s3200 (IEEE 1394-2008)

Anunciado en Diciembre de 2007. Permiten un ancho de banda de 1'6 y 3'2


Gbit/s, cuadruplicando la velocidad del Firewire 800, utilizando el mismo
conector de 9 pines de Firewire800

36
Tópicos Selectos De Programación Actividades 2009
FireWire s800T (IEEE 1394c-2006)

Anunciado en Junio de 2007. Aporta mejoras técnicas que permite el uso de


FireWire con puertos RJ45 sobre cable CAT 5, combinando así las ventajas de
Ethernet con Firewire800.

Características generales

 Soporta la conexión de hasta 63 dispositivos con cables de una longitud


máxima de 425 cm con topología en árbol.
 Soporte Plug-and-play.
 Soporta comunicación peer-to-peer que permite el enlace entre
dispositivos sin necesidad de usar la memoria del sistema o la CPU
 Soporta conexión en caliente.
 Todos los dispositivos Firewire son identificados por un identificador
IEEE EUI-64 exclusivo (una extensión de las direcciones MAC Ethernet
de 48-bit)

Aplicaciones de FireWire

Edición de vídeo digital


La edición de vídeo digital con FireWire ha permitido que tuviera lugar una
revolución en la producción del vídeo con sistemas de escritorio. La
incorporación de FireWire en cámaras de vídeo de bajo costo y elevada calidad
permite la creación de vídeo profesional en Macintosh o PC. Atrás quedan las
carísimas tarjetas de captura de vídeo y las estaciones de trabajo con
dispositivos SCSI de alto rendimiento. FireWire permite la captura de vídeo
directamente de las nuevas cámaras de vídeo digital con puertos FireWire
incorporados y de sistemas analógicos mediante conversores de audio y vídeo
a FireWire.

37
Tópicos Selectos De Programación Actividades 2009

Redes IP sobre FireWire


Como explica Apple, "con este software instalado, se pueden utilizar entre
computadoras Macintosh y periféricos los protocolos IP existentes, incluyendo
AFP, HTTP, FTP, SSH, etcétera. En todos los casos, se puede utilizar Bonjour
(Rendezvous) para su configuración, resolución de nombres y descubrimiento."
Si unimos la posibilidad de usar las conexiones FireWire para crear redes
TCP/IP a las prestaciones de FireWire 2 (FireWire 800), tenemos razones muy
serias para que Apple recupere rápidamente la atención de los fabricantes de
periféricos para satisfacer las necesidades de los usuarios de aplicaciones que
requieren gran ancho de banda en redes locales, como todas las relacionadas
con el vídeo digital. Por no hablar de introducirse en un posible mercado nuevo.

38
Tópicos Selectos De Programación Actividades 2009
TEMAS:

LICENCIAS
COPYRIGHT
GPL
GNU
LGPL
VREATIVE CAMMONS
NIT

Licencia copyright
El derecho de autor y copyright constituyen dos concepciones sobre
la propiedad literaria y artística. El primero proviene de la familia
delderecho continental, particularmente del derecho francés, mientras
que el segundo procede del derecho anglosajón (o common law).

El derecho de autor se basa en la idea de un derecho personal


del autor, fundado en una forma de identidad entre el autor y su
creación. El derecho moral está constituido como emanación de la
persona del autor: reconoce que la obra es expresión de la persona
del autor y así se le protege.

La protección del copyright se limita estrictamente a la obra, sin


considerar atributos morales del autor en relación con su obra,
excepto la paternidad; no lo considera como un autor propiamente
tal, pero tiene derechos que determinan las modalidades de
utilización de una obra.

En 1790, la obras protegidas por la Copyright Act de Estados Unidos


eran sólo los "mapas, cartas de navegación y libros" (no cubría las
obrasmusicales o de arquitectura). Este copyright otorgaba al autor el
derecho exclusivo a publicar las obras, por lo que sólo se violaba tal
derecho si reimprimía la obra sin el permiso de su titular. Además,
este derecho no se extendía a las "obras derivadas" (era un derecho
exclusivo sobre la obra en particular), por lo que no impedía las
traducciones o adaptaciones de dicho texto. Con los años, el titular
del copyright obtuvo el derecho exclusivo a controlar cualquier
39
Tópicos Selectos De Programación Actividades 2009

publicación de su obra. Sus derechos se extendieron, de la obra en


particular, a cualquier "obra derivada" que pudiera surgir en base a la
"obra original".

Asimismo, el Congreso de Estados Unidos incrementó


en 1831 el plazo inicial del copyright de 14 a 28 años (o sea, se llegó
a un máximo de 42 años de protección) y en 1909 extendió el plazo
de renovación de 14 a 28 años (obteniéndose un máximo de 56 años
de protección). Y, a partir de los años 50, comenzó a extender los
plazos existentes en forma habitual (1962, 1976 y 1998).

El símbolo ℗ (una letra "P" mayúscula ubicada dentro de un círculo)


representa la reserva de los "derechos de autor sobre una grabación
sonido" (música) y es la abreviatura para la palabra "fonógrafo"
(phonograph en inglés) o registro fonográfico. Este símbolo hace
referencia más directamente a la obra musical en sí grabada en un
determinado disco, casete, CD, etc., de hecho, es muy común verlo
impreso en las contraportadas de los álbumes musicales.

Por otro lado, el símbolo © (una letra "C" mayúscula dentro de


una circunferencia) hace referencia más propiamente al derecho de
autor (copyright) sobre obras intelectuales de otra índole, como por
ejemplo: libros, folletos, obras dramáticas, obras cinematográficas y
audiovisuales; dibujos, pinturas etc.

La diferencia entre el significado de un símbolo y otro es muy tenue.

Licencia GPL

Una de las más utilizadas es la Licencia Pública General de GNU (GNU


GPL). El autor conserva los derechos de autor (copyright), y permite
la redistribución y modificación bajo términos diseñados para
asegurarse de que todas las versiones modificadas del software
permanecen bajo los términos más restrictivos de la propia GNU GPL.

40
Tópicos Selectos De Programación Actividades 2009

Esto hace que sea imposible crear un producto con partes no


licenciadas GPL: el conjunto tiene que ser GPL.

Es decir, la licencia GNU GPL posibilita la modificación y redistribución


del software, pero únicamente bajo esa misma licencia. Y añade que
si se reutiliza en un mismo programa código "A" licenciado bajo
licencia GNU GPL y código "B" licenciado bajo otro tipo de licencia
libre, el código final "C", independientemente de la cantidad y calidad
de cada uno de los códigos "A" y "B", debe estar bajo la licencia GNU
GPL.

En la práctica esto hace que las licencias de software libre se dividan


en dos grandes grupos, aquellas que pueden ser mezcladas con
código licenciado bajo GNU GPL (y que inevitablemente
desaparecerán en el proceso, al ser el código resultante licenciado
bajo GNU GPL) y las que no lo permiten al incluir mayores u otros
requisitos que no contemplan ni admiten la GNU GPL y que por lo
tanto no pueden ser enlazadas ni mezcladas con código gobernado
por la licencia GNU GPL.

En el sitio web oficial de GNU hay una lista de licencias que cumplen
las condiciones impuestas por la GNU GPL y otras que no.4

Aproximadamente el 60% del software licenciado como software libre


emplea una licencia GPL.

A finales de 2005, la Free Software Foundation anunció que trabajaba


en la versión 3 de la licencia GPL, cuyo primer borrador fue
presentado para su discusión pública el 16 de enero de 2006. La
discusión se alargó 18 meses, habiendo sido publicados cuatro
borradores. Finalmente, la versión oficial fue publicada el día 29 de
junio de 2007 y es accesible a través del Portal de GNU.4 La nueva
versión contempla los siguientes aspectos:

 Las diversas formas en que alguna persona podría quitar


libertades a los usuarios.
 Prohibir el uso de software libre en sistemas diseñados para
quitar libertades (DRM).
 Resolver ambigüedades y aumentar la compatibilidad de GPLv3
con otras licencias.
 Facilitar su adaptación a otros países.

41
Tópicos Selectos De Programación Actividades 2009

 Incluir cláusulas que defiendan a la comunidad de software libre


del uso indebido de las patentes de software.
 Mostrar usuarios registrados.

Licencia GNU
La Licencia Pública General de GNU o más conocida por su nombre
en inglés GNU General Public License o simplemente su acrónimo del
inglés GNU GPL, es una licencia creada por la Free Software
Foundation a mediados de los 80, y está orientada principalmente a
proteger la libre distribución, modificación y uso de software. Su
propósito es declarar que el software cubierto por esta licencia
essoftware libre y protegerlo de intentos de apropiación que
restrinjan esas libertades a los usuarios.

Existen varias licencias "hermanas" de la GPL, como la licencia de


documentación libre de GNU(GFDL) que cubre los artículos de
la Wikipedia, la Open Audio License, para trabajos musicales,
etcétera, y otras menos restrictivas, como la MGPL, o la LGPL (Lesser
General Public License, antesLibrary General Public License), que
permiten el enlace dinámico de aplicaciones libres a aplicaciones no
libres.
GNU/Linux (Linux) es uno de los términos empleados para referirse
al sistema operativo libre similar aUnix que utiliza el núcleo Linux y
herramientas de sistema GNU. Su desarrollo es uno de los ejemplos
más prominentes de software libre; todo el código fuente puede ser
utilizado, modificado y redistribuido libremente por cualquiera bajo
los términos de la GPL (Licencia Pública General de GNU) y otras
licencias libres.1

A pesar de que Linux sólo es el núcleo de este sistema operativo una


parte significativa de la comunidad, así como muchos medios
generales y especializados, prefieren utilizar dicho término. Para más
información consulte la sección "Denominación GNU/Linux" o el
artículo "Controversia por la denominación GNU/Linux".

42
Tópicos Selectos De Programación Actividades 2009

Las variantes de este sistema se denominan distribuciones y su


objetivo es ofrecer una edición que cumpla con las necesidades de
determinado grupo de usuarios.

Algunas distribuciones son especialmente conocidas por su uso


en servidores y supercomputadoras.2No obstante, es posible instalar
GNU/Linux en una amplia variedad de hardware como computadoras
de escritorio y portátiles.

En el caso de computadoras de bolsillo, teléfonos


móviles, dispositivos empotrados, videoconsolas y otros, puede darse
el caso en que las partes de GNU se remplacen por alternativas más
adecuadas. Para saber más sobre las arquitecturas soportadas, lea el
artículo "Portabilidad del núcleo Linux y arquitecturas soportadas".

Licencia LGPL

La Licencia Pública General Reducida de GNU, o más conocida por su


nombre en inglés GNU Lesser General Public License (antes GNU
Library General Public License o Licencia Pública General para
Bibliotecas de GNU), o simplemente por su acrónimo del inglés GNU
LGPL es una licencia de software creada por la Free Software
Foundation. Los contratos de licencia de la mayor parte
delsoftware están diseñados para jugar con su libertad de compartir y
modificar dicho software. En contraste, la GNU General Public
License pretende garantizar su libertad de compartir y modificar el
software "libre", esto es para asegurar que el software es libre para
todos sus usuarios. Esta licencia pública general se aplica a la
mayoría del software de la FSF o Free Software
Foundation (Fundación para el software libre) y a cualquier otro
programa de software cuyos autores así lo establecen. Algunos otros
programas de software de la Free Software Foundation están
cubiertos por la "LGPL Lesser General Public License" (Licencia pública
general reducida), la cual puede aplicar a sus programas también.

43
Tópicos Selectos De Programación Actividades 2009

Esta licencia permisiva se aplica a cualquier programa o trabajo que


contenga una nota puesta por el propietario de los derechos del
trabajo estableciendo que su trabajo puede ser distribuido bajo los
términos de esta "GPL General Public License". El "Programa",
utilizado en lo subsecuente, se refiere a cualquier programa o trabajo
original, y el "trabajo basado en el Programa" significa ya sea el
programa o cualquier trabajo derivado del mismo bajo la ley de
derechos de autor: es decir, un trabajo que contenga el Programa o
alguna porción de él, ya sea íntegra o con modificaciones o
traducciones a otros idiomas.

Otras actividades que no sean copia, distribución o modificación si


están cubiertas en esta licencia y están fuera de su alcance. El acto
de ejecutar el programa no está restringido, y la salida de
información del programa está cubierta sólo si su contenido
constituye un trabajo basado en el Programa (es independiente de si
fue resultado de ejecutar el programa). Si esto es cierto o no
depende de la función del programa.

El proyecto OpenOffice.org de Sun Microsystems emplea la LGPL.

El término "GNU Library General Public License" daba la impresión de


que la FSF quería que todas las bibliotecas utilizaran la licencia LGPL
y todos los programas utilizaran la licencia GPL. En febrero de
1999 Richard Stallman escribió el documento "Por qué en su próxima
biblioteca no debería utilizar la GPL para Bibliotecas"1 explicando
porqué este no era el caso, y que la LGPL no se debería utilizar
necesariamente para bibliotecas:
Qué licencia es la mejor para una cierta biblioteca es una cuestión de
estrategia, y depende de los detalles de la situación. Actualmente, la
mayoría de las bibliotecas GNU están cubiertas por la GPL para
bibliotecas, y eso significa que estamos utilizando únicamente una de
estas dos estrategias, descuidando la otra. Así que ahora
pretendemos que se publiquen más bibliotecas bajo la GPL ordinaria.
Sin embargo, contrario a la impresión general, esto no significa que
la FSF infravalore la LGPL, sino simplemente dice que no debería ser
utilizada para todas las bibliotecas. En el mismo documento se lee:

44
Tópicos Selectos De Programación Actividades 2009

Hay razones que pueden hacer más apropiado el uso de la GPL para
bibliotecas en ciertos casos. El caso más común es cuando las
características de la biblioteca libre están ya disponibles para
software privativo a través de otras bibliotecas alternativas. En ese
caso, la biblioteca no puede dar al software libre ninguna ventaja en
particular, así que es mejor utilizar la GPL para bibliotecas para esa
biblioteca.
De hecho, Stallman y la FSF abogan por el uso de licencias incluso
menos restrictivas que la LGPL como estrategia (para maximizar la
libertad de los usuarios). Un ejemplo destacado es la aprobación de
Stallman para utilizar la licencia BSD en el proyecto Vorbis.2

Licencia creative commons

Creative Commons es una organización no gubernamental sin ánimo


de lucro que desarrolla planes para ayudar a reducir las barreras
legales de la creatividad, por medio de nueva legislacióny
nuevas tecnologías. Fue fundada por Lawrence Lessig, profesor
de derecho en la Universidad de Stanford y especialista
en ciberderecho, que la presidió hasta marzo de 2008.

Creative Commons (en español: «bienes comunes creativos») es


también el nombre dado a laslicencias desarrolladas por esta
organización.

Las licencias Creative Commons o CC están inspiradas en la


licencia GPL (General Public License) de la Free Software Foundation,
sin embargo no son un tipo de licenciamiento de software. La idea
principal es posibilitar un modelo legal ayudado por

45
Tópicos Selectos De Programación Actividades 2009

herramientas informáticaspara así facilitar la distribución y el uso de


contenidos.

Existe una serie de licencias Creative Commons, cada una con


diferentes configuraciones o principios, como el derecho del autor
original a dar libertad para citar su obra, reproducirla, crear obras
derivadas, ofrecerla públicamente y con diferentes restricciones como
no permitir el uso comercial o respetar la autoría original.

Una de las licencias que ofrecía Creative Commons es la que llevaba


por nombre "Developing Nations" (Naciones en Desarrollo), la cual
permitía que los derechos de autor y regalías por el uso de las obras
se cobraran sólo en los países desarrollados del primer mundo,
mientras que se ofrecían de forma abierta en los países en vías de
desarrollo. Esta licencia ha sido retirada por problemas comerciales.

Aunque originalmente fueron redactadas en inglés, las licencias han


sido adaptadas a varias legislaciones en otros países del mundo.
Entre otros idiomas, han sido traducidas al español, al portugués,
al gallego, al euskera y al catalán a través del proyecto Creative
Commons International. Existen varios países de habla hispana que
están involucrados en este
proceso: España, Chile, Guatemala, Argentina, México,Perú, Colombia
y Puerto Rico ya tienen las licencias traducidas y en funcionamiento,
en tanto que Ecuador y Venezuela se encuentran en proceso de
traducción e implementación de las mismas. Brasil también tiene las
licencias traducidas y adaptadas a su legislación.

LICENCIA MIT

Seguimos con esta serie sobre licencias de Software. Ya describí más


o menos lo que es una licencia, y ahora vamos a revisar la primera
de la lista: la licencia MIT.

Se trata de una de tantas licencias que ha empleado el MIT


(Massachusetts Institute of Technology) a lo largo de su historia, y

46
Tópicos Selectos De Programación Actividades 2009

quizás debería llamarse más correctamente 'licencia X11', ya que es


la licencia que llevaba este Software originario del MIT en los años
80. Pero ya sea como MIT o X11, su forma es idéntica.

Es una licencia sin copyright, lo que nos permite modificarla y


adaptarla a nuestras necesidades. No obstante esto puede no ser
recomendable, e incluso muchas voces dentro del Open Source lo
desaconsejan. Recordemos que las licencias es un terreno escabroso
difícil de transitar, y si un usuario o desarrollador ve que un paquete
tiene licencia MIT siempre sabrá a qué atenerse. La cosa es distinta si
la licencia está 'basada en la licencia MIT', lo que obligaría a una
revisión para asegurarse de qué efectos tienen esas modificaciones.
Siempre es más fácil elegir una licencia existente, que las hay para
todos los gustos, en lugar de jugar a entender de leyes :).

Como ya veremos, la licencia BSD es muy parecida a la licencia MIT


en cuanto a efectos. Pero veamos la forma:

Copyright (c) <year> <copyright holders>

Permission is hereby granted, free of charge, to any


person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice


shall be included in all copies or substantial portions of
the Software.

47
Tópicos Selectos De Programación Actividades 2009

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY


KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

El texto diferencia los tres puntos que comentamos en la pasada


entrega: condiciones, derechos y limitación de responsabilidad.

La condición es que la nota de copyright y la parte de los derechos se


incluya en todas las copias o partes sustanciales del Software. Esta es
la condición que invalidaría la licencia en caso de no cumplirse.

Los derechos son muchos: sin restricciones; incluyendo usar, copiar,


modificar, integrar con otro Software, publicar,sublicenciar y/o
vender copias del Software, y además permitir a las personas a las
que se les entregue el Software hacer lo mismo.

Finalmente tenemos un disclaimer o nota de limitación de la


responsabilidad estándar.

Ahora vamos a ver algunas características y usos de esta licencia.

Debido a la palabra resaltada en los derechos, tenemos que esta


licencia permite reutilizar el Software así licenciado tanto para ser
Software Libre como para ser Software privativo. Esto significa que el
hecho de permitir sublicenciar puede llevar a un trabajo derivado que
sea cerrado, o incluso bajo la licencia BSD, GPL, u otra cualquiera
compatible con la MIT.

48
Tópicos Selectos De Programación Actividades 2009

Esto puede ser una ventaja, en caso de hacer un producto que en un


momento dado puede aportar un beneficio por cerrarse (por ejemplo
en esquemas de licencias duales: se trabaja con MIT para uso
comercial a cambio de una retribución económica, y se sublicencia
GPL para el uso de la comunidad). Pero también un inconveniente si
no deseamos que nuestro trabajo se utilice en un producto cerrado.

Su aplicación es muy sencilla. Solo tendremos que añadir al fuente el


texto de la licencia con el año en que liberamos ese fuente y nuestro
nombre completo (y si puede ser una dirección de contacto, el e-mail
es suficiente).

Con esta licencia tenemos Software Libre. Nos puede interesar si


tenemos una estrategia comercial basada, por ejemplo, en las
licencias duales; si pretendemos que nuestro desarrollo se convierta
en un estándar y queremos facilitar su implantación, o si
simplemente pretendemos que nuestro producto sea Libre sin
mayores consideraciones.

Yo la empleo con frecuencia en desarrollos a medida en los que el


cliente paga el I+D. Obligatoriamente, y si no se pacta explícitamente
otra cosa, he de entregar el fuente, ya que es el cliente el que ha
pagado el desarrollo y el producto le pertenece. El hecho de usar la
licencia MIT me permite beneficiarme de ese código para proyectos
posteriores, y el cliente no pone pegas porque reutilizo código
existente y abarato el producto final.

49
Tópicos Selectos De Programación Actividades 2009
TEMAS:

CAMPOS DE BIT
HILOS
SYSTEM OBJECT
SET (#)
GET (#)

Campos de Bits

Los campos de bits, o simplemente campos, son grupos de un número


determinado de bits, que pueden o no tener un identificador asociado.
Representan un artificio que permite utilizar miembros de tamaño
arbitrario en estructuras, uniones y clases; independiente de la posibilidad
que proporcionan los tipos básicos cuyo tamaño está predeterminado por el
lenguaje. Por ejemplo, en ocasiones es necesario almacenar semáforos
(flags) con determinados estados del programa, para los que en realidad
solo hace falta un bit, pero incluso una variable bool ocupa un octeto. Los
campos de bits permiten utilizar cada bit de un octeto independientemente,
aumentando así su capacidad de representación.

Nota: esta técnica, de manejo independiente de bits en una palabra, ha sido


ampliamente utilizada desde siempre en la programación, no solo de C/C++;
casi todos los lenguajes ofrecen la posibilidad de operadores "bitwise", que
permiten esto de forma más o menos artesanal.

Entre otros usos, los campos de bits se han utilizado históricamente para
empaquetar variables en un espacio más pequeño, pero obligan al compilador
a generar código adicional para manejarlos, lo que resulta costoso en
términos de tamaño y velocidad del ejecutable. El resultado es que
frecuentemente, el código resulta mayor y más lento si se usan estos tipos,
por lo que generalmente se desaconseja su uso excepto para aplicaciones
muy específicas de bajo nivel, en las que la alineación exacta de los patrones
de bits a utilizar es un aspecto primordial. Por ejemplo, transmisiones de
datos

50
Tópicos Selectos De Programación Actividades 2009
Otra cuestión distinta, a veces decisiva para su utilización, es la
significativa reducción de espacio de almacenamiento externo (disco por
ejemplo) que puede conseguirse cuando en determinados casos, se almacena
gran número de registros que utilizan campos de bits en sustitución de tipos
básicos.

Declaración

La sintaxis para declaración de campos es la siguiente:

especificador-de-tipo <identificador> : ancho;

Ejemplos:

int Uno : 8;
unsigned int Dos : 16;
int : 2;

El especificador-de-tipo puede ser alguno de los siguientes: bool; char;


unsigned char; short; unsigned short; long; unsigned long; int; unsigned int;
__int64 o unsigned __int64. Abreviadamente lo denominaremos tipo del
campo.

Definición de Interfaz

1. En software, parte de un programa que permite el flujo de información


entre un usuario y la aplicación, o entre la aplicación y otros programas o
periféricos. Esa parte de un programa está constituida por un conjunto de
comandos y métodos que permiten estas intercomunicaciones.

2. Intefaz también hace referencia al conjunto de métodos para lograr


interactividad entre un usuario y una computadora. Una interaz puede ser

51
Tópicos Selectos De Programación Actividades 2009
del tipo GUI, o línea de comandos, etc. También puede ser a partir de un
hardware, por ejemplo, el monitor, el teclado y el mouse, son interfaces
entre el usuario y el ordenador.

n C#, una clase es un tipo de datos muy eficaz. Como las estructuras, las
clases definen los datos y el comportamiento del tipo de datos. Los
programadores pueden crear objetos que son instancias de una clase. A
diferencia de las estructuras, las clases admiten herencia, que es una parte
fundamental de la programación orientada a objetos. Para obtener más
información, vea Herencia.

Declarar clases

Las clases se definen mediante la palabra clave class, como se muestra en el


ejemplo siguiente:

C#

public class Customer


{
//Fields, properties, methods and events go here...
}

El nivel de acceso precede a la palabra clave class. En este caso, se utiliza


public, que significa que cualquiera puede crear objetos a partir de esta
clase. El nombre de la clase sigue a la palabra clave class. El resto de la
definición es el cuerpo de clase, donde se definen el comportamiento y los
datos. Los campos, propiedades, métodos y eventos de una clase se conocen
colectivamente como miembros de clase.

Crear objetos

Aunque se utilizan a veces de forma intercambiable, una clase y un objeto


son cosas diferentes. Una clase define un tipo de objeto, pero no es
propiamente un objeto. Un objeto es una entidad concreta basada en una
clase y, a veces, se denomina instancia de una clase.

52
Tópicos Selectos De Programación Actividades 2009
Los objetos se pueden crear con la palabra clave new seguida del nombre de
la clase en la que se basará el objeto, de la manera siguiente:

C#

Customer object1 = new Customer();

Cuando se crea una instancia de una clase, una referencia al objeto se


vuelve a pasar al programador. En el ejemplo anterior, object1 es una
referencia a un objeto basado en Customer. Esta referencia hace referencia
el nuevo objeto, pero no contiene los datos del propio objeto. De hecho, se
puede crear una referencia a objeto sin crear un objeto:

C#

Customer object2;

No se recomienda la creación de referencias a objetos como ésta, que no


hace referencia a un objeto, porque al intentar el acceso a un objeto a
través de esa referencia se producirá un error en tiempo de ejecución. Sin
embargo, este tipo de referencia se puede crear para hacer referencia a un
objeto, ya sea creando un nuevo objeto o asignándola a un objeto existente,
de la forma siguiente:

C#

Customer object3 = new Customer();


Customer object4 = object3;

Este código crea dos referencias a objeto que se refieren al mismo objeto.
Por consiguiente, los cambios realizados en el objeto a través de object3 se
reflejarán en los usos posteriores de object4. El hecho de que las clases se
conozcan como tipos de referencia se debe a que se hace referencia a los
objetos basados en clases por referencia.

Herencia de clase

La herencia se realiza a través de una derivación, lo que significa que una


clase se declara utilizando una clase base de la cual hereda los datos y el
comportamiento. Una clase base se especifica anexando dos puntos y el

53
Tópicos Selectos De Programación Actividades 2009
nombre de la clase base a continuación del nombre de la clase derivada, del
modo siguiente:

C#

public class Manager : Employee


{
// Employee fields, properties, methods and events are inherited
// New Manager fields, properties, methods and events go here...
}

Cuando una clase declara una clase base, todos los miembros de clase
definidos para la clase base también pasan a formar parte de la nueva clase.
Dado que una clase base se puede heredar de otra clase, que a su vez se
heredó de otra clase y así sucesivamente, una clase puede provenir de
varias clases base.

Las estructuras se definen mediante la palabra clave struct, por ejemplo:

C#

public struct PostalAddress


{
// Fields, properties, methods and events go here...
}

Casi todas las estructuras comparten la misma sintaxis que las clases,
aunque están más limitadas que éstas:

 Dentro de una declaración de estructura, los campos no se pueden


inicializar a menos que se declaren como constantes o estáticos.
 Una estructura no puede declarar un constructor predeterminado (es
decir, un constructor sin parámetros) ni un destructor.

El compilador crea y destruye automáticamente copias de estructuras, de


modo que un constructor y destructor predeterminados son innecesarios.
De hecho, el compilador implementa el constructor predeterminado
asignando a todos los campos sus valores predeterminados (vea Tabla de
valores predeterminados (Referencia de C#)). Las estructuras no pueden
heredar de clases u otras estructuras.

Las estructuras son tipos de valor; cuando un objeto se crea a partir de una
estructura y se asigna a una variable, la variable contiene el valor completo

54
Tópicos Selectos De Programación Actividades 2009
de la estructura. Cuando se copia una variable que contiene una estructura,
todos los datos se copian y cualquier modificación a la nueva copia no cambia
los datos de la copia antigua. Como las estructuras no utilizan referencias,
no tienen identidad; no existe ninguna forma de distinguir entre dos
instancias de un tipo de valor con los mismos datos. En C#, todos los tipos
de valor derivan inherentemente de ValueType, que hereda de Object.

El compilador puede convertir tipos de valor en tipos de referencia en un


proceso conocido como conversión boxing. Para obtener más información,
vea Boxing y Unboxing.

Información general sobre las estructuras

Las estructuras tienen las propiedades siguientes:

 Las estructuras son tipos de valor, mientras que las clases son tipos
de referencia.
 A diferencia de las clases, se pueden crear instancias de las
estructuras sin utilizar un operador new.
 Las estructuras pueden declarar constructores, pero deben utilizar
parámetros.
 Una estructura no puede heredar de otra estructura o clase, ni puede
ser la base de una clase. Todas las estructuras heredan directamente
de System.ValueType, que hereda de System.Object.
 Una estructura puede implementar interfaces.

HILOS
La creación de cada hilo se realiza mediante las líneas Thread th1 = new
Thread(new ThreadStart(msg.Mostrar1));. Esta línea indica que se crea una
instancia de la clase Thread, con nombre th1, a partir de un delegado de la
clase ThreadStart, que apunta al método Mostrar1 del objeto msg creado
anteriormente.

Una vez creados los dos hilos hay que activarlos, para lo que se llama al
método Start de cada uno de ellos. Tras este punto cada hilo se ejecuta en
paralelo entre si, y con el programa principal, por lo que utilizamos el

55
Tópicos Selectos De Programación Actividades 2009
método Join de ambos hilos para esperar a que terminen los hilos antes de
finalizar el programa.

El delegado ThreadStart no acepta parámetros de entrada ni de salida, por


lo que si queremos crear un hilo sobre un método que los necesite, hay que
utilizar algún mecanismo auxiliar. Una posible forma de conseguir esto es
crear una nueva clase con los parámetros necesarios en la entrada y con un
nuevo método sin parámetros que llame al método que queremos hacer
paralelo, enviándole estos parámetros. A partir de aquí tendríamos que
crear una instancia de dicha clase con los parámetros que queremos enviar
al método original, y hacer que el hilo se ejecutase sobre el nuevo método
de la clase. En el caso de que quisiéramos obtener el resultado de la
ejecución, deberíamos crear una función que acepte como parámetro de
entrada el tipo del valor devuelto por el método original, y hacer que la
nueva clase creada disponga también de un delegado que indique la función a
la que llamar tras la ejecución.

Como esto puede parecer un poco lioso, vamos a ver otro ejemplo. En esta
ocasión disponemos de una clase de funciones matemáticas y queremos
llamar de forma paralela a una de ellas. Este método acepta un valor entero
en la entrada y devuelve otro entero.

using System;
using System.Threading;
using System.IO;

public class EjemploMates{

public static int CalculoComplejo(int n)


{
// sumo uno y espero 5 segundos
n = n+1;
Thread.Sleep(5000);
return n;
}

public class HiloParaMates{


protected int n;
protected MatesCallback callback = null;

56
Tópicos Selectos De Programación Actividades 2009
public HiloParaMates(int n, MatesCallback callback){
this.n = n;
this.callback = callback;
}
public void CalculoComplejo()
{
int result = EjemploMates.CalculoComplejo(n);
if(callback != null)
callback(result);
}
}

// creo un delegado con la firma necesaria para capturar


// el valor devuelto por el método CalculoComplejo
public delegate void MatesCallback(int n);

public class Ejemplo{

public static void Main()


{
HiloParaMates hpm = new HiloParaMates(1000, new
MatesCallback(ResultCallback));

Thread th = new Thread(new ThreadStart(hpm.CalculoComplejo));

th.Start();
th.Join();

public static void ResultCallback(int n)


{
Console.WriteLine("Resultado de la operación: "+n);
}
}

En el anterior código la clase HiloParaMates es la que nos permite


encapsular la llamada al método EjemploMates.Calcular. Este método
requiere un parámetro de tipo entero, por lo que la clase requiere este
parámetro en su constructor. Además se requiere en el constructor otro
parámetro más, un delegado MatesCallback, que acepta un entero en la

57
Tópicos Selectos De Programación Actividades 2009
entrada. La idea es que tras realizar el cálculo se llame al método que se
indique proporcionándole el resultado.

Para hacer funcionar todo esto, en Main se crea una instancia de la clase
HiloParaMates indicándole que queremos utilizar el valor numérico 1000 y
que se llame al método (estático) ResultCallback cuando se obtenga el
resultado. Para crear el hilo es suficiente con indicar que se quiere hacer
sobre el método CalculoComplejo de la instancia hpm.

System. Object
Ahora que sabemos lo que es la herencia es el momento apropiado para
explicar que en .NET todos los tipos que se definan heredan implícitamente
de la clase System.Object predefinida en la BCL, por lo que dispondrán de
todos los miembros de ésta. Por esta razón se dice que System.Object es
la raíz de la jerarquía de objetos de .NET.

A continuación vamos a explicar cuáles son estos métodos comunes a todos


los objetos:

 public virtual bool Equals(object o): Se usa para comparar el objeto


sobre el que se aplica con cualquier otro que se le pase como
parámetro. Devuelve true si ambos objetos son iguales y false en
caso contrario.

La implementación que por defecto se ha dado a este método consiste


en usar igualdad por referencia para los tipos por referencia e
igualdad por valor para los tipos por valor. Es decir, si los
objetos a comparar son de tipos por referencia sólo se devuelve true

58
Tópicos Selectos De Programación Actividades 2009
si ambos objetos apuntan a la misma referencia en memoria dinámica,
y si los tipos a comparar son tipos por valor sólo se devuelve true si
todos los bits de ambos objetos son iguales, aunque se almacenen en
posiciones diferentes de memoria.

Como se ve, el método ha sido definido como virtual, lo que permite


que los programadores puedan redefinirlo para indicar cuándo ha de
considerarse que son iguales dos objetos de tipos definidos por ellos.
De hecho, muchos de los tipos incluidos en la BCL cuentan con
redefiniciones de este tipo, como es el caso de string, quien aún
siendo un tipo por referencia, sus objetos se consideran iguales si
apuntan a cadenas que sean iguales carácter a carácter (aunque
referencien a distintas direcciones de memoria dinámica)

El siguiente ejemplo muestra cómo hacer una redefinición de Equals()


de manera que aunque los objetos Persona sean de tipos por
referencia, se considere que dos Personas son iguales si tienen el
mismo NIF:

public override bool Equals(object o)

if (o==null)

return this==null;

else

return (o is Persona) && (this.NIF == ((Persona) o).NIF);

Hay que tener en cuenta que es conveniente que toda redefinición del
método Equals() que hagamos cumpla con una serie de propiedades
que muchos de los métodos incluidos en las distintas clases de la BCL
esperan que se cumplan. Estas propiedades son:

Reflexividad: Todo objeto ha de ser igual a sí mismo. Es decir,


x.Equals(x) siempre ha de devolver true.
59
Tópicos Selectos De Programación Actividades 2009
Simetría: Ha de dar igual el orden en que se haga la
comparación. Es decir, x.Equals(y) ha de devolver lo mismo que
y.Equals(x) .

Transitividad: Si dos objetos son iguales y uno de ellos es igual


a otro, entonces el primero también ha de ser igual a ese otro
objeto. Es decir, si x.Equals(y) e y.Equals(z) entonces x.Equals(z) .

Consistencia: Siempre que el método se aplique sobre los


mismos objetos ha de devolver el mismo resultado.

Tratamiento de objetos nulos: Si uno de los objetos


comparados es nulo (null), sólo se ha de devolver true si el otro
también lo es.

Hay que recalcar que el hecho de que redefinir Equals() no implica que
el operador de igualdad (==) quede también redefinido. Ello habría
que hacerlo de independientemente como se indica en el Tema 11:
Redefinición de operadores.

 public virtual int GetHashCode(): Devuelve un código de dispersión


(hash) que representa de forma numérica al objeto sobre el que el
método es aplicado. GetHashCode() suele usarse para trabajar con
tablas de dispersión, y se cumple que si dos objetos son iguales sus
códigos de dispersión serán iguales, mientras que si son distintos la
probabilidad de que sean iguales es ínfima.

En tanto que la búsqueda de objetos en tablas de dispersión no se


realiza únicamente usando la igualdad de objetos (método Equals())
sino usando también la igualdad de códigos de dispersión, suele ser
conveniente redefinir GetHashCode() siempre que se redefina
Equals() De hecho, si no se hace el compilador informa de la situación
con un mensaje de aviso.

 public virtual string ToString(): Devuelve una representación en


forma de cadena del objeto sobre el que se el método es aplicado, lo
que es muy útil para depurar aplicaciones ya que permite mostrar con
facilidad el estado de los objetos.

60
Tópicos Selectos De Programación Actividades 2009

Object (Referencia de C#)

El tipo object es un alias de Object en .NET Framework. En el sistema de tipos


unificado de C#, todos los tipos (tipos de valor y de referencia predefinidos y
definidos por el usuario) se heredan directa o indirectamente de Object. Las
variables de tipo object pueden recibir valores de cualquier tipo. Cuando una
variable de un tipo de valor se convierte en un objeto, se dice que se le ha
aplicado la conversión boxing. Cuando una variable de objeto de tipo se
convierte en un tipo de valor, se dice que se le ha aplicado la conversión
unboxing. Para obtener más información, vea Boxing y Unboxing.

Ejemplo

En el siguiente ejemplo se muestra cómo las variables de tipo object pueden


aceptar valores de cualquier tipo de datos y cómo pueden utilizar métodos de
Object procedentes de .NET Framework.

// keyword_object.cs
using System;
class SampleClass
{
public int i = 10;
}

class MainClass
{
static void Main()
{
object a;
a = 1; // an example of boxing
Console.WriteLine(a);
Console.WriteLine(a.GetType());
Console.WriteLine(a.ToString());

a = new SampleClass();
SampleClass classRef;
classRef = (SampleClass)a;
Console.WriteLine(classRef.i);
}
}

61
Tópicos Selectos De Programación Actividades 2009

Hilo de Ejecución

Un hilo de ejecución, en sistemas operativos, es una característica que permite


a una aplicación realizar varias tareas a la vez(concurrentemente). Los distintos
hilos de ejecución comparten una serie de recursos tales como el espacio de
memoria, los archivos abiertos, situación de autenticación, etc. Esta técnica
permite simplificar el diseño de una aplicación que debe llevar a cabo distintas
funciones simultáneamente.

Los hilos de ejecución que comparten los mismos recursos, sumados a estos
recursos, son en conjunto conocidos como un proceso. El hecho de que los
hilos de ejecución de un mismo proceso compartan los recursos hace que
cualquiera de estos hilos pueda modificar éstos. Cuando un hilo modifica un
dato en la memoria, los otros hilos acceden a ese dato modificado
inmediatamente.

Lo que es propio de cada hilo es el contador de programa, la pila de ejecución


y el estado de la CPU (incluyendo el valor de los registros).

El proceso sigue en ejecución mientras al menos uno de sus hilos de ejecución


siga activo. Cuando el proceso finaliza, todos sus hilos de ejecución también
han terminado. Asimismo en el momento en el que todos los hilos de ejecución
finalizan, el proceso no existe más y todos sus recursos son liberados.

Algunos lenguajes de programación tienen características de diseño


expresamente creadas para permitir a los programadores lidiar con hilos de
ejecución (como Java o Delphi). Otros (la mayoría) desconocen la existencia de
hilos de ejecución y éstos deben ser creados mediante llamadas de biblioteca
especiales que dependen del sistema operativo en el que estos lenguajes
están siendo utilizados (como es el caso del C y del C++).

Un ejemplo de la utilización de hilos es tener un hilo atento a la interfaz gráfica


(iconos, botones, ventanas), mientras otro hilo hace una larga operación
internamente. De esta manera el programa responde de manera más ágil a la
interacción con el usuario. También pueden ser utilizados por una aplicación
servidora para dar servicio a múltiples clientes.

62
Tópicos Selectos De Programación Actividades 2009

Funcionalidad de los hilos

Al igual que los procesos, los hilos poseen un estado de ejecución y pueden
sincronizarse entre ellos para evitar problemas de compartimiento de recursos.
Generalmente, cada hilo tiene una tarea especifica y determinada, como forma
de aumentar la eficiencia del uso del procesador.

 Informática
 Diseño Web
 Proyectos
 Ocio

Muchos lenguajes de programación permiten la creación de hilos o threads en


un programa. De forma resumida, los hilos son un mecanismo mediante el cual
podemos devidir una aplicación en diferentes partes que se pueden ejecutar de
forma paralela, existiendo mecanismos por los que pueden compartir
información.

C# ofrece un mecanismo muy sencillo de implementar hilos, basado en la


utilización de la clase Thread. El constructor de esta clase recibe como
parámetro el método o función que hay que ejecutar en paralelo. Este
parámetro se indica mediante la utilización de un delegado, que es el
mecanismo que, entre otras cosas, se utiliza en .NET para utilizar punteros a
funciones de forma segura. La firma del delegado no incluye ningún parámetro,
por lo que únicamente es posible crear hilos de forma directa sobre métodos y
funciones que no requieran parámetros de entrada ni de salida. En los
siguientes ejemplos muestro un caso sencillo de creación de un hilo y otro en el
que explico una forma de poder crear un hilo con entrada y salida de
parámetros.

En el siguiente ejemplo se dispone de una clase con dos métodos que


muestran mensajes por pantalla. El objetivo es crear dos hilos, uno para cada
uno de los métodos y ejecutarlos de forma paralela, de forma que podamos ver
como resultado cómo se van intercalando los mensajes escritos por cada
método.

using System;
using System.IO;
using System.Threading;

public class Mensajes{


public void Mostrar1()
{
for(int i=0;i<10;i++){
Console.WriteLine("Escribiendo desde ==> 1″);
63
Tópicos Selectos De Programación Actividades 2009
Thread.Sleep(1000);
}
}

public void Mostrar2()


{
for(int i=0;i<10;i++){
Console.WriteLine("Escribiendo desde ==> 2″);
Thread.Sleep(1000);
}
}
}

public class Ejemplo{

public static void Main()


{
Mensajes msg = new Mensajes();

Thread th1 = new Thread(new


ThreadStart(msg.Mostrar1));
Thread th2 = new Thread(new
ThreadStart(msg.Mostrar2));

th1.Start();
th2.Start();

th1.Join();
th2.Join();
}

La creación de cada hilo se realiza mediante las líneas Thread th1 = new
Thread(new ThreadStart(msg.Mostrar1));. Esta línea indica que se crea una instancia
de la clase Thread, con nombre th1, a partir de un delegado de la clase
ThreadStart, que apunta al método Mostrar1 del objeto msg creado
anteriormente.

Una vez creados los dos hilos hay que activarlos, para lo que se llama al
método Start de cada uno de ellos. Tras este punto cada hilo se ejecuta en
paralelo entre si, y con el programa principal, por lo que utilizamos el método
Join de ambos hilos para esperar a que terminen los hilos antes de finalizar el
programa.

El delegado ThreadStart no acepta parámetros de entrada ni de salida, por lo


que si queremos crear un hilo sobre un método que los necesite, hay que
utilizar algún mecanismo auxiliar. Una posible forma de conseguir esto es crear
una nueva clase con los parámetros necesarios en la entrada y con un nuevo
método sin parámetros que llame al método que queremos hacer paralelo,

64
Tópicos Selectos De Programación Actividades 2009
enviándole estos parámetros. A partir de aquí tendríamos que crear una
instancia de dicha clase con los parámetros que queremos enviar al método
original, y hacer que el hilo se ejecutase sobre el nuevo método de la clase. En
el caso de que quisiéramos obtener el resultado de la ejecución, deberíamos
crear una función que acepte como parámetro de entrada el tipo del valor
devuelto por el método original, y hacer que la nueva clase creada disponga
también de un delegado que indique la función a la que llamar tras la ejecución.

Como esto puede parecer un poco lioso, vamos a ver otro ejemplo. En esta
ocasión disponemos de una clase de funciones matemáticas y queremos
llamar de forma paralela a una de ellas. Este método acepta un valor entero en
la entrada y devuelve otro entero.

using System;
using System.Threading;
using System.IO;

public class EjemploMates{

public static int CalculoComplejo(int n)


{
// sumo uno y espero 5 segundos
n = n+1;
Thread.Sleep(5000);
return n;
}

public class HiloParaMates{


protected int n;
protected MatesCallback callback = null;
public HiloParaMates(int n, MatesCallback callback){
this.n = n;
this.callback = callback;
}
public void CalculoComplejo()
{
int result = EjemploMates.CalculoComplejo(n);
if(callback != null)
callback(result);
}
}

// creo un delegado con la firma necesaria para capturar


// el valor devuelto por el método CalculoComplejo
public delegate void MatesCallback(int n);

public class Ejemplo{

65
Tópicos Selectos De Programación Actividades 2009
public static void Main()
{
HiloParaMates hpm = new HiloParaMates(1000, new
MatesCallback(ResultCallback));

Thread th = new Thread(new


ThreadStart(hpm.CalculoComplejo));

th.Start();
th.Join();

public static void ResultCallback(int n)


{
Console.WriteLine("Resultado de la operación: "+n);
}
}

En el anterior código la clase HiloParaMates es la que nos permite encapsular


la llamada al método EjemploMates.Calcular. Este método requiere un
parámetro de tipo entero, por lo que la clase requiere este parámetro en su
constructor. Además se requiere en el constructor otro parámetro más, un
delegado MatesCallback, que acepta un entero en la entrada. La idea es que
tras realizar el cálculo se llame al método que se indique proporcionándole el
resultado.

Para hacer funcionar todo esto, en Main se crea una instancia de la clase
HiloParaMates indicándole que queremos utilizar el valor numérico 1000 y que
se llame al método (estático) ResultCallback cuando se obtenga el resultado.
Para crear el hilo es suficiente con indicar que se quiere hacer sobre el método
CalculoComplejo de la instancia hpm.

Set (Referencia de C#)

Define un método de descriptor de acceso en una propiedad o indizador que


estableció el valor de la propiedad o el elemento del indizador. Vea
Propiedades e Indizadores para obtener más información.

Éste es un ejemplo de un descriptor de acceso set para una propiedad


denominada Seconds:

class TimePeriod
{
private double _seconds;
public double Seconds

66
Tópicos Selectos De Programación Actividades 2009
{
get { return _seconds; }
set { _seconds = value; }
}
}
Get (Referencia de C#)

Define un método de descriptor de acceso en una propiedad o indizador que


recupera el valor de la propiedad o el elemento del indizador. Vea Propiedades
(Guía de programación de C#) e Indizadores (Guía de programación de C#)
para obtener más información.

Éste es un ejemplo de un descriptor de acceso get para una propiedad


denominada Seconds:

class TimePeriod
{
private double _seconds;
public double Seconds
{
get { return _seconds; }
set { _seconds = value; }
}
}

67
Tópicos Selectos De Programación Actividades 2009
TEMAS:

VECTOR
LISTAS
PILAS
WX WIDGETS
CIN
COUT
TEMPLATES

C++ vector estándar


La estructura de la clase vector está pensada para operar con arreglos unidimencionales
de datos, los elementos de un vector pueden ser manipulados de la misma manera en
que se hace con las estructuras de arreglos (arrays) tradicionales en C, C++; es decir, los
componentes de un vector pueden ser referenciados a travez de un índice numérico, de
la misma manera que en un arreglo cualquiera. Por ejemplo, si A es un objeto de vector,
entonces la instrucción: A[0]; se refiere al componente 0 (primer elemento) de A. El
resultado de todo esto es que usted puede navegar o iterar a travez de los componentes
de una lista haciendo uso de índices, o si lo prefiere a travez de punteros iteradores. Si
usted desea ver una lista completa de los métodos asociados a la clase vector siga éste
enlace ( Tabla de métodos ), pero recuerde que no todos ellos serán cubiertos aquí.

Para comenzar, vamos a presentar un ejemplo sencillo, el cual consistirá en crear un


vector de números de punto flotante. Al vector creado le agregaremos una serie de
valores los cuales posteriormente serán sumados y desplegados en la pantalla del
monitor. Para nuestro ejemplo vamos a emplear los métodos push_back (para agregar
los números), size (para obtener el número de componentes en el vector), e iteraremos
por medio de índices numéricos. Veamos.

// Demostracion del uso de un vector


// probado en: Dev-C++ 4.9.9.2

#include <cstdlib>
#include <iostream>
#include <vector>

using namespace std;

int main(int argc, char *argv[])


{
char buffer[80];
double suma;

68
Tópicos Selectos De Programación Actividades 2009
vector<double> v;

v.push_back(999.25);
v.push_back(888.50);
v.push_back(777.25);

suma = 0;
for(int i = 0; i < v.size(); i++)
{
suma += v[i];
sprintf(buffer, "%10.2f", v[i]);
cout << buffer << endl;
}

cout << "----------" << endl;


sprintf(buffer, "%10.2f", suma);
cout << buffer << endl;

cin.get();
return EXIT_SUCCESS;
}

De acuerdo con la referencia de ayuda de Dev-C++ es más seguro emplear el método


at() en lugar el eperador [] para leer o escribir componentes en un vector, ya que at() no
permite índices fuera del vector, y el operador [] sí. Por ejemplo, si V es un vector cuyo
número de componentes es de 3, entonces la instrucción V[5]; es sumamente peligrosa
ya que el índice 5 está fuera del rango (0 a 2 ) de los componentes de V; por otro lado,
la instrucción V.at(5); también está fuera de rango, salvo que at() en lugar de leer o
escribir el componente referenciado lanzará (throw) un error de excepción, de tal
manera que en el programa se pueda controlar la condición de error por medio de un
catch. El método at() actua de manera parecida al operador [], y para ver un ejemplo de
su uso compile y ejecute el siguiente programa.

// Demostracion del uso del método at()


// probado en: Dev-C++ 4.9.9.2

#include <cstdlib>
#include <iostream>
#include <vector>

using namespace std;

int main(int argc, char *argv[])


{
vector<char> v;

// llenamos el vector v con valores desde la 'A' hasta


la 'Z'
for (int x = 'A'; x <= 'Z'; x++) v.push_back(x);

69
Tópicos Selectos De Programación Actividades 2009
// despliegue de los elementos del vector v
// mediante el operador [].
for(int i = 0; i < v.size(); i++)
cout << v[i] << " ";

// despliegue de los elementos del vector v


// mediante el método at().
for(int i = 0; i < v.size(); i++)
cout << v.at(i) << " ";

cout << endl;

cin.get();
return EXIT_SUCCESS;
}

Colas de doble fin ( deque )

Las Colas de doble fin son como los vectores, excepto que en éstas la inserción y
borrado de elementos es más rapida, además de permitir insertar y borrar elementos al
principio así como al final del contenedor. La mayoría de los métodos aplicables a la
clase vector son aplicables a la clase deque, pero la clase deque posee ademas los
métodos push_front y pop_front para insertar y borrar elementos al principio. En
orden de ver un ejemplo modificaremos el programa anterior, en el mismo usaremos
una clase deque.

// Demostracion de la clase deque.


// probado en: Dev-C++ 4.9.9.2

#include <cstdlib>
#include <iostream>
#include <deque>

using namespace std;

int main(int argc, char *argv[])


{
deque<char> v;
int x;

// Metemos en la cola v valores desde la 'A' hasta la


'Z'
// mediante el método push_back.
for (x = 'A'; x <= 'Z'; x++) v.push_back(x);

// Metemos en la cola v valores desde la '0' hasta la


'9'

70
Tópicos Selectos De Programación Actividades 2009
// mediante el método push_front.
for (x = 0; x <= 9; x++) v.push_front(x);

// despliegue de los elementos de la cola


// mediante el operador [].
for(int i = 0; i < v.size(); i++)
cout << v[i] << " ";

// despliegue de los elementos del vector v


// mediante el método at().
for(int i = 0; i < v.size(); i++)
cout << v.at(i) << " ";

cout << endl;

cin.get();
return EXIT_SUCCESS;

Tabla de métodos

Métodos de la clase vector


assign asignar elementos al vector
at regresa el componente de una posición específica
back regresa una referencia a el último componente del vector
begin regresa un iterator al principio del vector
regresa el número de elementos que pueden ser
capacity
contenidos por el vector
clear remueve todos los componentes del vector
empty true si el vector está vacio
end regresa un iterator al final del vector
erase remueve componentes del vector
front regresa una referencia al primer componente del vector
insert insertar componentes en el vector
regresa el número máximo de elementos soportados por
max_size
el vector
pop_back remueve el último componente del vector
push_back agrega un componente al final del vector

71
Tópicos Selectos De Programación Actividades 2009

rbegin regresa un reverse_iterator hacia el final del vector


rend regresa un reverse_iterator hacia el inicio del vector
reserve establece la capacidad mínima del vector
resize cambia el tamaño del vector
size regresa el número de componentes en el vector
swap intercambia el contenido de un vector con el de otro

Destructores y constructores
Cuando vimos clases, hemos visto que toda clase tiene una función especial
llamada constructor que posee la particularidad de tener el mismo nombre
que la clase.

Esta función se ejecuta cada vez que se crea un objeto de la clase. De


parecidas carácteristicas existe una función, llamada destructor que se
ejecuta cada vez que el objeto se destruye.

Un objeto se destruye cuando se termina el programa, cuando se sale de una


función (o bloque) que había creado al objeto, o bien cuando indicamos con la
instrucción delete, la liberación de la memoria que habíamos reservado
dinámicamente para algún objeto.

En todos estos casos se ejecuta el destructor del objeto. Este también lleva el
nombre de la clase precedido por el símbolo ~.

Hasta el momento no nos hizo falta definir nuestros propios destructores, pero
como veremos más adelante resulta muy útil y necesario cuando los
programas administran memoria dinámica y no son clases tan triviales como
las que vimos.

En el programa siguiente, crearemos la clase Prueba y le definiremos


solamente un constructor y un destructor.

72
Tópicos Selectos De Programación Actividades 2009
También poseera un dato miembro, que lo utilizaremos para diferenciar los
diferentes objetos (objeto 0, objeto 1, etc).

El constructor y el destructor, sólo poseeran una instrucción cout, que


mostrará cuando se ejecuta uno u otro y además dará el número de objeto
para que podamos diferenciarlos.

Presten especial atención en los dos objetos creados dentro del bloque (el 1 y
el 2). Se crea primero el objeto 1 y luego el objeto 2. Cuando se sale del
bloque, los destructores se ejecutan en el orden inverso al que fueron
creados, es decir primero el del objeto 2 y luego el del objeto 1.

Aquí va a el código:

#include <iostream>
using std::cout;

using std::endl;
#include <new>

class Prueba
{

public:
Prueba(int numero = 0); //constructor.

~Prueba(); //destructor
private:
int x;

};

//definiciones de las funciones miembro


Prueba::Prueba(int numero)//constructor.

{
x = numero;
cout << "Se ejecuta el constructor del objeto nro: " <<
(*this).x << endl;

}
Prueba::~Prueba()
{
cout << "Se ejecuta el DESTRUCTOR del objeto nro: " <<
(*this).x << endl;

int main()

73
Tópicos Selectos De Programación Actividades 2009
{
cout << "***Entramos a main" << endl;

Prueba objeto(0);
{
cout << "***Entramos al bloque" << endl;

Prueba objetoA(1);
Prueba objetoB(2);

cout << "***Salimos del bloque" << endl;


}

Prueba *ptr; //creamos un puntero.

ptr = new Prueba(3);//le asignamos un objeto


dinamicamente, con argumento 3.
Prueba *ptr2;

= new Prueba(4);//segundo puntero.


ptr2
//AHORA LIBERAMOS LA MEMORIA DINAMICA QUE HABIAMOS
SOLICITADO, USANDO DELETE.

cout << "--Borramos ptr2" << endl;


delete ptr2;
cout << "--Borramos ptr" << endl;

delete ptr;
cout << "***Ahora estamos por salir de main(), al salir se
ejecutara el destructor" << endl << "del objeto 0 creado al
principio de main()" << endl;

return 0;
}

Para compilar:

g++ destructores.cpp -o destructores.out

Constructor
void __construct ( [mixed args [, ...]] )

PHP 5 permite a los desarrolladores declarar métodos constructores para las clases. Las
clases que tienen un método constructor llaman a este método cada vez que se crea un

74
Tópicos Selectos De Programación Actividades 2009
nuevo objeto, para cualquier inicialización que el objeto puede necesitar antes de ser
usado.

Nota: Los constructores padres no son llamados implicitamente si la clase hijo define
un constructor. Para poder ejecutar el constructor de la clase padre, se debe hacer una
llamada a parent::__construct() dentro del constructor de la clase hijo.

Ejemplo 19-6. Usando constructores unificados

<?php
class BaseClass {
function __construct() {
print "In BaseClass constructor\n";
}
}

class SubClass extends BaseClass {


function __construct() {
parent::__construct();
print "In SubClass constructor\n";
}
}

$obj = new BaseClass();


$obj = new SubClass();
?>

Para tener compatibilidad con versiones anteriores, si PHP 5 no encuentra una función
__construct() para una clase dada, buscará la forma de la función del constructor que se
usaba anteriormente por el nombre de la clase. Efectivamente, esto significa que el
único caso que puede tener compatibilidad es si la clase tiene un método llamado
__construct() el cual fue usado para semánticas diferentes.

Destructores
void __destruct ( void )

PHP 5 introduce un concepto de destructor similar a aquellos de otros lenguajes de


programación orientada a objetos, tal como C++. El método destructor será llamado tan
pronto como todas las referencias a un objeto en particular sean removidas o cuando el
objeto sea explícitamente destruido.

Ejemplo 19-7. Ejemplo de Destructor

<?php

75
Tópicos Selectos De Programación Actividades 2009

class MyDestructableClass {
function __construct() {
print "In constructor\n";
$this->name = "MyDestructableClass";
}

function __destruct() {
print "Destroying " . $this->name . "\n";
}
}

$obj = new MyDestructableClass();


?>

Como los constructores, los destructores de la clase padre no serán llamados


explícitamente por el compilador. Para ejecutar un destructor padre, se debe tener una
llamada explícita a parent::__destruct() en el cuerpo del destructor.

STL contenedores clase Introducción

Antes de empezar a cavar hondo en STL, es obligatorio que aprendemos acerca de las
clases de contenedores.

Una clase de contenedor se define como una clase que te da el poder para almacenar
cualquier tipo de datos. Existen dos tipos de clases de contenedores disponibles en
STL en C + +, es decir, "Clases de simple contenedor" y "Clases de contenedores
asociativos". Un asociados clase asociativa Container una clave para cada objeto de
reducir al mínimo el tiempo de acceso promedio.

Clases simples contenedores

 vector <>
 listas de <>
 pila <>
 cola <>
 deque <>

Asociativa clases de contenedores

 Mapa <>
 se <>
 Multimap <>
 multiconjunto <>

76
Tópicos Selectos De Programación Actividades 2009
Este artículo discutirá acerca de las clases simples contenedores en detalle. Esto le
mostrará cómo utilizar los diferentes métodos de estas clases para lograr su objetivo.
Vamos a empezar con vector <>

vector <> El Conjunto abierto

Constructor de la clase Vector

Hay 3 constructores sobrecargados en la clase vector de exclusión de la


predeterminada. Vamos a discutir acerca de ellos uno por uno. Usted puede crear un
objeto vectorial, con un tamaño predefinido y los valores para ellos. Aquí es cómo
usted puede hacer eso. Suponga que desea crear un vector <int> objeto cuya capacidad
inicial será de 10 y todos los valores se establece en 2. Entonces usted puede crear ese
objeto utilizando una de las versiones sobrecargadas del constructor de vector

vector <int> nums (10,2);

Si sólo desea especificar la capacidad, pero no queremos asignar valores para ellos
entonces puede usar el otro constructor que acepta un entero para establecer la
capacidad del vector, como

vector <int> nums (100);

El último constructor le permite inicializar el vector con los datos de otros vectores o
matrices o de otras colecciones. Supongamos que desea copiar un vector a otro a
continuación, este constructor demuestra ser muy útil. Aquí está el fragmento de
código.

vector <int> nums; for (int i = 1; i <5; i + +) nums.push_back (i); vector <int> códigos
(nums);

He aquí los códigos es un vector. Hemos copiado el contenido de nums en los códigos
con el constructor.

Los métodos de la clase de vectores

 _Destroy ()
 _Eq ()
 _Lt ()
 _Ucopy ()
 _Ufill ()
 assign ()
 at ()
 begin ()
 back ()
 capacidad ()
 clear ()
 empty ()
77
Tópicos Selectos De Programación Actividades 2009
 final ()
 erase ()
 front ()
 get_allocator ()
 max_size ()
 insert ()
 operador =
 operador []
 pop_back ()
 push_back ()
 rbegin ()
 rend ()
 reserva ()
 cambiar el tamaño ()
 size ()
 intercambio ()
 ~ Vector ()

Aquí vamos a discutir acerca de cada método, le mostrará lo que hace y luego, al final
vamos a dar ejemplo en el que usted puede usar estos métodos a la vez ....

Los métodos que empiezan con un guión bajo protección de métodos y no se puede
acceder usando el objeto de la clase. Estos métodos son utilizados por otro método
internamente.

assign () y size ()

Este método se utiliza para asignar un tamaño inicial del vector. Este método tiene 2
versiones sobrecargadas. Aquí hemos utilizado el primer método sobrecargado. Este
método toma un argumento entero para inicializar el tamaño del vector.

# include <iostream>
# include <vector>
# include <conio.h>

using namespace std;

int main ()
(
Vector <char> códigos;
codes.assign (10); / / Asignar el tamaño a 10.
cout <<codes.size ();// imprime el tamaño del vector
getch ();

78
Tópicos Selectos De Programación Actividades 2009
return 0;
)

El partido sobrecarga que asuma el próximo 2 de puntero como argumentos. Aquí


hay un código de ejemplo

# include <iostream>
# include <vector>
# include <conio.h>
using namespace std;

int main ()
(
char a [3] = ( 'a', 'b', 'c');
Vector <char> órdenes;
órdenes. asignar (a, a +3);
cout <<orders.size ();
getch ();
return 0;
)

Aquí el puntero utilizado no son sino el nombre de la matriz. Este concepto se debe al
nombre de C. Básicamente matriz es un puntero a la matriz.

at ()

No importa cuán sofisticada herramienta que conseguimos, el acceso a la gama C de


estilo antiguo utilizando el índice es muy popular. Esta cosa se tuvo en cuenta cuando
se diseñó STL. Como se comporta como vector de una matriz abierta, la gente espera
que para mostrar otros comportamientos de la matriz. en () es un método que toma
un entero como argumento y devuelve el valor en ese lugar. Así, en una forma que
simula la edad de indexación de arrays de edad. Aquí está un fragmento de código.

# include <iostream>
# include <vector>
# include <conio.h>

using namespace std;

int main ()

79
Tópicos Selectos De Programación Actividades 2009
(
vector <int> códigos;
for (int i = 0; i <10; i + +)
codes.push_back (i);
cout <<codes.at (9) <<endl;
getch ();

return 0;
)

La salida de este código será de 9.

begin (), end ()

En STL en los contenedores, los elementos son accesibles usando iteradores. Los
iteradores son algo así como punteros. begin () y final () devuelve el iterador al
principio y al final del contenedor. He aquí una cosa hay que señalar final () apunta a
una ubicación que es uno después del final físico del contenedor. Estos dos métodos
son los más utilizados en STL, porque para atravesar un contenedor, es necesario crear
un iterador. Y entonces para inicializar su valor al inicio del contenedor. Aquí está el
código para recorrer un vector <>. Este código de hacer uso de comenzar () y final ()
métodos.

# include <iostream>
# include <vector>
using namespace std;

int main ()
(
vector <int> números;
for (int i = 0; i <10; i + +)
numbers.push_back (i);
/ / Assiging al principio
<int> vector:: iterator k = Numbers.begin ();
/ / Tenga en cuenta que K es inferior al que
/ / numbers.end (), porque los puntos finales () en algún lugar que está más allá del final
físico.
for (; k <numbers.end (); k + +)
cout <<* k <<endl;
return 0;
)

frente (), back ()

80
Tópicos Selectos De Programación Actividades 2009
() frente método devuelve el elemento en la parte delantera del vector. Volver ()
devuelve el elemento en la parte posterior del vector. Aquí está el código para
comprender mejor sus operaciones.

# include <iostream>
# include <vector>
using namespace std;

int main ()
(
vector <int> m;
for (int i = 0; i <10; i + +)
m.push_back (i);
cout <<m.front () <<endl;
cout <<m.back () <<endl;
return 0;
)

La salida de este programa es


0
9

() capacidad, el tamaño ()

capacidad () devuelve el número de elementos del vector puede tener inicialmente


asignado. Para un vector aunque no es muy importante el método. Por otra parte, el
tamaño de método () devuelve el número de elementos que están actualmente en el
vector. Aquí está el código que le ayudará a entender la diferencia entre la capacidad
() y tamaño ().

# include <iostream>
# include <vector>
using namespace std;

int main ()
(
vector <int> m (100);
cout <<m.capacity () <<endl;
for (int i = 0; i <10; i + +)
m.push_back (i);
cout <<m.size () <<endl;
return 0;
)

81
Tópicos Selectos De Programación Actividades 2009

ITERADORES
Nota: el diseño de la STL permite que cualquier función genérica que acepte
un iterador funcione igualmente bien aceptando un puntero ordinario.

Nota: los iteradores no solo sirven para señalar elementos dentro de los
contenedores de la STL, también se utilizan para señalar elementos dentro de
otras estructuras: flujos ("Streams") y bufers de flujo ("Buffers streams") [4].
Además se han definido de modo que cuando los elementos de matriz están
definidos mediante subíndices son también iteradores. La consecuencia es que
ciertos algoritmos pueden aplicarse también sobre las matrices (recuerde las
matrices son un tipo de estructura de datos, y que el operador elemento de
matriz [] se define como la indirección de un puntero

La cosa funciona más o menos según el siguiente esquema (observe su


paralelismo con los punteros):

vector<int> cInt; // contenedor tipo vector para enteros

... // introducimos algunos enteros en cInt

std::vector<int>::iterator iT1; // iterador-a-vector-de-int

...

iT1 = cInt.begin(); // iT1 señala al primer miembro de cInt

...

std::cout << *iT1; // muestra el primer miembro de cInt

...
82
Tópicos Selectos De Programación Actividades 2009
++iT1; // desplaza iT1 al segundo miembro

std::cout << *iT1; // muestra el segundo miembro de cInt

§2 La clase iterator

La sentencia anterior, donde se declara un iterador iT1 de la clase


vector<int> es fundamental para entender la naturaleza de los iteradores

std::vector<int>::iterator iT1;

En efecto: esta sentencia intancia un objeto iT1 de la clase iterator que


pertenece al espacio de una instanciación concreta, vector<int>, de una
clase genérica vector<T>. Esto significa que la clase iterator para vectores-
de-int está definida en el ámbito de la clase genérica vector<T>. Es decir, la
definición de la clase vector es algo como:

template <class T, ...> vector {

public:

class iterator;

};

A su vez esta clase iterator debe dispone de su propia definición en algún


punto de la cabecera <vector>:

class vector<T>::iterator {

/* definición dependiente de la implementación */

};

Esta es la razón por la que coloquialmente se dice que un contenedor puede


generar un iterador adecuado. Como puede verse, existen tantas clases
iterator como contenedores distintos; todas ellas son distintas e
independientes. Aunque comparten el mismo nombre y están definidas en
subespacio distintos de std (recuerde que las clases constituyen ámbitos en sí
mismas).

typedef implementation defined iterator;


typedef implementation defined const_iterator;
typedef implementation defined size_type;
typedef implementation defined difference_type;
83
Tópicos Selectos De Programación Actividades 2009

typedef std::reverse_iterator<iterator> reverse_iterator;


typedef std::reverse_iterator<const_iterator> const_reverse_iterator;

§2 Características

Existen diversas características principales que distinguen unos iteradores de


otros. Podemos resumirlas como sigue:

 Capacidad de modificar los datos subyacentes. En este sentido pueden


ser de solo lectura; solo escritura, o lectura/escritura.
 Tipo de desplazamiento que puede realizarse con ellos para recorrer el
contenedor. Puede ser de avance secuencial; avance y retroceso
secuencial, o acceso aleatorio.
 Otras características adicionales. Por ejemplo, la posibilidad de ser
utilizados por algoritmos que permiten insertar o borrar elementos del
contenedor asociado.

La tabla siguiente muestra los modos en que son generadas las diversas
categorías de iteradores por los contenedores STL.

Categoría Generado por

Iterador de entrada istream_iterator

Iterador de salida ostream_iterator


insert_iterator
front_insert_iterator
back_insert_iterator
Iterador bidireccional list
set y multiset
map y multimap
Iterador de acceso aleatorio Punteros ordinarios
vector
deque

84
Tópicos Selectos De Programación Actividades 2009

List
Lista STL
Una lista es un contenedor secuencial optimizado para las inserciones y eliminaciones
de los elementos de datos en ubicaciones arbitrarias dentro de la colección.

Sin embargo, una lista no proporciona acceso basado en el índice a los elementos de la
colección. Lista STL se implementa como una lista doblemente vinculada. Una lista
doblemente vinculada es una estructura de datos en el que cada elemento tiene un
vínculo en el siguiente elemento y un vínculo al elemento anterior. Se debe usar una
lista cuando el orden de los elementos dentro de la lista y eficientes arbitrarias
inserciones y eliminaciones requeridos por la aplicación.

Los siguientes son algunos de los principales métodos de clase de lista de STL. Estos no
incluyen los métodos relacionados con iteradores, que se describen en la siguiente
sección.

Método Descripción
List() Crea una lista vacía.
lista (size_type n) Crea una lista de n elementos inicializada a su valor
predeterminado.
lista (size_type n, T const y Crea una lista de n elementos inicializada al valor.
valor)
T & back(void) Devuelve una referencia para el último elemento en la lista.
T & front(void) Devuelve una referencia al primer elemento en la lista.
anular push_back(const T& Inserta un valor al final de la lista.
value)
anular push_front(const T& Inserta un valor al principio a la lista.
value)
anular pop_back(void) Elimina el último elemento de la lista.
anular pop_front(void) Elimina el primer elemento de la lista.
void quitar (const T & valor) Elimina todos los elementos que coinciden con el valor.
Comparación se realiza mediante el operador ==.
anular reverse(void) Invierte el orden de los elementos en la lista.

85
Tópicos Selectos De Programación Actividades 2009

Método Descripción
size_type size(void) Devuelve el número de entradas de la lista.
anular sort(void) Ordena las entradas contenidas en la lista utilizando el < operador.

Stack
La pila de <>clase Container
En este artículo trataremos sobre unSencillo clase de contenedorPila.

Esto representa una pila de su tipo de datos. Pila de <>se modela utilizando un deque
(O un doble terminó cola). Todos los operadores <, > < =, > =,! =, == están
sobrecargados de la clase de contenedor de pila. Esto significa que puede comprobar
dos pilas al igual que dos enteros o dos tipos de datos integrada. Por ejemplo, hay dos
pilas s1 y s2 y desea comparar, puede escribir

if(S1==S2) y así sucesivamente para otros operadores

Stack<>-Una sobre otra

Métodos de clase de pila

 Empty()
 get_allocator()
 pop()
 Push()
 Size()
 Top()
 operador ==
 operador < =
 operador > =
 operador! =
 operador <
 operador >

86
Tópicos Selectos De Programación Actividades 2009
Aquí vamos a hablar acerca de cada método, le mostrará lo que hace y, a continuación,
al final nos dará ejemplo donde se puede utilizar estos métodos together….

Empty() y size()

El método empty() se utiliza para comprobar si una pila está vacía o no. Este método
devuelve un valor bool. El valor es true que si la pila está vacía, de lo contrario
devolverá false valor. Aquí es un código que lo hará entender mejor cómo se utiliza
empty()...

Size() devuelve el número de elementos presentes en la pila.

# include <iostream>
# include <stack>
# include <conio.h>

utilizando el espacio de nombres std;

int main()
{
la pila de códigos de <char>;
codes.Push('a');
codes.Push('b');
cout << "tamaño de la pila is:"<<codes.size<<endl;
¿//Checking si la pila está vacía o no?
if(codes.Empty()==true)
cout << "La pila está vacía"; //prints el tamaño de la pila
getch();
devolver 0;
}

Se puede eliminar la "true" en la anterior cada bloque. Pero es recomendable escribir


true, ya que hace su intención más claro a otros programadores

El resultado de este programa será 2 ya que hay dos elementos en la pila.

Push() y top()

Push() se utiliza para insertar un elemento en la parte superior de pila. Sólo necesitamos
pasar el argumento para empujar en la parte superior de la pila. El tipo de devolución de
este método es nulo. Por lo que no se devuelve sólo los valores se insertan en la parte
superior de pila.

Top(), como sugiere el nombre utilizado para el elemento MRA(Most Recently Added)
de la pila que se encuentra en la parte superior de pop. El código siguiente pone algunos
entero en una pila de entero y, a continuación, muestra el elemento ARM.

87
Tópicos Selectos De Programación Actividades 2009
# include <iostream>
# include <stack>
# include <conio.h>

utilizando el espacio de nombres std;

int main()
{
la pila de códigos de <int>;
for(int i=0;i<10;i++)
//Pushing elementos en la parte superior de la pila.
codes.Push(i);
cout<<codes.Top() << endl; //Displaying el elemento de la parte superior
getch();
devolver 0;
}

El resultado de este código será 9.

LAS WXWIDGETS

son unas bibliotecas multiplataforma y libres, para el desarrollo de interfaces


gráficas programadas en lenguaje C++. Están publicadas bajo una licencia LGPL,
similar a la GPL con la excepción de que el código binario producido por el usuario
a partir de ellas, puede ser propietario, permitiendo desarrollar aplicaciones
empresariales sin coste.

88
Tópicos Selectos De Programación Actividades 2009
Las wxWidgets proporcionan una interfaz gráfica basada en las bibliotecas ya
existentes en el sistema (nativas), con lo que se integran de forma óptima y
resultan muy portables entre distintos sistemas operativos. Están disponibles para
Windows, MacOS, GTK+, Motif, OpenVMS y OS/2.

También pueden ser utilizadas desde otros lenguajes de programación, aparte del
C++: Java, Javascript, Perl, Python, Smalltalk, Ruby .

ImplementationAplicación

La biblioteca wxWidgets se implementa en C + +, con los enlaces disponibles para


muchos lenguajes de programación de uso común, entre ellos, Python (wxPython),
Erlang (wxErlang), Haskell (wxHaskell), Lua (wxLua), Perl (wxPerl), Ruby
(wxRuby) , Smalltalk (wxSqueak), Common Lisp (wxCL), Básico (wxBasic), C
(WXC), D (WXD), Euphoria (wxEuphoria). NET Framework (wx.NET), Java (wx4j) e
incluso JavaScript (wxJavaScript / GLUEScript). Para obtener una lista completa,
con enlaces a los sitios de los proyectos respectivos, ver las referencias externas al
final de este artículo. También hay PLT Scheme, que utiliza una rama de
incompatibilidad de wxWindows (versión 1), creada en 1995. El kit de
herramientas está totalmente integrada con la carrera de idiomas a tiempo (la
recolección de basura, la administración de recursos) a diferencia de en otros
idiomas, que se limitan a establecer una biblioteca de carácter vinculante.

El uso
Todos los usos de los wxWidgets se derivan de wxApp, y necesitan simplemente
eliminar a un solo miembro, wxApp:: OnInit, y crea una ventana. Mientras la
ventana esté abierta, el uso está también.

Definición
Un Frame
es una ventana cuyo tamaño y posición pueden (usualmente) ser cambiados
por el usuario.Esta usualmente tiene unas fronteras gruesas y un titulo de
barra, y puede contener opcionalmente una barra de menú, una barra de
herramientas y una barra de estado. Un Frame puede contener cualquier
ventana que no sea un diálogo. Un Frame va a tener una barra de estado, que
es creada por la función CreateStatusBar y una barra de herramientas que es
creada por la función CreateToolBar que manejara la ventana.

89
Tópicos Selectos De Programación Actividades 2009

Métodos
Estos son algunos métodos del Frame:

wxFrame::Centre Centra la ventana en la pantalla.


wxFrame::CreateStatusBar Crea una barra de estado en el fondo del Frame.
Recibe un entero que dividirá la barra de estado.
wxFrame::CreateToolBar Crea una barra de herramientas en la parte
superior o la izquierda del Frame.
wxFrame::GetMenuBar Regresa un indicador a la barra de menú, asociado
actualmente con el Frame.
wxFrame::Iconize Minimiza o maximiza.Recibe un parámetro verdadero o
falso.
wxFrame::IsFullScreen Retorna verdadero si el Frame el modo pantalla
completa.
wxFrame::Maximize Maximiza o restaura el Frame.
wxFrame::GetTitle Retorna la cadena que tiene el título del Frame.
wxFrame::GetToolBar Regresa un indicador a la barra de herramientas
asociado actualmente al Frame.
wxFrame::SetIcon Fija el icono para este Frame.
wxFrame::SetStatusText Fija el texto de la barra de estado y rediseña la barra
de estado.
wxFrame::SetTitle Fija el título del Frame.

Constructora
wxFrame( parent, id, const title, const wxPoin pos =
wxDefaultPosition, const size = 0, long style =
wxDEFAULT_FRAME_STYLE, name = "frame")

Parámetros de la constructora
Parent:
El padre de la ventana. Este puede ser NULL. si es non-NULL, el frame será
exhibido siempre encima de la ventana del padre en Windows.
Id: El identificador de la ventana. Puede tomar el valor de -1 para que se le
asigne un valor predeterinado.
Title: El subtitulo que se exhibirá en la barra del título del Frame.
Pos: La posición de la ventana. Un valor de (-1,-1) indica una posición del
defecto, elegida por el sistema o los wxWidgets del windowing, dependiendo
de la plataforma.
Size: El tamaño de la ventana. Un valor de (-1,-1) indica un tamaño
predeterminado, elegido por el sistema, dependiendo de la ventana.
Style: El estilo de la ventana.

90
Tópicos Selectos De Programación Actividades 2009
Name: El nombre de la ventana. Este parámetro se utiliza para asociar un
nombre a la ventana.

wxTextCtrl
Un control de texto permite que el texto que se mostrará y editado. Puede ser de
una sola línea o de varias líneas.

Derivado de

streambuf
wxControl
wxWindow
wxEvtHandler
wxObject

Incluir archivos

<wx/textctrl.h>

wxTextCtrl:: wxTextCtrl

wxTextCtrl ()

Default constructor.

wxTextCtrl (wxWindow padre *, wxWindowID id, wxString const & value = "",
wxPoint const & POS = wxDefaultPosition, wxSize const & size = wxDefaultSize,
long style = 0, wxValidator const & validador = wxDefaultValidator, wxString const
& name = wxTextCtrlNameStr)

Constructor, crear y mostrar un control de texto.

wxTextCtrl:: ~ wxTextCtrl

~ wxTextCtrl ()

Destructor, destruyendo el control de texto.


91
Tópicos Selectos De Programación Actividades 2009

wxTextCtrl:: AppendText

void AppendText (wxString const y texto)

Añade el texto al final del control de texto.

wxTextCtrl:: CanCopy

virtual bool CanCopy ()

Devuelve true si la selección se puede copiar en el portapapeles.

wxTextCtrl:: CanCut

virtual bool CanCut ()

Devuelve true si la selección puede ser cortado en el portapapeles.

wxTextCtrl:: CanPaste

CanPaste bool virtual ()

Devuelve true si el contenido del portapapeles puede pegarse en el control de texto.


En algunas plataformas (Motif, GTK) Esta es una aproximación y devuelve true si el
control se puede editar, false en caso contrario.

wxTextCtrl:: CanRedo

virtual bool CanRedo ()

Devuelve true si hay un servicio disponible y rehacer la última operación se puede


rehacer.

wxTextCtrl:: CanUndo

virtual bool CanUndo ()

92
Tópicos Selectos De Programación Actividades 2009
Devuelve true si existe la posibilidad de deshacer disponible y la última operación
se puede deshacer.

wxTextCtrl:: Clear

virtual void Clear ()

Borra el texto en el control.

Tenga en cuenta que esta función generará un evento de


wxEVT_COMMAND_TEXT_UPDATED.

wxTextCtrl:: Copiar

virtual void Copy ()

Copia el texto seleccionado al portapapeles con Motif y MS Windows.

wxTextCtrl:: Create

bool Crear (* wxWindow padre, wxWindowID id, wxString const & value = "",
wxPoint const & POS = wxDefaultPosition, wxSize const & size = wxDefaultSize,
long style = 0, wxValidator const & validador = wxDefaultValidator, wxString const
& name = wxTextCtrlNameStr )

Crea el control de texto para la construcción de dos pasos. Las clases derivadas
deben llamar o sustituir esta función

wxTextCtrl:: Corte

virtual void Cut ()

Copia el texto seleccionado en el portapapeles y elimina la selección.

wxTextCtrl:: DiscardEdits

DiscardEdits void ()

93
Tópicos Selectos De Programación Actividades 2009
Restablece el indicador interno "modificado" como si las ediciones actuales se
habían salvado.

wxTextCtrl:: EmulateKeyPress

EmulateKeyPress bool (wxKeyEvent const & event)

Esto inserta funciones en el control del personaje que se han insertado si el


acontecimiento clave dada se han producido en el control de texto. El objeto de
evento debe ser el mismo que el que pasó a EVT_KEY_DOWN controlador
anteriormente por wxWidgets.

Tenga en cuenta que esta función actualmente no funciona correctamente para


todas las claves en cualquier plataforma, pero los RSU.

Valor devuelto

true si el evento dio lugar a un cambio en el control, false en caso contrario.

wxTextCtrl:: GetDefaultStyle

wxTextAttr const GetDefaultStyle () const

Devuelve el estilo utilizado actualmente para el nuevo texto.

Compatibilidad

Aplicada en wxMSW / wxGTK comenzando con wxWidgets 2.3.2.

wxTextCtrl:: operator <<

wxTextCtrl & operator <<(wxString const & s)

wxTextCtrl & operator <<(int i)

wxTextCtrl & operator <<(long i)

wxTextCtrl & operator <<(float f)

wxTextCtrl & operator <<(double d)

94
Tópicos Selectos De Programación Actividades 2009
wxTextCtrl & operator <<(char c)

wxControl
Esta es la clase base para un control o widget''.

Un control es generalmente una pequeña ventana que los procesos de entrada de


usuario y / o mostrar uno o más tema de los datos.

Derivado de

wxWindow
wxEvtHandler
wxObject

Incluir archivos

<wx/control.h>

Ver también

wxValidator

Miembros

wxControl:: Command
wxControl:: getLabel
wxControl:: SetLabel

wxControl:: Command

void Comando (wxCommandEvent & event)

Simula el efecto de que el usuario se da la orden al tema. Ver wxCommandEvent.

95
Tópicos Selectos De Programación Actividades 2009
wxControl:: getLabel

wxString & getLabel ()

Devuelve el texto del control.

wxControl:: SetLabel

void setLabel (wxString const & etiqueta)

wxButton

Un botón es un control que contiene una cadena de texto, y es uno de los


elementos más comunes de una GUI. Puede ser colocado en un cuadro de diálogo o
panel, o de hecho, casi cualquier otra ventana.

Derivado de

wxControl
wxWindow
wxEvtHandler
wxObject

Incluir archivos

<wx/button.h>

Estilos de ventanas

wxBU_LEFT Izquierda-justifica la etiqueta. Windows y


GTK + solamente.

wxBU_TOP Alinea la etiqueta a la parte superior del


botón. Windows y GTK + solamente.

wxBU_RIGHT Haga justifica la etiqueta de mapa de bits.


Windows y GTK + solamente.

wxBU_BOTTOM Alinea la etiqueta en la parte inferior del


botón. Windows y GTK + solamente.

96
Tópicos Selectos De Programación Actividades 2009

wxBU_EXACTFIT Crea el botón tan pequeñas como sea


posible en lugar de hacerlo del tamaño
estándar (que es el comportamiento por
defecto).

wxNO_BORDER Crea un botón plano. Windows y GTK +


solamente.

wxButton:: wxButton

wxButton ()
Default constructor.

(wxButton wxWindow padre *, wxWindowID id, wxString const & label =


wxEmptyString, wxPoint const & POS = wxDefaultPosition, wxSize const & size =
wxDefaultSize, long style = 0, wxValidator const & validador = wxDefaultValidator,
wxString const & name = "botón" )

Constructor, crear y mostrar un botón.

La mejor manera de crear botones estándar es utilizar el valor predeterminado de


la etiqueta. Si no se suministra y la etiqueta de identificación es uno de los
identificadores estándar de esta lista, con etiqueta estándar se usará. Además de
eso, el botón estará decorado con iconos en GTK + 2.

wxButton:: ~ wxButton

~ wxButton ()

Destructor, destruyendo el botón.

wxButton:: Create

bool Crear (* wxWindow padre, wxWindowID id, wxString const & label =
wxEmptyString, wxPoint const & POS = wxDefaultPosition, wxSize const & size =

97
Tópicos Selectos De Programación Actividades 2009
wxDefaultSize, long style = 0, wxValidator const y validador, wxString const &
name = "button")

La función de creación de botones para la creación de dos pasos. Para más


detalles, véase wxButton:: wxButton.

wxButton:: getLabel

getLabel wxString () const

Devuelve la etiqueta de cadena para el botón.

Valor devuelto

La etiqueta del botón.

Ver también

wxButton:: SetLabel

wxButton:: GetDefaultSize

GetDefaultSize wxSize ()

Devuelve el tamaño por defecto de los botones. Se recomienda hacer todos los
botones de diálogo del mismo tamaño y esta función permite recuperar la
(plataforma y tamaño actual depende de fuentes) que debe ser el más adecuado
para ello.

wxButton:: Por defecto

Por defecto void ()

Esto establece el botón para ser el tema por defecto para el grupo especial o
cuadro de diálogo.

wxButton:: SetLabel

void setLabel (wxString const & etiqueta)

98
Tópicos Selectos De Programación Actividades 2009
Establece la etiqueta de cadena para el botón

wxT

wxChar wxT (char ch)

wxChar const char * wxT (const char * s)

wxT () es una macro que puede ser utilizado con carácter y literales de cadena (en
otras palabras, "x" o "foo") para convertir automáticamente a Unicode en Unicode
configuración de generación. Por favor vea el resumen Unicode para más
información.

Esta macro es simplemente devuelve el valor que se le pasa sin cambios en ASCII
construir. De hecho, su definición es:

# ifdef UNICODE
# define wxT (x) L # # x
# else / /! Unicode
# define wxT (x) x
# endif

wxString
wxString es una clase que representa una cadena de caracteres. Por favor vea el
resumen wxString para obtener más información al respecto.

Como se explica allí, wxString implementa la mayoría de los métodos de la std::


clase String. Estas funciones estándar no están documentadas en este manual,
consulte la documentación de STL). El comportamiento de todas estas funciones es
idéntico al comportamiento descrito allí.

99
Tópicos Selectos De Programación Actividades 2009
Usted puede notar que wxString a veces tiene muchas funciones que hacer lo
mismo, como, por ejemplo, Longitud (), Len () y longitud (), que todos los devolverá
la longitud de cadena. En todos los casos de esa duplicación de la std:: cadena
método compatible (longitud () en este caso, siempre la versión en minúsculas) se
debe usar ya que asegurará un suave transición a std:: string wxWidgets cuando
comienza a utilizar en lugar de wxString.

Derivado de

Ninguno

Incluir archivos

<wx/string.h>

Objetos predefinidos

Objetos:

wxEmptyString

wxString:: wxString

wxString ()

Default constructor. Inicializa la cadena "" (cadena vacía).

wxString (const wxString & x)

Constructor de copia.

wxString (wxChar ch, size_t n = 1)

Construye una cadena de n copias de carácter ch

wxString:: IsWord

IsWord bool () const

Devuelve true si la cadena es una palabra.

Esta es una wxWidgets 1.xx función de compatibilidad, usted no debe utilizar en el


nuevo código.

100
Tópicos Selectos De Programación Actividades 2009
wxString:: Última

Última wxChar () const

Devuelve el último carácter.

wxChar y Last ()

Devuelve una referencia al último carácter (modificable).

Esta es una wxWidgets 1.xx función de compatibilidad, usted no debe utilizar en el


nuevo código.

wxString:: Izquierda

wxString izquierda (size_t count) const

Devuelve los caracteres primer recuento de la cadena.

wxString:: Len

size_t len () const

Devuelve la longitud de la cadena.

wxString:: Longitud

size_t length () const

Devuelve la longitud de la cadena (al igual que Len).

Esta es una wxWidgets 1.xx función de compatibilidad, usted no debe utilizar en el


nuevo código.

wxString:: Baja

Baja wxString () const

Devoluciones esta cadena convertida a la minúscula.

101
Tópicos Selectos De Programación Actividades 2009

wxString:: Minúsculas

Minúsculas void ()

Igual que MakeLower.

Esta es una wxWidgets 1.xx función de compatibilidad, usted no debe utilizar en el


nuevo código.

wxString:: MakeLower

wxString & MakeLower ()

Convierte todos los caracteres a minúsculas y devuelve el resultado.

wxString:: MakeUpper

wxString & MakeUpper ()

Convierte todos los caracteres a mayúsculas y devuelve el resultado

COUT Y CIN
C++, al igual que C, no tiene operaciones de entrada/salida como parte del lenguaje en
sí, sino que define la librería stream para añadir estas funciones. La salida por pantalla
se hace a través de cout, pero es algo diferente a la función printf(), ya que no tenemos

102
Tópicos Selectos De Programación Actividades 2009
que indicarle el tipo de la variable que queremos imprimir. Así se escribe en C++ el
tradicional ejemplo "Hello, world":

#include <iostream.h>

main()
{
cout << "Hello, world";
}

cout saca por pantalla cualquier tipo de dato estándar que existe en C++, bien sea un
carácter, un número o movimientos especiales del cursor,como \n en el ejemplo
anterior. Veamos otro programa:

#include <iostream.h>
main()
{
int a;
float b;

a = 4;
b = 52.2;

cout <<"Vamos a imprimir un número entero:";


cout << a;
cout <<'\n';
cout <<"Y ahora uno real:";
cout << b;
cout <<'\n';
}

La salida de este programa es:

Vamos a imprimir un número entero: 4


Y ahora uno real: 52.

Veamos algunos ejemplos que utilicen los que ya sabemos de C++.

Pero antes introduciremos, sin explicarlo en profundidad, dos elementos que nos
permitirán que nuestros programas se comuniquen con nosotros. Se trata de la salida
estándar, "cout" y de la entrada estándar "cin". Estos elementos nos permiten enviar a la
pantalla o leer desde el teclado cualquier variable o constante, incluidos literales. Lo
veremos más detalladamente en un capítulo dedicado a ellos, de momento sólo nos
interesa cómo usarlos para mostrar o leer cadenas de caracteres y variables.

103
Tópicos Selectos De Programación Actividades 2009
Nota: en realidad "cout" es un objeto de la clase "ostream", y "cin" un objeto de la clase
"istream" pero los conceptos de clase y objeto quedarán mucho más claros en capítulos
posteriores.

El uso es muy simple:

#include <iostream>
using namespace std;

cout << <variable|constante> [<< <variable|constante>...];


cin >> <variable> [>> <variable>...];

Veamos un ejemplo:

#include <iostream>
using namespace std;

int main()
{
int a;

cin >> a;
cout << "la variable a vale " << a;
return 0;
}

Un método muy útil para "cout" es "endl", que hará que la siguiente salida se imprima
en una nueva línea.

cout << "hola" << endl;

Otro método, este para "cin" es get(), que sirve para leer un carácter, pero que nos puede
servir para detener la ejecución de un programa.

ASSIGN

El bloque de ASSIGN se utiliza para dar o modificar el valor de un parámetro de las


trabucaciones.

104
Tópicos Selectos De Programación Actividades 2009
A: Número del parámetro de una transacción activa que recibe la información. El
operando debe ser número entero, nombre o SNA seguido por nulo + o - (requerido).
B: El valor que será asignado al parámetro indicado por el operando A. El operando
debe ser número entero, nombre o SNA (requerido).
C: El número de la función (modificador de la función). El operando debe ser nulo,
número entero, nombre o SNA (requerido).

Puede utilizar el comando at con el fin de programar un comando,


una secuencia de comandos o un programa para ejecutarse en una
fecha y hora especificados. También puede utilizar este comando para
ver las tareas programadas existentes.

assign () y size ()

Este método se utiliza para asignar un tamaño inicial del vector. Este método tiene 2
versiones sobrecargadas. Aquí hemos utilizado el primer método sobrecargado. Este
método toma un argumento entero para inicializar el tamaño del vector.

at ()

No importa cuán sofisticada herramienta que conseguimos, el acceso a la gama C de


estilo antiguo utilizando el índice es muy popular. Esta cosa se tuvo en cuenta cuando
se diseñó STL. Como se comporta como vector de una matriz abierta, la gente espera
que para mostrar otros comportamientos de la matriz. en () es un método que toma
un entero como argumento y devuelve el valor en ese lugar. Así, en una forma que
simula la edad de indexación de arrays de edad.

begin (), end ()

En STL en los contenedores, los elementos son accesibles usando iteradores. Los
iteradores son algo así como punteros. begin () y final () devuelve el iterador al
principio y al final del contenedor. He aquí una cosa hay que señalar final () apunta a
una ubicación que es uno después del final físico del contenedor. Estos dos métodos
son los más utilizados en STL, porque para atravesar un contenedor, es necesario crear
un iterador. Y entonces para inicializar su valor al inicio del contenedor.

105
Tópicos Selectos De Programación Actividades 2009
frente (), back ()

() frente método devuelve el elemento en la parte delantera del vector. Volver ()


devuelve el elemento en la parte posterior del vector. Aquí está el código para
comprender mejor sus operaciones.

() capacidad, el tamaño ()

capacidad () devuelve el número de elementos del vector puede tener inicialmente


asignado. Para un vector aunque no es muy importante el método. Por otra parte, el
tamaño de método () devuelve el número de elementos que están actualmente en el
vector. Aquí está el código que le ayudará a entender la diferencia entre la capacidad
() y tamaño ().

Plantillas de clase

Aplicar una plantilla de clase


Definición de una plantilla de clase es similar a una definición de clase regulares,
salvo que se pone la plantilla de palabra clave. Por ejemplo, aquí es la definición de
una plantilla de clase para una pila.

clase de plantilla < clase T > pila {pública: Stack(int = 10); ~Stack() {delete []
stackPtr;} int inserción (const T &); int pop(T&); int isEmpty () const {arriba
devuelto ==-1;} int isFull() const {devuelto parte superior == tamaño - 1;} privada:
int tamaño; / / número de elementos de la pila.parte superior de int; T * stackPtr;};

106
Tópicos Selectos De Programación Actividades 2009
T es un parámetro de tipo y puede ser cualquier tipo. Por ejemplo, la pila T no tiene
que ser un tipo de clase como implícitas, por la clase de palabra clave.

Aplicación de las funciones de miembro de plantilla de clase

Implementación de las funciones de miembro de la plantilla es algo diferente en


comparación con las funciones miembro de la clase regular.Las declaraciones y
definiciones de las funciones de miembro de la plantilla de clase deben ser en el
mismo archivo de encabezado.Las definiciones y las declaraciones deben ser en el
mismo archivo de encabezado. Tenga en cuenta lo siguiente.

/ / B.CPP # include
//B.H plantilla < clase t > //MAIN.CPP # include
"B.H" plantilla < clase t >
clase b {pública: b(); ~b();}; "B.H" void main() {b
b

Al compilar B.cpp, el compilador tiene las declaraciones y las definiciones


disponibles. En este momento el compilador no es necesario generar las
definiciones para las clases de plantilla, ya no hay instancias.Cuando el compilador
compila main.cpp, hay dos instancias: plantilla de clase B En este momento el
compilador tiene las declaraciones pero no definiciones!

Mientras se implementan las funciones de miembro de plantilla de clase, las


definiciones tienen el prefijo por la plantilla de palabra clave.Aquí es la
implementación completa de la plantilla de clase pila:

//stack.h
#pragma once
template <class T>
class Stack
{
public:
Stack(int = 10) ;
~Stack() { delete [] stackPtr ; }
int push(const T&);
int pop(T&) ; // pop an element off the stack
int isEmpty()const { return top == -1 ; }
int isFull() const { return top == size - 1 ; }
private:
int size ; // Number of elements on Stack
int top ;
T* stackPtr ;
};

//constructor with the default size 10


template <class T>
Stack<T>::Stack(int s)
{
size = s > 0 && s < 1000 ? s : 10 ;

107
Tópicos Selectos De Programación Actividades 2009
top = -1 ; // initialize stack
stackPtr = new T[size] ;
}
// push an element onto the Stack
template <class T>
int Stack<T>::push(const T& item)
{
if (!isFull())
{
stackPtr[++top] = item ;
return 1 ; // push successful
}
return 0 ; // push unsuccessful
}

// pop an element off the Stack


template <class T>
int Stack<T>::pop(T& popValue)
{
if (!isEmpty())
{
popValue = stackPtr[top--] ;
return 1 ; // pop successful
}
return 0 ; // pop unsuccessful
}

Utilizando una plantilla de clase


Es fácil de utilizar una plantilla de clase. Crear las clases requeridas con conectar el
tipo real para los parámetros de tipo.Este proceso se conoce comúnmente como
"Instantiating una clase". Aquí es una clase de controlador de ejemplo que utiliza la
plantilla de clase de pila.

# include <iostream># include "stack.h" con espacio de nombres std; void main()
{typedef pila <float><int>FloatStack; typedef pila < int > IntStack; FloatStack fs(5);
flotar f = 1.1; cout << "Insertar elementos en fs" << endl; mientras que (fs.push(f))
{cout << f << ' '; f += 1.1;} cout << endl << "Pila completo." << endl << endl <<
"Elementos de fs emergen" << endl; mientras que (fs.pop(f)) cout << f << ' '; cout
<< endl << "Pila vacío" << endl; cout << endl; es IntStack; int I = 1.1; cout <<
"Insertar elementos en es" << endl; mientras que (is.push(i)) {cout << me << ' '; I
+= 1;} cout << endl << "Lleno de pila" << endl << endl << "Emergen elementos de
es" << endl; mientras que (is.pop(i)) cout << me << ' '; cout << endl << "Pila vacío"
<< endl;}

108
Tópicos Selectos De Programación Actividades 2009
Programa de salida
Insertar elementos en fs 1.1 2,2 3.3 4,4 5.5 pila completo.Haciendo estallar los
elementos de fs 5.5 4.4 3,3 2.2 1.1 pila vacío insertar elementos en es 1 2 3 4 5 6 y 7
8 9 10 elementos desde que emergen completa de pila es 10 9 8 7 6 5 4 3 2 1 pila
vacío

En el ejemplo anterior definimos una pila de la plantilla de clase. En el programa


del controlador crea una instancia una pila de float (FloatStack) y una pila de
int(IntStack). Una vez que se crea una instancia de las clases de plantilla puede
crear instancias de objetos de ese tipo (por ejemplo, fs y es.)

Una buena práctica de programación está utilizando typedef mientras crear una
instancia de clases de plantilla. A continuación, a lo largo de todo el programa, se
puede usar el nombre de typedef. Existen dos ventajas:

 del typedef son muy útiles "plantillas de plantillas" entrado en uso. Por
ejemplo, al crear una instancia de un vector de STL del int, puede utilizar:

vector typedef < int, asignador

 Si cambia la definición de la plantilla, simplemente cambiar la definición de


typedef. Por ejemplo, actualmente la definición de vector de la clase de
plantilla requiere un segundo parámetro.

vector typedef < int, asignador

En una versión futura, el segundo parámetro puede no ser necesario, por


ejemplo,

vector typedef

Imagine cuántos cambios sería necesarios si ha habido ningún typedef.

Plantillas de función
Para realizar operaciones idénticas para cada tipo de datos de forma compacta y
convenientemente, utilice plantillas de función. Puede escribir una definición única
función de la plantilla. Según los tipos de argumentos en llamadas a la función, el
compilador crea automáticamente las funciones de código objeto independiente
para controlar adecuadamente cada tipo de llamada. Los algoritmos STL se
implementan como plantillas de función.

109
Tópicos Selectos De Programación Actividades 2009

Implementación de funciones de plantilla


Plantillas de función se implementan como funciones regulares, excepto que tienen
el prefijo con la plantilla de palabra clave. Here is a sample with a function
template.

#include <iostream>
using namespace std ;
//max returns the maximum of the two elements
template <class T>
T max(T a, T b)
{
return a > b ? a : b ;
}

Using Template Functions


Using function templates is very easy: just use them like regular functions. When
the compiler sees an instantiation of the function template, for example: the call
max(10, 15) in function main, the compiler generates a function max(int, int).
Similarly the compiler generates definitions for max(char, char) and max(float,
float) in this case.

#include <iostream>
using namespace std ;
//max returns the maximum of the two elements
template <class T>
T max(T a, T b)
{
return a > b ? a : b ;
}
void main()
{

cout << "max(10, 15) = " << max(10, 15) << endl ;
cout << "max('k', 's') = " << max('k', 's') << endl ;
cout << "max(10.1, 15.2) = " << max(10.1, 15.2) << endl ;
}

Program Output
max(10, 15) = 15
max('k', 's') = s
max(10.1, 15.2) = 15.2

110
Tópicos Selectos De Programación Actividades 2009

una clase no es de plantilla.

Especialización de la función de plantilla


En algunos casos es posible reemplazar el código generado plantilla al ofrecer
definiciones especiales para tipos específicos. Esto se denomina la especialización
de plantilla. El ejemplo siguiente muestra una situación donde reemplazar el
código de plantilla generado sería necesario:

# include plantilla < clase T > T max(T a, T b) {devolver un > b? un: b;} int main()
{cout << "max (10, 15) =" << max (10, 15) << endl; cout << "max('k', 's') =" <<
max('k', 's') << endl; cout << "max (10.1, 15.2) =" << max (10.1, 15.2) << endl; cout
<< "max(\"Aladdin\", \"Jasmine\") =" << max ("Aladdin", "Jazmín") << endl;
devolver 0;}

Programa de salida
Max (10, 15) = max('k', 's') 15 = s máx. (10.1, 15.2) = 15.2 máx. ("Aladdin",
"Jasmine") = Aladdin

No exactamente los resultados esperados. ¿Por qué ha ocurrido que? La llamada a


función máxima ("Aladdin", "Jasmine") hace que el compilador generar el código
de max (char *, char *), que compara las direcciones de las cadenas! Para corregir
estos casos especiales o para proporcionar implementaciones más eficientes para
determinados tipos, se pueden usar especializaciones de plantilla. El ejemplo
anterior puede ser reescrito con especialización como sigue:

# include <iostream># include <cstring>con espacio de nombres std; //max


devuelve el máximo de la max(T a, T b) de T de dos elementos plantilla < clase T >
{devolver un > b? un: b;} / / especialización de max para char * plantilla <>char *
max(char* a, char* b) {volver strcmp (a, b) > 0? un: b;} int main() {cout << "max
(10, 15) =" << max (10, 15) << endl; cout << "max('k', 's') =" << max('k', 's') <<
endl; cout << "max (10.1, 15.2) =" << max (10.1, 15.2) << endl; cout <<
"max(\"Aladdin\", \"Jasmine\") =" << max ("Aladdin", "Jazmín") << endl; devolver
0;}

Programa de salida
Max (10, 15) = max('k', 's') 15 = s máx. (10.1, 15.2) = 15.2 máx. ("Aladdin",
"Jasmine") = Jasmine

111
Tópicos Selectos De Programación Actividades 2009

Parámetros de plantilla
1. Las plantillas de C++ permiten uno implementar una cola genérica T se
puede sustituir por tipos reales, por ejemplo, Por ejemplo,

clase de plantilla < clase T > pila {};

Aquí T es un parámetro de plantilla, que también se conoce como


parámetro de tipo.

2. C++ le permite especificar un parámetro de plantilla por defecto, por lo que


ahora podría parecerse a la definición:

plantilla < clase T = float, int elementos = 100 > pila {...};

A continuación, una declaración como

La pila de <>mostRecentSalesFigures;

sería una instancia (en tiempo de compilación) una plantilla de pila de 100
elemento clase denominada mostRecentSalesFigures de valores flotantes;
esta clase de plantilla sería de tipo Stack < float, 100 >.

Tenga en cuenta, C++ también permite que los parámetros de plantilla no


son de tipo. En este caso, la clase de plantilla pila tiene un int como un
parámetro de tipo no.

Si especifica un parámetro de plantilla predeterminado para cualquier


parámetro formal, las reglas son las mismas que para las funciones y
parámetros por defecto. Una vez que un parámetro por defecto se
declara todos los parámetros deben tener valores predeterminados.

3. No se pueden especificar argumentos predeterminados en una declaración


o una definición de una especialización. Por ejemplo,

{} de pila de clase de plantilla < clase T, int tamaño >; //error C2989: "Stack

4. Un parámetro de tipo define su identificador a ser un nombre de tipo en el


ámbito de la declaración de plantilla, y canot ser re-declared en su ámbito
de aplicación (incluyendo ámbitos anidados). Por ejemplo,

clase de plantilla < clase T, int tamaño > pila {int T; //error-parámetro de
tipo re-defined.anular f() {char T; //error-parámetro de tipo re-defined.}};
{} de clase A; int main() {pila si

Nota: Visual c++ 5.0 o SP1 compila este ejemplo sin errores. No bandera la
redefinición del parámetro de tipo como un error.

112
Tópicos Selectos De Programación Actividades 2009
5. El valor de un no-parámetro de tipo no se puede asignar o su valor
cambiado. Por ejemplo,

clase de plantilla < clase T, int tamaño > pila {anular f() {//error C2105: ' ++
' l-valor de las necesidades de tamaño ++; cambio de //error de valor del
argumento de plantilla}}; int main() {pila si

6. Un parámetro de plantilla que podría interpretarse como una declaración


de parámetro o un parámetro de tipo, se toma como un parámetro de tipo.
Por ejemplo,

clase T {}; int I; plantilla < clase T, T I > f void (T t) {t1 T = I; //template
argumentos T y:: t2 T =:: I; //globals T y} int main() {f('s'); //C2783 volver
aquí 0;}

Nota: compilando el ejemplo anterior mediante Visual c++ 5.0 y SP1


provoca un error del compilador C2783: no se pudo deducir el argumento
de plantilla para 'I'. Para solucionar el problema, reemplace la llamada a
f('s') con f < char, ' s'>('s').

clase T {}; int I; plantilla < clase T, T I > f void (T t) {t1 T = I; //template
argumentos T y:: t2 T =:: I; //globals T y} int main() {f < char, ' s'>('s');
//workaround devuelven 0;}

7. Un parámetro de plantilla no son de tipo no puede ser de tipo de flotantes.


Por ejemplo,

clase de plantilla < doble d > X; //error C2079: 'xd' utiliza //undefined clase
'X <1.e66>' //template < doble * pd > clase X; //ok //template < doble & rd
> clase X; //ok int main() {X <1.0> xd; return 0;}

113
Tópicos Selectos De Programación Actividades 2009
EXTRAS:

ALGORITMO DE CONVERSION FIJA A POTSFIJA


CONVERSION DE NUMEROS ARABIGOS A ROMANOS
CODIGO DE CALCULADORA

Algoritmo de Conversión de Expresión Infija a Expresión


Postfija

1) Meter un paréntesis izquierdo '(' en la pila


2) Agregar un paréntesis derecho ')' al final de Infijo
3) Mientras que la pila no esté vacía, leer Infijo de izquierda a derecha y hacer lo
siguiente:
1. Si el carácter actual en Infijo es un paréntesis es un dígito, copiarlo al siguiente
elemento de Postfijo
2. Si el carácter actual en Infijo es un paréntesis izquierdo, meterlo a la pila
3. Si el carácter actual en Infijo es un operador
1. Sacar los operadores (si los hay) de la parte superior de la pila, mientras
tengan igual o mayor precedencia que el operador actual, e insertar en Postfijo
los operadores que se sacaron
2. Meter en la pila el carácter actual en infijo
3. Si el carácter actual en Infijo es un paréntesis derecho:
1. Sacar operadores de la parte superior de la pila e insertarlos en Postfijo,
hasta que haya un paréntesis izquierdo en la parte superior de la pila.
2. Sacar (y descartar) el paréntesis izquierdo de la pila

Las siguientes operaciones aritméticas se permiten en una expresión:

+ Suma
- Resta
* Multiplicación
/ División
^ Exponenciación
% Módulo

114
Tópicos Selectos De Programación Actividades 2009

Convertir números Arábigos a Romanos


También puedes ver el proceso inverso: Romano a Arábigo

El proceso de conversión de Números Arábigos (1, 2, 3,…) a Números Romanos


(I, II, III,…) consiste en lo siguiente:

Dado un número, se realiza una serie de comparaciones contra cada uno de los
valores correspondientes a los símbolos romanos enlistados una tabla que los
contenga ordenados de mayor a menor.

La clave es dicha tabla (que por facilidad puede ser también dos vectores
paralelos) que nos muestre los valores romanos ordenados desde el más alto y
hasta terminar en el más bajo (o sea el 1) así como su “Valor nominal”.

i Romanos[ ] Valores[ ]
1 M 1000
2 CM 900
3 D 500
4 CD 400
5 C 100
6 XC 90
7 L 50
8 XL 40
9 X 10
10 IX 9
11 V 5
12 IV 4
13 I 1

Definición de las tablas Romanos y Valores


(Tabla de equivalencias Romano-Arábigo)

115
Tópicos Selectos De Programación Actividades 2009
Nótese que en la tabla se incluyeron los elementos que están formados por una
resta (4, 9, 40, 90, 400 y 900) de esta manera se simplifica bastante el proceso
porque a partir de aquí, ya solo concatenamos.

EJEMPLO: Si tenemos un número a convertir a Romano llamado “A” que tiene un


valor de 1200. Recorremos la lista revisando cuales de los valores de los elementos
de la tabla caben en nuestro número:
• El 1000 cabe, así que ponemos la letra “M” y le restamos 1000. Nos quedan 200
• El siguiente que cabe es el 100 así que ponemos la “C” y restamos 100. nos
quedan 100.
• Cabe otro 100, así que ponemos otra “C” y como nos queda cero, se acabó.

Y el resultado es “MCC” = 1200.

Pseudocódigo

Este mismo proceso en pseudo-código, nos quedaría de la siguiente manera:

1.Inicio
2.Dadas las tablas Romanos[] y Valores[]
3.Pedir: A
4.Hacer Resultado = ""
5. Ciclo en i para cada elemento de Romanos
6. Mientras Valores<i> <= A hacer
7. Resultado = Resultado + Romanos<i>
8. A = A - Valores<i>
9. Fin-Mientras
10. Fin-Ciclo
11.Mostrar Resultado
12.Fin

NOTA: El algoritmo se puede hacer más eficiente si se valida que cuando A llegue a
cero, se interrumpa el ciclo. De ese modo sería más óptimo, aunque funcionaría
igual.

116
Tópicos Selectos De Programación Actividades 2009
Diagrama de
Flujo

Para convertir numeros romanos muy grandes

Si quieres convertir numeros con valores por arriba de los que se explican en la
tablita arriba mencionada, recuerda que la regla es que una rayita arriba del
número lo multiplica por mil. Si pones dos rallitas, entonces es 1,000 x 1,000
osea 1 millón, y así sucesivamente.

Por ejemplo:

_
X = 10000 (Diez con una rayita igual a diez mil)

=
X = 10000000 (Diez con dos rayitas igual a diez millones)

117
Tópicos Selectos De Programación Actividades 2009

Convertir números Romanos a Arábigos

También puedes ver el proceso inverso: Arábigo a Romano

El proceso de conversión de Números Romanos (I, II, III,…) a Números Arábigos


(1, 2, 3,…) es un proceso bastante simple y consiste en

La suma de los valores individuales de los caracteres que forman el Número Romano

Así pues tenemos que la conversión sería así:

I=1
II =1+1
III =1+1+1
IV = 1 + 4 ?

Y aquí viene el primer tropiezo, ya que números como el 4 (IV), el 9 (IX) o el


900(CM) requieren de una resta. Para solucionarlo podemos tomar dos caminos,
uno sería tener una tabla con los valores romanos de todos los elementos,
incluyendo los valores compuestos, e ir comparando cada uno contra dicha tabla
asegurándonos de comparar primero XC antes de X y IV antes de I.

La otra manera es implementando un sistema de sumas y restas condicionadas


donde para cada número estemos revisando también el de adelante y si el de
adelante es mayor, entonces es una resta, y si es menor o igual entonces es una
suma.

La primera opción es la mas sencilla de implementar, no obstante, en lo personal


considero más elegante la segunda, ya que puede también funcionar con valores
"no ortodoxos" como en el caso de que para poner 1999 ponen MIM (1000 -1
+1000) en lugar de MCMXCIX (1000 + 900 +90 +9)

Entonces, vamos a desarrollar esta segunda opción primero por medio de un


Seudo código.

Pseudocódigo

Condiciones iniciales:
Como condición inicial consideraremos que contamos con una función llamada

118
Tópicos Selectos De Programación Actividades 2009
ValorRomano() que nos devuelve el valor numérico de caracteres individuales.
Ejemplo: ValorRomano("I")=1, ValorRomano("V")=5
1. Inicio
2. Previa definición de ValorRomano()
3. Obtener: R como número Romano
4. Resultado = 0
5. Max = Longitud de R
6. Ciclo en i hasta Max - 1
7. C = Caracter i de R
8. C2 = Caracter i+1 de R
9. si ValorRomano(C)< ValorRomano(C2) entonces
10. Resultado = Resultado - ValorRomano(C)
11. sino
12. Resultado = Resultado + ValorRomano(C)
13. Fin-si
14. Fin-Ciclo
15. C2 = Caracter Max de R
16. Resultado = Resultado + ValorRomano(C2)
17. Mostrar Resultado
18. Fin

Observaciones al Proceso:

 Primero calculamos la longitud de la cadena (5)


 Como vamos a estar checando un carácter adelante, hacemos el ciclo hasta
uno antes del total. (6)
 Obtenemos los caracteres individuales de las posiciones e [i+1] (7 y 8)
 Si el primer carácter es menor que el segundo (9) entonces es un IV, IX, XC y
hay que restarle el valor del primero (10) de lo contrario se suma (12)
 Al final, hay que sumar también el valor del último carácter de la cadena
(15,16) ya que en nuestro ciclo lo habíamos omitido con el -1

Diagrama de flujo

119
Tópicos Selectos De Programación Actividades 2009

string convertirRomano (int n)

{
/* Se explican los 3 tipos de casos, los demas son análogos */
int z;
string respuesta = "";

/* Se recorre cada caso y se verifica divisibilidad */


if ((z = n/1000) > 0)
{
for (int x =0; x < z; x++)
/* Si es divisible agrega las letra a la cadena de respuesta
*/
/* esto se hace tantas veces el caso sea multiplo del
número en cuestion */
respuesta = respuesta + "M";
/* Se resta la cantidad correspondiente a cuantas veces cabia el
caso entre el número */
n -= 1000 * z;
}

if ((z = n/900) > 0)


{

120
Tópicos Selectos De Programación Actividades 2009
/* Si es divisible agrega las letras a la cadena de respuesta */
respuesta = respuesta + "CM";
/* Resta el valor de la letras ya agregadas */
n -= 900;
}

if ((z = n/500) > 0)


{
/* Si es divisible agrega la letra a la cadena de respuesta */
respuesta = respuesta + "D";
/* Resta el valor de la letra ya agregada */
n -= 500;
}

if ((z = n/400) > 0)


{
respuesta = respuesta + "CD";
n -= 400;
}

if ((z = n/100) > 0)


{
for (int x =0; x < z; x++)
respuesta = respuesta + "C";
n -= 100 * z;
}

if ((z = n/90) > 0)


{
respuesta = respuesta + "XC";
n -= 90;
}

if ((z = n/50) > 0)


{
respuesta = respuesta + "L";
n -= 50;
}

if ((z = n/40) > 0)


{
respuesta = respuesta + "XL";
n -= 40;
}

if ((z = n/10) > 0)


{
for (int x =0; x < z; x++)
respuesta = respuesta + "X";
121
Tópicos Selectos De Programación Actividades 2009
n -= 10 * z;
}

if ((z = n/9) > 0)


{
respuesta = respuesta + "IX";
n -= 9;
}

if ((z = n/5) > 0)


{
respuesta = respuesta + "V";
n -= 5;
}

if ((z = n/4) > 0)


{
respuesta = respuesta + "IV";
n -= 4;
}

if ((z = n) > 0)
{
for (int x =0; x < z; x++)
respuesta = respuesta + "I";
n -= z;
}

return respuesta;

122
Tópicos Selectos De Programación Actividades 2009
#include <iostream>

#include <string>

#include <stack>

#include <sstream>

#include <cctype>

#include <cmath>

using std::cout;

using std::cin;

using std::stack;

using std::string;

using std::istringstream;

// Implementación de la clase Infija

// Esta clase lo que realiza es la conversión de una cadena Infja -> Postfija

class Infija

private:

string strInfija;

public:

//Este constructor recibe una cadena en infijo y la guarda en la


variable privada strInfija

Infija(string i) : strInfija(i.c_str())

123
Tópicos Selectos De Programación Actividades 2009

// Aqui vas a meter la implementacion de tu algoritmo de


Conversion infija a -> Postfija

// Es decir lo que tienes en tu primer MAIN que hace la conversion


de Infija a Postfija

// Un ejemplo puede ser este

string Postfija()

// Declaramos la variable en donde vamos a almacenar la


cadena Postfija para el retorno del valor

string strCadenaPostfija;

// Instancia de la clase <T> stack de la biblioteca estandar


STL de una pila

stack<char> Pila;

// Segun el algoritmo nos dice:

// 1) Meter un parentesis izquierdo '(' en la pila

Pila.push('(');

// 2) Agregar un parentesis derecho ')' al final de infijo

string stream strCadenaInfija(strInfija + ")");

// 3) Mientras que la pila no este vacia, leer infijo de izquierda


a derecha y hacer lo siguiente:

while(!Pila.empty())

// Leo el siguiente caracter de infijo

124
Tópicos Selectos De Programación Actividades 2009
char caracterActual = (char)strCadenaInfija.get();

// 3.1) Si el caracter actual en infijo es un digito,


copiarlo al siguiente elemento de postfijo

if(isdigit(caracterActual))

strCadenaPostfija += caracterActual;

// 3.2) Si el caracter actual en infijo es un parentesis


izquierdo, meterlo a la pila

if( caracterActual == '(')

Pila.push(caracterActual);

// 3.3) Si el caracter actual en infijo es un operador,

if(isoperator(caracterActual))

// 3.3.1) Sacar los operadores (si los hay) de la


parte superior de la pila,

// mientras tengan igual o mayor


precedencia que el operador actual

// e insertar en postfija los operadores


que se sacaron

char topePila = Pila.top();

Pila.pop();

125
Tópicos Selectos De Programación Actividades 2009
while(!Pila.empty() &&
Precedencia(caracterActual, topePila))

strCadenaPostfija += topePila;

topePila = Pila.top();

Pila.pop();

// 3.3.2) Meter en la pila el caracter actual en


infija

Pila.push(caracterActual);

// 3.4.) Si el caracter actual en infijo es un parentesis


derecho

if(caracterActual == ')')

// 3.4.1) Sacar los operadores de la parte superior de la pila e insertarlos en


postfijo,

// hasta que haya un parentesis


izquierdo en la parte superior de la pila

while(!Pila.empty() || Pila.top() == '(')

strCadenaPostfija += Pila.top();

Pila.pop();

126
Tópicos Selectos De Programación Actividades 2009
// 3.4.2) Sacar (y descartar) el parentesis
izquierdo de la pila

Pila.pop();

bool isoperator(char o)

// Aqui vas a agregar los operadores que tu quieras y los que


te pida tu profe, es decir yo :D

// solamente te dejo estos como ejemplo, es responsabilidad


tuya escalarlos, es decir, hacer mas

// funcional tu calculadora

if(o == '+' || o == '-' || o == '*' || o == '/')

return true;

return false;

// Este metodo lo que hace es determinar la precedencia de los


operadores 1 y 2

// Devuelve -1 si op1 es de menor precedencia que op2

// devuelve 0 si la precedencia es igual

// devuelve 1 si la precedencia de op1 es mayor que la de op2

// RECUERDA: la precedencia se refiere a cual de los operadores se


evalua primero, por ejemplo

// en la expresion: 3 * 4 + 1

127
Tópicos Selectos De Programación Actividades 2009
// el operador con mayor precedencia es * y despues el +

int Precedencia(char op1, char op2)

// Aqui dejo que tu hagas la implementacion como mejor te


parezca

// y con el ingenio que te caracterice

// por omision ahora voy a retornar siempre 0

return 0;

};

//Esta clase evalua una expresio Postfija y devuelve un resultado

class Evaluador

private:

string strPostfija;

public:

//Este constructot recibe una expresio potsija y la almacena en la


variable privada strPostfija

Evaluador(string p) : strPostfija(p.c_str())

//Aqui vas a meter la implementacion de tu algoritmo de Evaluacion


de una expresion Postfija

double Ejecutar()

128
Tópicos Selectos De Programación Actividades 2009
{

// Aqui en esta parte, vas a poner el algoritmo para evaluar


una expresion postfija

// La ayuda es la siguiente:

// Voy a transcribir el algoritmo que podrán utilizar para


implementar su Evaluador

/*

1) Mientras no sea el final de la cadena Postfija, leer la


expresio de izquierda a derecha

1.1) Si el caracter actual es un DIGITO

1.1.1) Meter su valor a la pila

1.2) En caso contrario, si el caracter actual es un


OPERADOR

1.2.1) Sacar los 2 (dos) elementos


superiores de la pila y colocarlos en las variables X y Y

1.2.2) Calcular Y operador X (En esta


parte es donde utilizas la librerias math para invocar a las funciones de Raiz
Cuadrada,

logaritmo, exponenciacion,
y las tradicionales +, -, *, /, %

1.2.3) Meter el resultado del calculo en la


pila

2) Al encontrar el final de la cadena Postfija, sacar el


valor superior de la pila. Este es el resultado de la expresion postfija

Retornar el valor

129
Tópicos Selectos De Programación Actividades 2009
*/

};

int main()

string strCadenaInfija;

// Leer una cadena completa de la entrada estandar, hasta encontrar un


ENTER

getline(cin, strCadenaInfija);

Infija MiInfija(strCadenaInfija);

Evaluador MiEvaluador(MiInfija.Postfija());

cout<<"Resultado de la operacion "<<strCadenaInfija<<" es


"<<MiEvaluador.Ejecutar();

return 0;

130

Das könnte Ihnen auch gefallen