INSTITUTO SUPERIOR TECNOLÓGICO NORBERT WIENER Manual del Alumno ASIGNATURA: Metodología de Programación Avanzada con Visual C++ PROGRAMA: S3C
INSTITUTO SUPERIOR
TECNOLÓGICO
NORBERT WIENER
Manual del Alumno
ASIGNATURA: Metodología de Programación Avanzada con
Visual C++
PROGRAMA: S3C
2
Manual del Alumno
Tipos de Datos en C.
Hay Varios tipos de Datos básicos en C:
TIPO I. DESCRIPCION
Char Un solo byte (8 bits) capaz de entender un carácter del
conjunto de caracteres local.
Int Un entero , normalmente del tamaño natural de los
enteros en la maquina que los ejecuta.
Float Utilizado para números con punto flotante de
precisión. P/e: 12.345 .12345e2 o 123495.0e-3
Double Utilizado para números con punto flotante de doble
precisión p/e:
Log E= 2.718281828459 ó
Sin_per_sec=4.848136811076e-7
Además existen algunos calificadores que se aplican a estos tipos
de básicos: Short y Long se aplican a enteros.
La intención es que short y long proporcionen diferentes longitudes
de entero donde sea Practico, int será normalmente el tamaño
natural para una maquina en particular. A menudo short es de 16
bits y long de 32, por lo tanto, int es de 16 o 32. Cada compilador
puede seleccionar libremente los tamaños apropiados para su
propio hardware, sujeto solo a las restricciones de que los short's e
int's son por lo menos de 16 bits, los long's son por lo menos de 32
bits y el short no es mayor que el int, el cual a su vez no es mayor
que long.
Ejemplos short int n; Long int contar ; El calificador signed o
unsigned puede aplicarse a char o a enteros. Los números unsigned
son >=0. Por ejemplo la declaración : unsigned int numero; es igual
a unsigned n;
3
Manual del Alumno
El tipo long double especifica punto flotante de precisión
extendida, igual que los enteros, los tamaños de objetos de punto
flotante se definen en la implementación, float, double y long
double representan 3 tamaños distintos.
Los archivos de cabecera estándar <limits.h>y <float.h> contienen
constantes similares para todos esos tamaños.
Tipos de Enteros y sus Rangos.
TIPO DE ENTERO RANGO BYTES
Int -32.768.. 32.767 2
Unsigned int 0 .. 65.535 2
Long -2.147.483.648 ..
2.147.483.647
4
Unsigned long 0 .. 4.294.967.295 4
Tipos de Coma Flotante y Rangos.
TIPO COMA FLOTANTE RANGO BYTES
Float 3.4 x 10-38 .. 3.4 x 1038 4
Double 1.7 x 10-308 .. 1.7 x 10308 8
Long Double 3.4 x 10-4932 .. 1.1 x
104932
10
Tipo de Carácter y rangos de Valores.
TIPO CARÁCTER RANGO BYTES
Char -128 .. 127 1
Unsigned char 0 .. 255 1
4
Manual del Alumno
Variables en C.
Las variables son datos de un programa cuyo valor almacenado en
memoria puede cambiar durante la ejecución del programa, deben
ser declaradas con un nombre y un tipo de dato dado al principio
del nombre o de la función
ejemplo tipo_de_dato nombre_de_dato ; int contador ;
Existen algunas restricciones en el nombre de las variables y
constantes. Los nombres se componen de letras y dígitos; el primer
carácter debe ser una letra, el carácter de subrayado ( _ ) cuenta
como una letra que algunas veces es útil para hacer mas legible un
nombre largo, sin embargo no se debe de comenzar los nombre de
variables con este carácter puesto que las rutinas de biblioteca con
frecuencia usan tal formato en sus variables.
La practica tradicional de C es usar letras minúsculas para nombre
de variables y mayúsculas para las constantes simbólicas.
Al menos los primeros 31 caracteres de un nombre son
significativos, para nombre de funciones el numero puede ser
menor de 31.
Las palabras reservadas del lenguaje no se pueden utilizar como
variables. C hace diferencia entre mayúsculas y minúsculas por lo
que "A" es diferente de "a". Una declaración de variable especifica
que debe de reservarse memoria para el objeto del tipo especificado
y que podemos referirnos a ese objeto por medio de la variable,
ademas de que especifica cuanta memoria debe apartarse para esos
objetos, tambien implica el como han de interpretarse los datos
representandos por cadenas de bits en una localidad de memoria.
Constantes en C.
5
Manual del Alumno
Las Variables son datos que tienen un valor dado por el
programador por default y nunca cambiara en la ejecución del
programa.
Hay diferentes tipos de constantes, las cuales pueden ser declaradas
en C de la siguiente forma:
Constantes Enteras. #define IVA 15 ó const int IVA=15. El
valor de un entero también se puede hacer constante con un
valor Octal o Hexadecimal, un 0 al principio de una
constante entera refiere a un octal y un 0x refiere a un
hexadecimal. #define IVA 017 ó #define IVA 0xF
Constante Reales. #define PI 3.14159265 ó const float
PI=3.14159265
Constante Carácter. Es un entero escrito como un carácter
dentro de apostrofos. El valor de una constante de carácter
es el valor numérico del carácter en el código ASCII.
#define SALIR 's' ó const char SALIR='s'. Existen
caracteres especiales que suelen representar caracteres tales
como la comilla, doble comilla, signo ? y son llamados
Secuencias de Escape:
TIPO DESCRIPCION
‘\n’ Nueva línea
‘\r’ Retorno de carro
‘\t’ Tabulación horizontal
‘\v’ Tabulación Vertical
‘\f’ Avance de pagina
‘\b’ Retroceso de espacio
‘\a’ Alerta(pitido sonoro)
‘\\’ Barra inclinada inversa
‘\’ Comilla simple
‘\’’’ Doble comilla
‘\?’ Signo interrogación
6
Manual del Alumno
‘\000’ Numero Octal
‘\xhhh’ Numero hexadecimal
Constante Cadena ó Cadena Literal. Secuencia de 0 o mas
caracteres encerrados entre comillas como en:
"Hola mundo" ó /* Hola mundo */
Las comillas no son parte de la cadena, solo sirve para
delimitarla.
Constante Enumeración. Una enumeración es una lista de
valores enteros constantes.
Enum boolean {NO,YES};
El primer nombre en un Enum tienen valor 0 y el siguiente 1 y as
sucesivamente, a menos que sean especificados valores explícitos.
Si no todos los valores son especificados, los valores no específicos
continúan con la progresión a partir del ultimo valor que si lo fue.
Enum{Luis=1, fernando, josué=10, juana=11, paty}
Luis vale 1, fernando vale 2, josué vale 10 juana 11 y paty 12.
Operadores en C
Los Operadores son símbolos que permiten al programa llevar
acabo funciones de asignación, matemáticas , lógicas y
relacionales.
Operadores de Asignación.
TIPO OPERADOR ACCIÓN
Simple = Asignación Simple
Compuesta +=
-=
*=
/= (Cociente de la división)
%= (residuo de división)
<<=
>>=
Asignación suma
Asignación Resta
Asignación Producto
Asignación cociente
Asignación módulo
Asignación desplazar izq
Asignación desplazar der
7
Manual del Alumno
&=
¡=
^=
Asignación AND bitwise
Asignación OR bitwise
Asignación XOR bitwise
Ejemplo : n+=5; equivale a n=n+5;
Operadores Aritméticos.
TIPO OPERADOR ACCIÓN
Unitarios +
-
--
++
Signo positivo
Signo Negativo
Decremento
Incremento
Binarios +
-
*
/
%
Suma
Resta
Producto
División
Módulo
Ejemplos: n++ es igual ++n y equivale a n=n+1; a*b; a-b; son resta
y producto de 2 variables.
En C también existe una prioridad dentro de estos operadores,
téngalos en cuenta para el momento de efectuar operaciones en los
programas:
OPERADOR PRIORIDAD
++
--
*
/
%
+
-
=
1
1
2
2
2
3
3
4
8
Manual del Alumno
Operadores Relacionales.
Permiten comparar 2 operandos dando como resultado solo
verdadero ó falso (0 ó 1).
OPERADOR ACCIÓN
<
>
<=
>=
==
¡=
Menor que..
Mayor que..
Menor o igual que..
Mayor o igual que..
Idéntico ó igual a..
Diferente de..
Ejemplos: a<b; a>=(d*r); z==y
Operadores Lógicos.
Permiten manejar y evaluar expresiones lógicas.
OPERADOR OPERACIÓN LÓGICA
¡
&&
||
No (complemento)
Y (AND)
O (OR)
Ejemplos: ¡a-(a*d)&&(x+u)
Operadores de Manipulación de Bits.
Estos operadores de tratamiento de bits (bitwise) ejecutan
operaciones lógicas sobre cada uno de los bits de los operandos.
Esta operaciones son comparables en eficiencia y en velocidad a
sus equivalentes en lenguaje ensamblador.
OPERADOR OPERACIÓN
& Y (AND) Lógica bit a bit
9
Manual del Alumno
| O (OR)
^ O (OR)
~
<<
>>
Lógica bit a bit
Lógica bit a bit (OR exclusivo, XOR)
Complemento a uno (inversión de todos los
bits)
Desplazamiento de bits a izquierda
Desplazamiento de bits a derecha
Ejemplo:
83 = 0101 0011
206= 1100 1100
83^206= 1001 1101 =157
Estructuras de Control en C
Estas preposiciones de control de flujo especifican el orden en que
se realizan el procesamiento de datos dentro de los programas.
Hay Diferentes Tipos de Flujos de Control:
Estructuras Secuenciales (goto).
Estructuras Condicionales (if, switch)
Estructuras Repetitivas (for, while, do-while).
Estructuras de Salida (continue, break).
Estructuras Secuenciales.
El orden de Ejecución es una después de otra en el orden
especificado.
La sentencia GOTO tiene esta categoría. Es una instrucción
conocida en varios lenguajes clásicos de programación, rompe la
10
Manual del Alumno
secuencia de ejecución del programa bifurcándolo hasta el lugar
indicado por la etiqueta, esta instrucción ha sido duramente
criticada desde la aparición de la Programación Estructurada ya que
tiende a fomentar desorden en el programa, cosa que el Paradigma
Estructurado tiende a eliminar. No obstante es indudable que su uso
en algunas ocasiones no produce muchos problemas en la
comprensión de los programas.
goto salto;
Salto: n++ ;
Estructuras Condicionales.
Estructuras formadas por 1 o mas bloques secuenciales que se
inician y separan por una serie de palabras clave.
1.- La instrucción IF es utilizada para tomar decisiones entre 2 o
mas opciones, ejecutadas por el hecho de ser cierta o no las
expresiones a la que refiere el IF.
If (expresión)Preposición 1
Else Preposición 2
La construcción
If (expresión_1) Preposición_1
Else if (expresión_2)
Preposición_2
Else if (expresión_3)
Preposición_3
:
Establece un bloque de sentencias con opciones múltiples.
La sentencia if-else abreviada:
Expresión_condicional ? Preposiciõn_1: Preposición_2
Que equivale a que si se cumple la expresión condicional se ejecuta
la preposición 1 y en caso contrario la preposición 2.
Ejemplo a>b ? printf("a es mayor a b") : printf ("a es menor a b")
11
Manual del Alumno
2.-La Instrucciõn SWITCH es de decisión múltiple que prueba si
una expresión coincide con uno de un número de valores constantes
enteros y traslada el control adecuadamente.
switch (expresión) {
case expre_cte_1: preposición_1; break;
case expre_cte_2: preposición_2; break;
:
default: preposición_n;
}
"expresión" devuelve un valor tipo entero o constante de carácter.
"expre_cte" es un numero o una constante carácter. "break"es una
orden imperativa de salida de bloque puesto que ya en ese
momento se encontrõ un valor que coincidió. "default" indica que
si no se cumple ninguna de las opciones se ejecuta la preposición
por defecto. Esta instrucción de bloque switch es opcional, puede
escribirse o no. "case" es la Etiqueta de 1 o mas valores de
expresiones constantes.
La diferencia entre la estructura de control SWITCH y el IF:
El switch solo puede comprobar la igualdad.
Las constantes de tipo carácter se convierten
automáticamente a enteros.
No pueden existir 2 sentencias Case iguales en el mismo
switch.
El bloque switch es usado generalmente para la construcción de
menús en los programas.
Estructuras Repetitivas.
Estructuras formadas por 1 o mas bloques secuenciales cuya
ejecución se repite un número definido de veces especificado por
un numero ó variable.
1.-La instrucción FOR cae en esta categoría y tiene 3 formatos:
12
Manual del Alumno
for (inicialización ; Condición ; Incremento)
{
preposiciones;
}
"Inicialización" Normalmente es una sentencia de asignación cuya
variable se utiliza para el control del bucle.
"Condición" es una condición relacional que determina cuando
debe de finalizarse la ejecución del bucle. "Incremento" define
como varia la variable de control cada vez que se ejecuta el bucle.
Deacuerdo a los valores de inicialización, este incremento también
puede ser decrementado.
Eje 1:
For (i=1 ; i<10 ; i++)
{
printf ("\n El numero es : %d \n", i );
}
Eje 2:
For (i=100 ; i=1 ; i--)
{
printf ("\n El numero es : %d \n", i );
}
Cuando la estructura FOR inicia su ejecución, la variable de
inicialización empieza en 1 (Eje 1) la cual solo se evalúa una sola
vez, a continuación se verifica la condición y mientras esta no se
cumpla se ejecutaran las preposiciones, como paso siguiente la
variable de inicialización se modifica (incrementándose o
decrementandose) y el ciclo vuelve a empezar.
2.-La instrucción WHILE en C tiene el siguiente formato.
while( expresión_condicional)
{
13
Manual del Alumno
preposiciones;
}
Indican que mientras la expresión condicional sea verdadera se
llevan acabo las preposiciones. Dentro de las preposiciones de esta
estructura se necesita que una de ellas sea la de modificar la
variable (s) de la expresión condicional ya que si no se haria un
bucle infinito. Si desde un principio la expresión condicional es
falsa, las preposiciones no se ejecutaran ni una sola vez.
3.-La instrucción DO-WHILE tiene la siguiente sintaxis:
do
{preposiciones;
}
while(expresión_condicional);
En este caso las preposiciones se ejecutaran al menos una vez hasta
que llegan a la comprobación del bucle. El ciclo trabaja hasta que
la expresión condicional se vuelve falsa, es decir, trabaja mientras
la condición es cierta.
Estructuras de Salida.
Tienen como propósito salirse de ciclos y saltar sobre partes del
código del programa.
1.-La instrucción BREAK interrumpe la ejecución de un bucle
while, do-while, y for
while (cuenta<=100) {
preposición_1;
if (cuenta>5) break;
preposición_2
}
preposición_3;
14
Manual del Alumno
2.-La instrucción CONTINUE se utiliza en bucles para saltar el
resto del cuerpo del bucle, es decir, terminar la iteración.
For(x=1; x<=10; x++)
{if(x==4)
continue;
printf ("\n %d ", x);
}
Estructura de los Programas en C
Un Programa en C esta formado por 1 ó mas funciones que a su
vez, contienen una serie de elementos propios. Una de las
funciones que deben estar obligatoriamente presente en un
programa C es el main. Una función es un grupo de instrucciones
que realizan una tarea especifica en el programa, así mismo el
programa tendrá que contener una serie de directivas de
preprocesador ó archivos de cabecera escritos como # include<*.h>
que son archivos de librerÍas con extensión *.h que contienen la
declaración de variables y funciones que permiten el
funcionamiento y compilación de los programas.
Un ejemplo sencillo podría ser:
Una vez creado un programa en C como el anterior, se debe de
ejecutar compilándolo, y esto depende claro esta, del compilador
con el que trabaje, pero la siguiente gráfica da los pasos generales
para esto:
Un Programa en C contiene varios Elementos que a continuación
de Explican:
15
Manual del Alumno
1.-Tokens.
Existiendo 5 tipos de ellos:
1.1.-Identificadores. Secuencia de caracteres, letras, dígitos
y subrayados.
1.2.-Palabras Reservadas. Elementos que son utilizados
por el lenguaje exclusivamente para sus procesos.
PALABRA PALABRA PALABRA PALABRA
Asm Double New Switch
Auto Else Operator Template
Break Enum Private This
Case Extern Protcted Throw
Catch Float Public Try
Char For Register Typedef
Class Friend Return Union
Const Goto Short Unsigned
Continue If Signed Virtual
Default Inline Sizeof Void
Delete Int Statc Volatile
Do Long Struct while
1.3.-Comentarios. Tiene 2 formatos:
// Este es un comentario
/* Este es otro comentario */
16
Manual del Alumno
La diferencia entre ellos es que el comentario que empieza con //
solo abarca a una línea y los /*.... */ se extienden a varias líneas.
1.4.-Signos de Puntuación. Todas las sentencias deben de terminar con un punto y coma ; .
Otros signos de puntuación son: ¡ % ^ & * ( ) - + = { } ~ [ ] \ ; ' : <
> ?, . / "
1.5- Separadores. Son espacios en blanco, tabulaciones, retornos de carro y avances
de línea.
2.-Funciones.
C utiliza en sus programas las funciones de Biblioteca que están
declaradas en los archivos de cabecera (*.h), pero también utiliza
las funciones definidas por el usuario. Las funciones tienen el
propósito de realizar tareas especificas en el programa, las cuales
no deben de llevar acabo mas de 1 trabajo, si no, es recomendable
dividirla en otra función, y además no deben ser muy grandes por
que entonces pierden legibilidad y eficiencia. Cuando una función
es llamada, el control se pasa a la misma para llevar acabo su tarea
hasta que la termine, para después regresar de nuevo el control al
modulo que lo llamo. En C todas las funciones o son externas o
globales. En todo programa una función debe ser : Declarada,
Definida y Llamado ó invocada.
2.1-La Declaración de una función representa su prototipo,
y en C debe de estar declarada antes de utilizarla. El
17
Manual del Alumno
prototipo de una función es su cabecera terminada en un
punto y coma.
Tipo nombre_función(tipo_1 par_1, tipo_2 par_2 , ... );
Int suma( int x , int y );
Tipo: Especifica que tipo de variable devuelve la función.
Se puede establecer cualquier tipo de dato, pero
generalmente es el int. nombre_función : Identificador que
representa a la función, si el nombre va precedido de un *
entonces la función devuelve un apuntador. Una función no
devuelve arreglos o funciones. tipo_1 par_1, tipo_2 par_2 ,
... : Son llamados Parámetros o argumentos y son las
variables que recibe la función cada vez que es invocada. Si
no se pasan argumentos a la función, entonces esta línea se
sustituye por la palabra clave void. La definición de las
variables en C pueden estar situadas en cualquier parte del
programa con la condición de preceder a las instrucciones
que las utilizan. Los prototipos suelen situarse encima de la
función main().
2.2-La Definición de la función consiste en escribir las
instrucciones o tarea a realizar dentro del programa, la cual
debe de estar en alguna parte del mismo.
Dentro de la definición de las funciones, existe mucho la
instrucción "return" la cual tiene 2 funciones:
1.-Salir de la función y regresar el control a la parte de
código que la llamo.
2.-Devolver un valor acorde con el tipo que se ha indicado
al declarar la función.
18
Manual del Alumno
La instrucción void es también empleada para definir que una
función no devolverá ningún valor
2.3- El llamado de una función, en el lenguaje C es
transferir el control a esa función. Cuando se llama a esa
función, se proporciona un nombre de función y una lista de
parámetros, si es que los hay. Cuando se llama a una
función se realizan los siguientes pasos:
2.3.1-El compilador toma nota de la localización desde
donde se llamó la función y hace una copia de la lista de
parámetros en caso de haberlos.
2.3.2-Se crea temporalmente cualquier espacio de
almacenamiento que se requiere para ejecutar la función.
2.3.3-Comienza la ejecución de la función que se llama,
usando copias de los datos que se proporcionaron en la lista
de parámetros.
2.3.4-Despues de que la función termina la ejecución, se
regresa el control a la función que la llamo y se libera
memoria que se usó para la función.
La llamada a una función tiene el siguiente formato: Variable = expresión(parámetros actuales);
Variable: Especifica la variable en donde se guardara el valor
devuelto por la función.
Expresión : Especifica una dirección a una función. Puede ser una
expresión que es evaluada a una dirección de una función ó
simplemente un identificador que corresponde con el nombre de la
función llamada.
Parámetros actuales : Son una lista de expresiones separadas por
comas. Los valores resultantes son pasados a la función y
asignados a sus correspondientes parámetros formales. El numero
19
Manual del Alumno
de expresiones en la lista debe ser igual al número de parámetros
formales y del mismo tipo.
Ejemplos:
if(suma(13,45)<=0)
y=calcular(21,78,90);
Otra manera de llamar a las funciones es por el Paso de
Parámetros, si tenemos la siguiente función:
La llamada a la función seria: factorial (15); y se calcularía el
correspondiente numero factorial del número 15 por que ese es el
valor parámetro que le damos.
Un ejemplo completo de esto quedaría de la siguiente manera,
observe la manera en que se mandan llamar la función:
Una Consideración especial que se tiene que estudiar es a la
función main(), tan importante en la programación en C, ya que es
una función que engloba todo el corazón del programa en si,
además de que es muy útil para hacer mas funcional a los
programas y en el siguiente punto se estudiara.
2.4 "argc y argv" Argumentos o Parámetros de la función
main(). Algunas veces es de gran utilidad pasarle
información al programa escrito en C cuando tu lo corres, y
generalmente le pasas información al programa a la función
main() vía Argumentos en línea de Comandos. Un
argumento en línea de comandos es la información que
sigue del nombre del programa en la línea de comandos en
el sistema operativo. Hay 2 argumentos especiales que lo
implementan argc y argv, estos son usados para recibir la
información. El parámetro argc contiene el numero de
argumentos en la línea de comandos y es un entero, es
siempre por lo menos 1 por que el nombre del programa se
20
Manual del Alumno
considera como el primer argumento. El parámetro argv es
un puntero a un arreglo de punteros de tipo carácter. Cada
elemento en este arreglo de punteros corresponde a un
argumento en línea de comandos. Todos los argumentos en
líneas de comandos son strings.
Por ejemplo, este simple programa imprime "Hola" y tu nombre en
la pantalla si tu tecleas tu nombre después del nombre del
programa.
Se debe de declarar apropiadamente a argv y la forma mas común
de hacerlo es: char *argv[ ] ;
Los corchetes vacíos significa que el arreglo es de una longitud
indeterminada, y se puede accesar a los argumentos de manera
indexada, es decir, argv[0] tiene el primer argumento, argv[1] tiene
al segundo argumento y así continua.
Otro ejemplo simple de esta manera de manejo de información con
la función main() es el siguiente, llamado countdown.c :
El programa cuenta de forma descendente desde un valor dado (el
cual es especificado en la línea de comandos DOS), y suena en la
computadora un beep (es decir un pequeño sonido) cuando llega al
cero. Como todo argumento en las líneas de comandos son strings,
se debe de convertir el argumento a entero con la función atoi(), y
observen que si se da un segundo argumento o parámetro al
programa llamado display entonces se imprimirá el conteo
regresivo en la pantalla si no, no lo hará.
Usualmente se utilizan a argc y argv para obtener comandos
iniciales a tu programa, en teoría se pueden tener 32,767
parámetros, pero la mayoría de los sistemas operativos no te
permiten mas que unos pocos. Normalmente el uso de estos
argumentos es para dar nombres de archivos u opciones al
programa. El uso de parámetros en líneas de comandos dará a tus
21
Manual del Alumno
programas mas profesionalismo y facilidades para el uso de
archivos batch.
Cuando un programa en C no requiere de parámetros en líneas de
comandos, es practica común especificar a la función main() con la
instrucción void dentro de la lista de parámetros de la misma
función main(), es decir : main(void) ó simplemente dejar vacío
este espacio de parámetros es otra opción valida. Los nombres argc
y argv son los nombres comunes para estos parámetros pero se le
pueden poner los nombres que se deseen (no cambiando los tipos).
También algunos compiladores pueden soportar argumentos
adicionales pero asegúrate checando el manual de usuario.
3.-Alcance de las Variables.
Hasta ahora se ha visto la declaración de las variables, es decir, la
descripción de un tipo de variable o atributo. Sin embargo restan 2
problemas importantes que deben de discutirse: ¿Hasta que punto
una variable está asociada con un almacenamiento real (esto es
asignación de memoria)?, y ¿Hasta que punto puede referirse a una
variable particular un programa (esto es, cual es el alcance de las
variables)?. En C, se conocen a las variables y parámetros que se
declaran dentro de una función como variables automáticas. A éstas
se les asigna memoria cuando se llama a la función. Cuando
termina la ejecución de la función, se pierde la asignación. Por ello
las variables automáticas existen solo cuando esta activa la función.
Además se dice que las variables automáticas son Locales a la
función. Es decir, las variables automáticas se conocen sólo dentro
de la función donde están declaradas y otra función no puede
referirse a ellas. Las variables automáticas (es decir, parámetros en
el encabezado de una función o variables locales que siguen de
inmediato a cualquier llave de apertura) pueden declararse dentro
de cualquier bloque y existen hasta que éste se acaba. Puede
22
Manual del Alumno
referirse a la variable atraves de todo el bloque a menos que el
identificador de la misma se declare de nuevo en un bloque interno.
Dentro del bloque interno, una referencia al identificador es una
referencia a la declaración más interna y no puede hacerse
referencia a las variables de afuera. La segunda clase de variables
en C es la de las variables Externas. A las variables que se declaran
fuera de cualquier función se les asigna almacenamiento en el
primer punto de donde se les encuentra y existen para el resto de la
ejecución del programa. El alcance de una variable externa va
desde el punto en que se declaran hasta el final del archivo fuente
que lo contiene. Todas las funciones del archivo fuente pueden
referirse a este tipo de variable que estén más allá de su declaración
y, en consecuencia, se dice que son Globales para dichas funciones.
4.- Recursividad.
Esta es la propiedad que tienen las funciones en C++ de poder
llamarse a si mismas. Una función recursiva necesita de una
condición de Salida. El típico problema es el del calculo del
factorial de un número:
5.- Directivas de Preprocesador.
Instruyen al compilador para que realice ciertas tareas antes de que
la compilación del programa se active. La directiva mas común es
"#include" que le indica al preprocesador que incluya otro archivo
fuente C durante el proceso de compilación, y otra típica es
"#define" que es una definición de macros. Es una libertad del
programador en C poder crear sus propias librerías con extensión
*.h y luego incluirlas de manera general en sus programas con la
directiva #include. Como se ha mencionado anteriormente, los
23
Manual del Alumno
archivos de las directivas #include contienen la declaraciõn de
variables y funciones útiles para los programas en C, las mas
comunes (por que es un total de 450 funciones y macros) se
muestran en la sig. Tabla:
LIBRERÍA DESCRIPCIÓN
Conio.h Permite definir coordenadas de una ventana de
texto en pantalla y manipular texto.
Ctype.h Declara funciones para la comprobación de
caracteres.
Float.h Define constantes relacionadas con la aritmetica
de punto flotante
Graphic.h Permite la manipulación de Graficos
Limits.h Define constantes para el tamaño de los tipos de
enteros
Math.h Declara funciones y macros matematicas
Setjmp.h Proporciona una forma de evitar la secuencia
normal de llamada y regreso de funciones.
Signal.h DA facilidades para manejar condiciones
excepcionales que aparecen durante la ejecución
Stdio.h Permite leer y escribir datos de archivos y
dispositivos
Sdlib.h Declara fucniones para conversión númerica,
asignación de memoria.
String,h Devclara funciones para manipular cadenas como
arreglos de caracteres.
Time.h Declara funciones para la manipulación de fecha
y hora.
24
Manual del Alumno
Alloc.h Declara funciones para el manejo de la memoria.
Por ejemplo, en el siguiente programa se imprimirán cadena de
caracteres en la pantalla:
printf() es una de las funciones de Salida de caracteres que esta
definida en #include<stdio.h>, si yo no incluyera esta librería en el
programa habría error en la compilación, y así cada librería *.h
tiene definidas una serie de funciones especificas.
El Archivo de Cabecera Stdio.h
Cuando nos referimos a entrada/salida estándar (E/S estándar)
queremos decir que los datos o bien se están leyendo del teclado, ó
bien se están escribiendo en el monitor de video. Como se utilizan
muy frecuentemente se consideran como los dispositivos de E/S
por default y no necesitan ser nombrados en las instrucciones de
E/S. Esto le quedará claro a lo largo de este capítulo. El primer
código es el siguiente:
#include <stdio.h> /* define entrada/salida estandar */
int main() {
int c;
printf("Introduzca cualquier caracter y presione enter,"
"X = termina el programa.\n");
do
{
c = getchar(); /* Toma un caracter del teclado */
putchar(c); /* Despliega el caracter en el monitor */
}
while (c != 'X'); /* Mientras que sea diferente de X */
printf("\nFin del programa.\n");
return 0;
}
25
Manual del Alumno
La primera cosa por estudiar la tenemos en la primera línea. La
instrucción #include <stdio.h> es muy parecida a la instrucción
#define que ya hemos estudiado excepto que en lugar de una
simple sustitución se lee todo un archivo en este punto. El sistema
encontrará el archivo llamado stdio.h y leerá la totalidad del
contenido reemplazando la instrucción #include stdio.h.
Obviamente el archivo debe contener enunciados válidos de C que
pueden ser compilados como parte del programa. Este archivo en
particular contiene varias definiciones y prototipos para las
operaciones de E/S estándar. El archivo es llamado archivo de
cabecera y su compilador incluye una gran variedad de ellos, cada
uno con un propósito específico y cualquiera de ellos puede ser
incluido en cualquier programa. El compilador C utiliza el símbolo
de doble comilla para indicar que la búsqueda del archivo include
empieza en el directorio actual de trabajo, y si no se encuentra ahí,
la búsqueda continuará en el directorio include tal y como está
especificado en el ambiente de su compilador. Además se utilizan
los símbolos de "menor que" y "mayor que" para indicar que la
búsqueda empieza directamente en el directorio include del
compilador. Se pueden utilizar tantos include como sea necesario, y
es perfectamente válido que un archivo de cabecera incluya uno o
más archivos de cabecera adicionales. Comprenderá a su vez que
cuando escriba programas largos, es posible incluir ciertas rutinas
comunes en un archivo de cabecera e incluir el mismo como ya se
ha descrito.
Continuando con el código de ejemplo. Se define la variable
llamada c y se despliega un mensaje en pantalla con la yá conocida
función printf ( ), entramos en un bucle que no termina sino hasta
que el caracter introducido sea una X mayúscula, dentro del bucle
nos encontramos con dos nuevas funciones, una que sirve para leer
un caracter desde el teclado y otra que despliega dicho caracter en
pantalla. La función getchar ( ) lee un solo caracter desde el
26
Manual del Alumno
dispositivo estándar de entrada, o sea, el teclado, y lo asigna a la
variable llamada c. La siguiente función llamada putchar ( ) utiliza
el dispositivo de salida estándar, es decir, el monitor de video, para
desplegar el caracter contenido en c. El caracter se despliega en la
posición actual del cursor y éste avanza un espacio para el siguiente
caracter, por lo tanto el sistema se ocupa del orden de despliegue de
los caracteres.
Compile y ejecute este programa para descubrir algunas
características adicionales, como el hecho que conforme escriba en
el teclado, lo escrito se despliega en el monitor y al presionar la
tecla enter se repite la línea completa de texto, tal parece que
memoriza los caracteres y luego los vuelve a deplegar.
El Sistema Operativo a Nuestra Ayuda
Es conveniente dar una breve explicación de cómo trabaja el
sistema operativo para entender lo que pasa.
Cuando se leen datos desde el teclado bajo el control del sistema
operativo, los caracteres se almacenan en un buffer (segmento de
memoria RAM temporal) hasta que se introduce un retorno de
carro momento en el cual la totalidad de los caracteres se
devuelven al programa. Cuando se teclean los caracteres, éstos son
mostrados en pantalla uno a la vez. A ésto se le llama eco, y
sucedeo en muchas de las aplicaciones que Usted corre en su
computadora. Para demostrar lo dicho en el programa anterior,
teclee una serie continua de caracteres tal que contenga una X
mayúscula, verá que conforme vaya tecleando la 'X' aparece en
pantalla, pero una vez que presiona la tecla enter, la llegar la
cadena de caracteres al punto donde se encuentra la 'X', ahí termina
el programa, lo que sucede es que al presionar la tecla enter se
entrega al programa la cadena de caracteres, como el programa
señala su fin al encontrar una X mayúscula, los caracteres escritos
después de la 'X' no se despliegan en pantalla. Veamos otro código:
27
Manual del Alumno
#include "stdio.h"
#include "conio.h"
int main()
{
int c;
printf("Introduzca cualquier caracter, el programa"
"termina con una X\n");
do
{
c = _getch(); /* Se obtiene un caracter */
putchar(c); /* Se despliega la tecla presionada */
}
while (c != 'X');
printf("\nFin del programa.\n");
return 0;
}
Nuevamente empezamos con el archivo de E/S estándar de
cabecera, luego definimos una variable llamada c e imprimimos un
mensaje de bienvenida. Como en el programa anterior, entramos en
un bucle y lo ejecutamos hasta encontrar una X mayúscula, pero la
acción es un poco diferente, observe la inclusión del archivo
conio.h y de la función _getch ( ) mismos que no forman parte del
estándar ANSI-C pero que están disponibles en la mayoría de los
compiladores escritos para DOS. La función llamada _getch ( ) es
una función para obtener un caracter que difiere de la función
getchar ( ) en que no depende de DOS. Ësta función lee un caracter
sin hacer eco en pantalla entregando el caracter inmediatamente al
programa.
Tenga en cuenta que la función _getch ( ) no está incluida en el
estándar ANSI-C y por lo tanto puede no estar disponible en todos
los compiladores, además utilizar ésta función puede hacer el
progama menos portable a otras máquinas, si su compilador soporta
28
Manual del Alumno
la función, compile este programa y observe su funcionamiento
comparado con el programa anterior, verá que al presionar la tecla
enter no coloca una nueva línea con el retorno de carro, para
corregir esta situación tenemos el siguiente programa:
#include <stdio.h>
#include <conio.h>
#define RC 13 /* Define RC igual a 13 */
#define AL 10 /* Define AL igual a 10 */
int main()
{
int c;
printf("Introduzca cualquier caracter, X para terminar.\n");
do
{
c = _getch(); /* Se obtiene un caracter */
putchar(c); /* Despliega la tecla presionada */
if (c == RC) putchar(AL); /* Si es retorno de carro */
/* coloca una nueva linea */
}
while (c != 'X');
printf("\nFin del programa.\n");
return 0;
}
Tenemos dos nuevos enunciados que definen los códigos de
caracter para la nueva línea (linefeed en inglés, traducido en este
programa como "alimentar línea", AL), y para el retorno de carro
(carriage return, "retorno de carro", RC), si Usted consulta una
tabla de códigos ASCII verá por qué éstos términos se han definido
como 10 y 13. En el programa principal, después de desplegar el
caracter introducido en pantalla lo comparamos con RC, y si es
igual además desplegamos una nueva línea, AL.
29
Manual del Alumno
Le recomiendo ampliamente la compilación y ejecución de cada
programa de ejemplo para un mejor entendimiento de la mecánica
de las funciones aquí presentadas. Le toca ahora el turno a los
números enteros.
Entrada numérica
Estudie el siguiente programa:
#include <stdio.h>
int main()
{
int numero;
printf("Introduzca un numero de 0 a 32767, el programa"
"finaliza con un 100.\n");
do
{
scanf("%d", &numero); /* Lee un valor entero */
printf("El numero es %d\n", numero);
}
while (numero != 100);
printf("Adios!\n");
return 0;
}
La mecánica del programa es bastante similar a lo que hemos
estado trabajando, excepto que definimos una variable de tipo int
llamada numero y el bucle continua hasta que el valor introducido
sea 100. En lugar de leer un solo caracter tal y como lo hemos
hecho en los programas anteriores, ahora leemos un número
completo con una sola llamada a la función llamada scanf ( ), esta
función es muy similar a la conocida printf ( ) solo que se utiliza
para introducir datos en lugar de desplegarlos. Observe en la línea
donde se encuentra la función scanf ( ) que ésta no refiere
directamente a la variable llamada numero, en lugar de esto, utiliza
la dirección de la misma (o sea, un puntero a la variable), ya que
30
Manual del Alumno
espera le sea retornado un valor. La función scanf ( ) busca en la
línea de entrada hasta encontrar el primer campo de datos, lee los
caracteres enteros hasta encontrar un espacio en blanco ó un
caracter decimal inválido, en este punto detiene la lectura y retorna
el valor encontrado.
Si su sistema utiliza enteros de 2 bytes y Usted introduce un
número hasta 32767 inclusive éste se despliega correctamente, pero
con números mayores parece haber un error. Por ejemplo, si Usted
introduce 32768 se despliega -32768, e introduciendo 65536 el
valor desplegado es cero. La explicación de éste fenómeno está en
la manera en que está definido una variable de tipo int, el bit más
significativo para un patrón disponible de 16 bits para una variable
entera es el bit de signo por lo que sólo nos quedan 15 bits para
representar el valor, por lo tanto la variable sólo puede tomar
valores comprendidos entre -32768 y 32767. Este detalle debe
tomarlo en cuenta al momento de hacer sus programas. Lo dicho es
válido sólo para los compiladores de 16 bits, aunque existe la cada
vez mayor posibilidad de que su compilador utilice valores enteros
almacenados en campos mayores de 16 bits, en este caso se aplican
los mismos principios excepto que el rango de valores es mayor.
Compile y ejecute el programa anterior y experimente con lo
anteriormente dicho.
Entrada de Cadenas
Ahora veremos cómo introducir una cadena de caracteres en
nuestro siguiente programa, el código es el siguiente:
#include <stdio.h>
int main()
{
char cadena[25];
31
Manual del Alumno
printf("Introduzca una cadena de caracteres, maximo 25
caracteres.\n");
printf("Una X en la columna 1 termina el programa.\n");
do
{
scanf("%s", cadena);
printf("La cadena es -> %s\n", cadena);
}
while (cadena[0] != 'X');
printf("Adios!.\n");
return 0;
}
Este programa es similar al último código que estudiamos, excepto
que en lugar de definir una variable de tipo int, definimos una
variable de tipo string con un límite de 24 caracteres (recuerde que
las cadenas de caracteres deben incluir un caracter nulo al final de
la cadena). La variable en la función scanf ( ) no requiere un
símbolo de & porque cadena es un array y por definición incluye
un puntero. Este programa no requiere mayor explicación.
Cuando compile y ejecute éste programa notará que los enunciados
son separados en palabras. Cuando scanf ( ) se utiliza en el modo
de entrada de cadena de caracteres lee los caracteres hasta que
encuentra el final de la linea ó un caracter en blanco, por lo tanto,
lee una palabra a la vez. Experimente introduciendo más de 24
caracteres y observe cómo el sistema maneja una situación de error.
Como scanf ( ) no tiene manera de parar la introducción de
caracteres cuando el array está lleno, por lo tanto no lo utilice para
introducir cadenas de caracteres en un programa importante, aquí lo
usamos solamente para propósitos de ilustración.
32
Manual del Alumno
Entrada/Salida en Memoria
Hablemos ahora de otro tipo de E/S, uno que no tiene salida al
mundo exterior pero que permanece en la computadora, el código
es este:
#include <stdio.h>
int main()
{
int numeros[5], resultado[5], indice;
char linea[80];
numeros[0] = 74;
numeros[1] = 18;
numeros[2] = 33;
numeros[3] = 30;
numeros[4] = 97;
sprintf(linea,"%d %d %d %d %d\n", numeros[0], numeros[1],
numeros[2], numeros[3], numeros[4]);
printf("%s", linea);
sscanf(linea,"%d %d %d %d %d", &resultado[4], &resultado[3],
(resultado+2), (resultado+1), resultado);
for (indice = 0 ; indice < 5 ; indice++)
printf("El resultado final es %d\n", resultado[indice]);
return 0;
}
En este programa definimos algunas variables, después asignamos
algunos valores a las llamadas números para propósitos de
ilustración y entonces utilizamos la función sprintf ( ), ésta actúa
similar a la función printf ( ) pero en lugar de desplegar los datos a
un dispositivo de salida, imprime la cadena formateada en una
cadena en memoria. En este caso la cadena vá a la variable llamada
linea, porque esta es la cadena que introducimos como primer
argumento de la función sprintf ( ). Como la cadena generada
continúa en memoria, podemos leerla utilizando la función sscanf (
33
Manual del Alumno
), le decimos a la función en el primer argumento que linea es la
cadena a utilizar para su entrada, el resto de los argumentos se
manejan igual que en la función scanf ( ). Observe que en este caso
si utilizamos punteros porque necesitamos regresar datos de la
función y observe además que utilizamos varias formas para
declarar punteros, las primeras dos simplemente declaran la
dirección de los elementos del array, mientras que los últimos tres
aprovechan el hecho que resultado, sin el subíndice, es un puntero.
Finalmente y para agregarle más interés, los datos se despliegan en
orden inverso.
¡ Crea Tus Propias Librerías *.h de C !.
Se ha visto hasta ahorita que las librerías de C son archivos con la
extensión *.h, y que su objetivo es proporcionar al momento de la
compilación la declaración de variables y funciones de todo tipo
que lleva a cabo las instrucciones generales de programas,
Recordando que para utilizar por ejemplo la función que imprime
strings en la pantalla "printf()" se tiene que incluir la librería
stdio.h, por que si no, el compilador reportara que tal función no
esta declarada, y asi de esta manera, de forma general.
Tambien, se ha explicado en el trancurso de este tutorial que tales
librerías estan de manera estandar dentro del lenguaje C, tan solo
hay que mandarlas llamar, sin embargo se tiene la gran posibilidad
de como programador poder crear Nuestras propias librerías con el
código propio relacionados con nuestros programas en donde las
mandemos llamar con la linea #include<nombre_de_libreria.h>
Esto de poder crear nuestras propias librerías tiene 2 objetivos
claros en el momento de la creación de un programa:
Evita tener en un solo código toda la declaración de
variables y funciones que hacen muy grande al código
34
Manual del Alumno
fuente principal, esto propicia un orden especifico y
eficiencia en el código final del programa por la razón de no
quedar tan grande.
Permite Tener un control mas preciso de tales variables y
funciones en el trancurso de la creación del programa, asi
de poder tener un acceso facíl al momento de
modificaciones futuras.
Puntos ha tomar muy encuenta al momento de crear librerías son los siguientes:
Tomese en consideración que una libreria NO ES UN
PROGRAMA, es tan solo la declaración de variables y
funciones, NADA MAS.
Por la razón de que no es un programa, DE NINGUNA
FORMA tiene que estar en ningun tipo de librería la
función MAIN (), ya que esta solo tiene que estar presente
en el código principal del programa.
NUNCA tiene que compilarse una librería
Habiendo considerado lo anterior, proceda de la siguiente manera para la creación de su librería:
1.- Tiene que usar cualquier tipo de Editor de Textos, puede ser el
mismo Turbo C++, o incluso el NotePad o Edit de DOS creando un
Archivo nuevo.
2.- Haga la declaración de sus funciones y Variables tal y como lo
haría en el programa *.c o *.cpp y que se utilizaran al momento de
compilar su programa.
3.- Al momento de Salvar o guardar su Archivo lo hará con la
extensión *.h y Listo.
Hecho Lo anterior debe quedar claro que ya no es necesario
declarar funciones y variables en su código principal, esto marcaria
un error de redeclaración de variables o funciones.
35
Manual del Alumno
AHORA! la manera de incluirlos en su programa es de la siguiente forma:
1.- Al momento de agregar tu librería al programa, se utiliza la
misma instrucción de preprocesador
#include<nombre_de_libreria>, nada mas que ahora se tiene que
especificar al compilador que esta librería en especial la tiene que
leer desde tu diskette o Disco Duro, por lo tanto lo especificas asi
#include<nombre_de_unidad:nombre_de_libreria.h>. Por ejemplo
una llamada a una libreria propia podría ser
#include<a:mi_mouse.h>, asi especifico que mi librería
mi_mouse.h la tiene que leer del diskette de la unidad a: (a: es la
unidad mas común para la disquetera, pero si cambia, por ejemplo
a b: no importa, coloquela de la manera indicada)o puede ser c: ,
siendo esta la común para el DD.
2.- Al momento de especificar la unidad de almacenamiento donde
se encuentra mi librería *.h se tiene la posibilidad tambien de
especificar un path o camino donde se encuentra mi librería. Por
ejemplo:
#include<a:\libreria\mi_mouse.h>
Y de esta manera el compilador buscara en la unidad a: en el
directorio libreria el archivo mi_mouse.h, y asi se pueden
especificar incluso subdirectorios subsecuentes.
3.- Algunos programadores de C estilan para diferenciar entre las
librerías de C estandar y las que ellos han creado declarar sus
propias librerías de la siguiente forma:
#include"a:\libreria\mi_mouse.h"
es decir, cambian los simbolos de mayor que > y menor que < por
dobles comillas, lo cual es valido, el compilador lo reconoce sin
ningún problema, pero dejarlos con la forma tradicional no marca
ningún problema, es tan solo formas y estilos propios de cada
programador.
36
Manual del Alumno
4.-Se puede No especificar una unidad de almacenamiento al
momento de llamar a la nuestras librerías siempre y cuando mi
archivo fuente *.c o *.cpp y el archivo *.h se encuentren en el
mismo subdirectorio /BIN de Turbo C++, es decir se hallen en
/TC/BIN, es decir, que en el programa principal solo especifique
#include<mi_mouse.h> .Pero si yo no guardo ahi mis archivos SI
tendre que especificarle unidad de almacenamiento y subdirectorios
(Si los hay) a la directiva #include<> al momento de llamar mis
propias librerías
Por el Hecho de manejar de una manera separada código de un
programa principal (es decir el *.h) TENGA CUIDADO de no
perder o dañar este archivo, si no, no se compilara su programa, y
tedrá que de manera detallada volver a encontrar las variables y
funciones utilizados en su programa y volverlas a declarar en un
nuevo archivo *.h . Y por Ultimo siempre tenga al momento de
compilar su programa con librerías hechas por Usted, el diskette en
su unidad lista, para que pueda tener acceso a la librería el
compilador.
Nunca trate de Editar las librerías que son estandar del lenguaje C,
como tratar de editar el stdio.h agregando mis propias variables o
funciones, por que entonces el Compilador no Reconocera nunca
mas a la librerí aunque se deshagan las modificaciones hecha por
mi.Y siempre tenga en cuenta que las instrucciones anteriores son
solo para la creación y uso de MIS PROPIAS librerías, el uso de las
librerías estandar de C son de la manera especificada en otras
secciones del tutorial.
Archivos en C
El Almacenamiento de datos en estructuras de datos vistas
anteriormente solo se generan temporalmente, es decir, cuando
termina el programa los datos se pierden. Para ello, una forma de
37
Manual del Alumno
no perderlos y recuperarlos aun cuando el programa termine es con
el manejo de Archivos.
C ve cada uno de los archivos simplemente como un grupo
secuenciales de bytes. Cada archivo termina con un marcador de
fin de archivo o un número de bytes especificado registrado en una
estructura de datos.
Las funciones para manipular los Archivos se encuentran en el
archivo de cabecera <stdio.h> y vale la pena mencionarlos:
FUNCIÓN DESCRIPCIÓN
fopen() Abre un flujo
fclose() Cierra un flujo
Putc Escribe un carácter en un flujo
getc() Lee un carácter desde un flujo
fputs() Escribe una cadena en un flujo
fgets() Obtiene una cadena de un flujo
fseek() Salta al byte especificado en un flujo
fprintf() Imprime datos con formato en un flujo
fscanf() Lee datos con formato en un flujo
eof() Devuelve verdadero o falso si se halla el fin del
archivo.
Los flujos dan canales de comunicación entre el archivo y el
programa. Por ejemplo, el flujo de entrada estándar permite que un
programa lea datos desde el teclado, el flujo de salida estándar
permite que un programa imprima datos en la pantalla. Un flujo se
relaciona con un archivo utilizando una operación de apertura y se
desliga del archivo con una operación de cierre.
Existen 2 tipos de flujos: Texto y binarios.
38
Manual del Alumno
El flujo de Texto se utiliza con conversiones de Caracteres. Es
decir, puede no haber correspondencia entre lo que se envía al flujo
y lo que se escribe en el archivo.
El flujo Binario tiene una correspondencia directa entre lo que se
envía y se escribe en el archivo.
Un archivo es el grupo de información que están en una entidad
física real que recibe los datos, ya sea DD o diskette.
Abrir un Archivo puede ser para varios propósitos: lectura,
escritura o ambos, por lo que se utiliza la siguiente función para
ello:
FILE fopen(char *f,char *modo); que es lo mismo que
fopen (Nombre del Archivo, Modo);
Los modos de Abrir al archivo se especifican en la siguiente tabla:
ACCES
O
DESCRIPCIÓN.
"r" Abre un archivo para lectura. El archivo debe de existir
"w" Abre para escritura. Si el archivo no existe se crea, pero si existe se borra su
información para crearla de nuevo.
"a" Abre para escribir al final de el. Si el archivo no existe se crea.
"r+" Abre para Escritura/Lectura. El archivo debe existir.
"w+" Abre para Escritura/Lectura. Si el archivo no existe se crea, pero si existe su
información se destruye para crearla de nuevo.
"a+" Abre para escribir al final de el y leer. Si el archivo no existe se crea.
"rb" Abre un archivo binario para lectura. El archivo debe existir.
"wb" Abre un archivo binario para escritura. Si el archivo no existe se crea, pero si existe
su información se destruye para crearla de nuevo.
"ab" Se abre un archivo binario para escribir al final de el. Si el archivo no existe se crea.
"rb+" Abre un archivo binario para lectura/Escritura. El archivo debe existir
"wb+" Abre un archivo binario para lectura/escritura. Si el archivo no existe se crea, pero si
39
Manual del Alumno
existe su información se destruye para crearla de nuevo.
"ab+" Abre un archivo binario para lectura y añadir. Si el archivo no existe se crea.
Cerrar un Archivo después de trabajar es algo que debe de
hacerse, si no se hace este se cierra automáticamente cuando
finaliza el programa. Se cierra de la siguiente manera:
fclose(FILE *f)
Ejemplo de programa que Escribe y lee carácter por carácter de un
archivo las instrucciones usadas son putc y getc. Observe el manejo
de fopen y fclose en los siguientes ejemplos.
Int putc(int c, FILE*f); Escribe el carácter c en un archivo apuntado
por f
Int getc(FILE *f); Devuelve el carácter leído e incrementa la
posición actual del indicador del archivo, si se detecta el fin del
archivo se devuelve EOF.
FILE *apuntador establece que "apuntador" es un apuntador a la
estructura FILE. La forma de Escribir y leer la información por
medio de strings es por medio de las instrucciones fgets y fputs. Int
fputs(char *cad, FILE *f);Escribe una cadena de caracteres aun
archivo apuntado por f. Int fgets(char *cad, int n, FILE *f); Lee una
cadena de Caracteres.
Las instrucciones fread y fwrite se utilizan en archivos en el que se
utilizan estructuras para definir la longitud de cada record. Size_t
fwrite(void *buffer,size_t num_bytes,size_t n, FILE *f); Escribe n
registros de longitud num_bytes desde la memoria apuntada por
buffer al archivo f. Size_t fread(void *buffer,size_t
num_bytes,size_t n, FILE *f); Lee n registros de num_bytes en la
memoria apuntada hacia buffer desde el archivo f.
40
Manual del Alumno
Gráficos en C
Las funciones que llevan acabo los gráficos en C es permitir
generar dibujos y presentaciones de los programas en general (es
decir, la forma en que lucen al usuario). Las funciones de salida
dependen del adaptador y del monitor que se este utilizando. El
controlador seleccionado se carga desde el disco durante la
inicialización de la biblioteca de gráficos llamado initgraph(), y se
necesita incluir la librería #include<graphics.h>
Para Activar el Modo Gráfico un Programa, este debe de
empezar llamando a la función initgraph(), que inicializa el paquete
de gráficos BGI.
Void far initgraph(far *controlador, int far *modo, char far
*camino)
Se carga en la memoria el controlador de gráficos correspondiente
al numero determinado por el controlador. El parámetro modo
apunta a un entero que especifica el modo de vídeo que van a usar
las funciones de gráficos. El parámetro camino especifica el
directorio donde se encuentra el controlador. Si no se especifica se
busca en el directorio actual. Los archivos BGI deben de estar
disponibles en el sistema ya que contienen los controladores
graficos, sin embargo no es necesario preocuparse por el nombre
real del archivo, ya que el controlador queda especificado
solamewnte con su número, para ello graphics.h tiene varias
macros para esto:
MACRO EQUIVALE A...
DETECT 0
CGA 1
MCGA 2
EGA64 4
EGAMONO 5
IBM8514 6
41
Manual del Alumno
HERCMONO 7
ATT400 8
VGA 9
PC3270 10
Al usar DETECT, initgraph() detecta automáticamente el tipo de
HW de vídeo presente en el sistema y selecciona el modo de vídeo
con la resolución adecuada.
Para Desactivar el modo gráfico del programa se usa la función
void closegraph(void) que implica la devolución al sistema de la
memoria que se utilizaba para tener los controladores y las fuentes
gráficas en uso.
La Escritura en Modo Gráfico se puede llevar acabo con las
funciones especiales para ello:
Void far outtext(char far *cadena de texto );
Muestra en una pantalla en modo gráfico una cadena de texto en la
posición actual (en los modos gráficos no existe un cursor visible,
pero la posición actual se conserva como si existiera uno invisible).
Void far outtextxy(int x,int y, char far *cad)
Escribe un texto en la posición determinada por las coordenadas
x,y en la ventana gráfica.
Para Cambiar el tipo de letra se usa la función:
Void far settextstyle(int fuente, int direccion, int tamaño_car);
Establece la fuente activa que usaran las funciones de escritura de
texto grafico, tambien se establece la dirección y el tamaño de los
caracteres. El parametro fuente determina el tipo de fuente que se
va a utilizar. Fuente puede tomar algunos de los siguientes valores:
MACRO VALOR TIPO FUENTE
DEFAULT_FONT 0 Mapa de bits 8x8.
TRIPLEX_FONT 1 Fuente trazada triple.
SMALL_FONT 2 Fuente trazada pequeña.
SANS_SERIF_FONT 3 Fuente trazada sans serif.
42
Manual del Alumno
GOTHIC_FONT 4 Fuente trazada gótica.
El parámetro dirección refiere a la forma de desplegarse, ya sea de
izquierda a derecha o de arriba hacia abajo, se determina mediante
el valor HORIZ_DIR (0) o VERT_DIR (1).
El parámetro tamaño es un multiplicador que aumenta el tamaño de
los caracteres. Puede tomar valores de 0 a 10.
Cabe mencionar que las coordenadas de pantalla modo texto y
gráfico cambian de la siguiente manera:
Así que tome en cuenta estas gráficas para modelar sus
coordenadas al momento de escribir textos.
Programación Orientada a Objetos con C++
INTRODUCCIÓN.
Actualmente una de las áreas más candentes en la industria y en el
ámbito académico es la orientación a objetos. La orientación a
objetos promete mejoras de amplio alcance en la forma de diseño,
desarrollo y mantenimiento del software ofreciendo una solución a
largo plazo a los problemas y preocupaciones que han existido
desde el comienzo en el desarrollo de software: la falta de
portabilidad del código y reusabilidad, código que es dificil de
modificar, ciclos de desarrollo largos y tecnicas de codificacion no
intuituvas.
Un lenguaje orientado a objetos ataca estos problemas. Tiene tres
características basicas: debe estar basado en objetos, basado en
clases y capaz de tener herencia de clases. Muchos lenguajes
cumplen uno o dos de estos puntos; muchos menos cumplen los
tres. La barrera más difícil de sortear es usualmente la herencia.
43
Manual del Alumno
El concepto de programación orientada a objetos (OOP) no es
nuevo, lenguajes clásicos como SmallTalk se basan en ella. Dado
que la OOP. se basa en la idea natural de la existencia de un mundo
lleno de objetos y que la resolución del problema se realiza en
términos de objetos, un lenguaje se dice que está basado en objetos
si soporta objetos como una característica fundamental del mismo.
El elemento fundamental de la OOP es, como su nombre lo indica,
el objeto. Podemos definir un objeto como un conjunto complejo
de datos y programas que poseen estructura y forman parte de una
organización.
Esta definición especifica varias propiedades importantes de los
objetos. En primer lugar, un objeto no es un dato simple, sino que
contiene en su interior cierto nómero de componentes bién
estructurados. En segundo lugar, cada objeto no es un ente aislado,
sino que forma parte de una organización jerárquica o de otro tipo.
Beneficios que se obtienen del desarrollo con OOP
Día a día los costos del Hardware decrecen. Así surgen nuevas
áreas de aplicación cotidianamente: procesamiento de imágenes y
sonido, bases de datos multimediales, automatización de oficinas,
ambientes de ingeniería de software, etc. Aún en las aplicaciones
tradicionales encontramos que definir interfases hombre-máquina
"a-la-Windows" suele ser bastante conveniente.
Lamentablemente, los costos de producción de software siguen
aumentando; el mantenimiento y la modificación de sistemas
complejos suele ser una tarea trabajosa; cada aplicación, (aunque
tenga aspectos similares a otra) suele encararse como un proyecto
nuevo, etc.
Todos estos problemas aún no han sido solucionados en forma
completa. Pero como los objetos son portables (teóricamente)
mientras que la herencia permite la reusabilidad del código
orientado a objetos, es más sencillo modificar código existente
44
Manual del Alumno
porque los objetos no interaccionan excepto a través de mensajes;
en consecuencia un cambio en la codificación de un objeto no
afectará la operación con otro objeto siempre que los métodos
respectivos permanezcan intactos. La introducción de tecnología de
objetos como una herramienta concepual para analizar, diseñar e
implementar aplicaciones permite obtener aplicaciones más
modificables, fácilmente extendibles y a partir de componentes
reusables. Esta reusabilidad del código disminuye el tiempo que se
utiliza en el desarrollo y hace que el desarrollo del software sea
mas intuitivo porque la gente piensa naturalmente en términos de
objetos más que en términos de algoritmos de software.
Problemas derivados de la utilización de OOP en la actualidad
Un sistema orientado a objetos, por lo visto, puede parecer un
paraíso virtual. El problema sin embargo surge en la
implementación de tal sistema. Muchas compañías oyen acerca de
los beneficios de un sistema orientado a objetos e invierten gran
cantidad de recursos luego comienzan a darse cuenta que han
impuesto una nueva cultura que es ajena a los programadores
actuales. Específicamente los siguientes temas suelen aparecer
repetidamente:
Curvas de aprendizaje largas. Un sistema orientado a objetos ve al
mundo en una forma única. Involucra la conceptualización de todos
los elementos de un programa, desde subsistemas a los datos, en la
forma de objetos. Toda la comunicación entre los objetos debe
realizarse en la forma de mensajes. Esta no es la forma en que están
escritos los programas orientados a objetos actualmente; al hacer la
transición a un sistema orientado a objetos la mayoría de los
programadores deben capacitarse nuevamente antes de poder
usarlo.
45
Manual del Alumno
Dependencia del lenguaje. A pesar de la portabilidad conceptual de
los objetos en un sistema orientado a objetos, en la práctica existen
muchas dependencias. Muchos lenguajes orientados a objetos están
compitiendo actualmente para dominar el mercado. Cambiar el
lenguaje de implementación de un sistema orientado a objetos no es
una tarea sencilla; por ejemplo C++ soporta el concepto de
herencia multiple mientras que SmallTalk no lo soporta; en
consecuencia la elección de un lenguaje tiene ramificaciones de
diseño muy importamtes.
Determinacion de las clases. Una clase es un molde que se utiliza
para crear nuevos objetos. En consecuencia es importante crear el
conjunto de clases adecuado para un proyecto. Desafortunadamente
la definición de las clases es más un arte que una ciencia. Si bien
hay muchas jerarquías de clase predefinidas usualmente se deben
crear clases específicas para la aplicación que se este desarrollando.
Luego, en 6 meses ó 1 año se da cuenta que las clases que se
establecieron no son posibles; en ese caso será necesario
reestructurar la jerarquía de clases devastando totalmente la
planificación original.
Performance. En un sistema donde todo es un objeto y toda
interaccion es a través de mensajes, el tráfico de mensajes afecta la
performance. A medida que la tecnología avanza y la velocidad de
microprocesamiento, potencia y tamaño de la memoria aumentan,
la situacion mejorará; pero en la situación actual, un diseño de una
aplicación orientada a objetos que no tiene en cuenta la
performance no será viable comercialmente.
Idealmente, habría una forma de atacar estos problemas
eficientemente al mismo tiempo que se obtienen los beneficios del
desarrollo de una estrategia orientada a objetos. Deberia existir una
metodología fácil de aprender e independiente del lenguaje, y facil
de reestructurar que no drene la performance del sistema .
46
Manual del Alumno
Objetos
Las unidades básicas de construcción, para conceptualización,
diseño o programación, son instancias organizadas en clases con
características comunes. Estas características comprenden los
atributos y procedimientos, denominados operaciones o métodos.
Los objetos deben estar basados, hasta donde sea posible, en
entidades del mundo real y en conceptos de la aplicación o
dominio. Los objetos pueden ser clases o instancias, aunque
algunos autoridades en la materia utilizan el término objeto como
sinónimo de instancia. El estándar OMG es denominar las
descripciones de clases como tipos de objetos y se emplea el
término clase para referirse a su realización (implementación).
Un objeto es una abstracción encapsulada que tiene un estado
interno como dado por una lista de atributos cuyos valores son
únicos al objeto. El objeto también conoce la lista de mensajes al
que puede responder y como responderá en cada uno.
Así mismo un objeto puede modelar muchas cosas diferentes.
Algunos tipos más comunes son como se indican a continuación.
Objetos de dispositivo - Modela dispositivos individuales.
Objetos de propiedad - Modela importantes propiedades o
características de la aplicación del dominio
Objetos de roles - Modela personas y los roles que
desempeñan en el sistema
Objetos de organización - Modela la organización
Objetos de sucesos - Modelar los sucesos
Objetos de interacción - Modela las interacciones entre
otros objetos
Explícitamente los objetos pueden ser también:
· Entidades externas que producen o consumen información a
ser utilizada en el sistema basado en computadora
· Cosas que son parte del dominio de información del problema
47
Manual del Alumno
· Ocurrencia o sucesos que ocurren en el contexto de operación
del sistema
· Papeles que juegan las personas que interactúan con el
sistema
· Unidades organizativas que son relevantes para la aplicación
· Lugares que establecen el contexto del problema y del
funcionamiento general del sistema
· Estructuras que definen clases de objetos o, en casos
extremos, clases de objetivos relacionadas
Ejemplos de objetos.
ventanas, iconos, arreglos, pilas, colas, árboles binarios, números
complejos. Los datos de un objetos están ocultos y solamente a
través de sus funciones pueden ser accesados.
Todos los campos miembros y funciones están completamente en
el interior del objeto y son ocultos desde el exterior, lo que
significa que están encapsulados. A las funciones de un objeto también se les conoce como métodos
y a los datos como variables.
Estructura de un Objeto.
Un objeto puede considerarse como una especie de cápsula
dividida en tres partes:
1 - RELACIONES
2 - PROPIEDADES
3 - METODOS
Cada uno de estos componentes desempeña un papel totalmente
independiente:
Las relaciones permiten que el objeto se insterte en la organización
y están formadas esencialmente por punteros a otros objetos.
48
Manual del Alumno
Las propiedades distinguen un objeto determinado de los restantes
que forman parte de la misma organización y tiene valores que
dependen de la propiedad de que se trate. Las propiedades de un
objeto pueden ser heredadas a sus descendientes en la organización.
Los métodos son las operaciones que pueden realizarse sobre el
objeto, que normalmente estarán incorporados en forma de
programas (código) que el objeto es capaz de ejecutar y que
también pone a disposición de sus descendientes a través de la
herencia.
Organización de los Objetos
En principio, los objetos forman siempre una organización
jerárquica, en el sentido de que ciertos objetos son superiores a
otros de cierto modo.
Existen varios tipos tipos de jerarquías: serán simples cuando su
estructura pueda ser representada por medio de un "arbol". En otros
casos puede ser más compleja. En cualquier caso, sea la estructura simple o compleja, podrán
distinguirse en ella tres niveles de objetos.
-La raíz de la jerarquía. Se trata de un objeto único y especial. Este
se caracteríza por estar en el nivel más alto de la estructura y suele
recibir un nombre muy genérico, que indica su categoría especial,
como por ejemplo objeto madre, Raíz o Entidad.
-Los objetos intermedios. Son aquellos que descienden
directamente de la raíz y que a su vez tienen descendientes.
Representan conjuntos o clases de objetos, que pueden ser muy
generales o muy especializados, según la aplicación. Normalmente
reciben nombres genéricos que denotan al conjunto de objetos que
representan, por ejemplo, VENTANA, CUENTA, FICHERO. En
un conjunto reciben el nombre de clases o tipos si descienden de
otra clase o subclase.
49
Manual del Alumno
-Los objetos terminales. Son todos aquellos que descienden de una
clase o subclase y no tienen descendientes. Suelen llamarse casos
particulares, instancias o ítems porque representan los elementos
del conjunto representado por la clase o subclase a la que
pertenecen.
Programación Orientada a Objetos .
Como Definición formal podremos decir que la programación
orientada a objetos (POO) es una técnica o estilo de programación
que utiliza objetos como bloque esencial de construcción.
Programas Orientados a Objetos .
Un programa orientado a objetos es una colección de clases.
Necesita de una función principal que cree objetos y comience la
ejecución mediante la invocación de sus funciones o métodos.
En primer lugar, se crean los objetos. Segundo, los mensajes se
envían desde unos objetos y se reciben en otros a medida que el
programa se ejecuta. Y tercero, se borran los objetos cuando ya no
son necesarios y se recupera la memoria ocupada por ellos.
Los objetos son tipos de datos abstractos definidos por el
programador. En realidad son unidades que contienen datos y
funciones que operan sobre esos datos. A los objetos también se les
conoce como instancias de clase.
A los elementos de un objeto se les conoce como miembros (datos
miembros y funciones miembro).
1. RELACIONES Las relaciones entre objetos son, precisamente, los enlaces que
permiten a un objeto relacionarse con aquellos que forman parte de
la misma organización.
Las hay de dos tipos fundamentales:
50
Manual del Alumno
-Relaciones jerárquicas. Son esenciales para la existencia misma
de la aplicación porque la construyen. Son bidireccionales, es decir,
un objeto es padre de otro cuando el primer objeto se encuentra
situado inmediatamente encima del segundo en la organización en
la que ambos forman parte; asimismo, si un objeto es padre de otro,
el segundo es hijo del primero (en la fig. 2, B es padre de D,E y F,
es decir, D,E y F son hijos de B; en la fig. 3, los objetos B y C son
padres de F, que a su vez es hijo de ambos).
Una organización jerárquica simple puede definirse como aquella
en la que un objeto puede tener un solo padre, mientras que en una
organizacion jerárquica compleja un hijo puede tener varios
padres).
-Relaciones semánticas. Se refieren a las relaciones que no tienen
nada que ver con la organización de la que forman parte los objetos
que las establecen. Sus propiedades y consecuencia solo dependen
de los objetos en sí mismos (de su significado) y no de su posición
en la organización.
Se puede ver mejor con un ejemplo: supongamos que vamos a
construir un diccionario informatizado que permita al usuario
obtener la definición de una palabra cualquiera. Supongamos que,
en dicho diccionario, las palabras son objetos y que la organización
jerárquica es la que proviene de forma natural de la estructura de
nuestros conocimientos sobre el mundo.
La raíz del diccionario podría llamarse TEMAS. De éste término
genérico descenderán tres grandes ramas de objetos llamadas
VIDA, MUNDO y HOMBRE. El primero (vida) comprenderá las
ciencias biológicas: Biología y Medicina. El segundo (mundo), las
ciencias de la naturaleza inerte: las Matemáticas, la Física, la
Química y la Geología. El tercero (hombre) comprenderá las
ciencias humanas: la Geografía, la Historia, etc.
Veamos un ejemplo: estableceremos la relación trabajo entre los
objetos NEWTON y OPTICA y la interpretaremos diciendo que
51
Manual del Alumno
significa que Newton trabajó en óptica (véase la fig. 4). La relación
es, evidentemente, semántica, pués no establece ninguna
connotación jerárquica entre NEWTON y OPTICA y su
interpretación depende exclusivamente del significado de ambos
objetos.
La existencia de esta relación nos permitirá responder a preguntas
como:
¿Quién trabajó en óptica?
¿En qué trabajó Newton?
¿Quien trabajó en Física?
Las dos primeras se deducen inmediatamente de la existencia de la
relación trabajo. Para la tercera observamos que si Newton trabajó
en óptica automáticamente sabemos que trabajó en Física, por ser
óptica una rama de la Física (en nuestro diccionario, el objeto
OPTICA es hijo del objeto FISICA). Entonces gracias a la OOP
podemos responder a la tercera pregunta sin necesidad de
establecer una relación entre NEWTON y FISICA, apoyandonos
sólo en la relación definida entre NEWTON y OPTICA y en que
OPTICA es hijo de FISICA. De este modo se elimina toda
redundancia innecesaria y la cantidad de información que
tendremos que definir para todo el diccionario será mínima.
2. PROPIEDADES Todo objeto puede tener cierto número de propiedades, cada una de
las cuales tendrá, a su vez, uno o varios valores. En OOP, las
propiedades corresponden a las clásicas "variables" de la
programación estructurada. Son, por lo tanto, datos encapsulados
dentro del objeto, junto con los métodos (programas) y las
relaciones (punteros a otros objetos). Las propiedades de un objeto
pueden tener un valor único o pueden contener un conjunto de
valores mas o menos estructurados (matrices, vectores, listas, etc.).
52
Manual del Alumno
Además, los valores pueden ser de cualquier tipo (numérico,
alfabético, etc.) si el sistema de programación lo permite.
Pero existe una diferencia con las "variables", y es que las
propiedades se pueden heredar de unos objetos a otros. En
consecuencia, un objeto puede tener una propiedad de maneras
diferentes:
-Propiedades propias. Están formadas dentro de la cápsula del
objeto.
-Propiedades heredadas. Estan definidas en un objeto diferente,
antepasado de éste (padre,"abuelo", etc.). A veces estas
propiedades se llaman propiedades miembro porque el objeto las
posee por el mero hecho de ser miembro de una clase.
3. METODOS Una operación que realiza acceso a los datos. Podemos definir
método como un programa procedimental o procedural escrito en
cualquier lenguaje, que está asociado a un objeto determinado y
cuya ejecución sólo puede desencadenarse a través de un mensaje
recibido por éste o por sus descendientes.
Son sinónimos de 'método' todos aquellos términos que se han
aplicado tradicionalmente a los programas, como procedimiento,
función, rutina, etc. Sin embargo, es conveniente utilizar el término
'método' para que se distingan claramente las propiedades
especiales que adquiere un programa en el entorno OOP, que
afectan fundamentalmente a la forma de invocarlo (únicamente a
través de un mensaje) y a su campo de acción, limitado a un objeto
y a sus descendientes, aunque posiblemente no a todos.
Si los métodos son programas, se deduce que podrían tener
argumentos, o parámetros. Puesto que los métodos pueden
heredarse de unos objetos a otros, un objeto puede disponer de un
método de dos maneras diferentes:
53
Manual del Alumno
-Métodos propios. Están incluídos dentro de la cápsula del objeto.
-Métodos heredados. Estan definidos en un objeto diferente,
antepasado de éste (padre,"abuelo", etc.). A veces estos métodos se
llaman métodos miembro porque el objeto los posee por el mero
hecho de ser miembro de una clase.
Demonios
Es un tipo especial de métodos, relativamente poco frecuente en los
sistemas de OOP, que se activa automáticamente cuando sucede
algo especial. Es decir, es un programa, como los métodos
ordinarios, pero se diferencia de estos porque su ejecución no se
activa con un mensaje, sino que se desencadena autmáticamente
cuando ocurre un suceso determinado: la asignación de un valor a
una propiedad de un objeto, la lectura de un valor determinado, etc.
Los demonios, cuando existen, se diferencian de otros métodos por
que no son heredables y porque a veces están ligados a una de las
propiedades de un objeto, mas que al objeto entero.
Clase .
Es un modelo que se utiliza para describir uno o más objetos del
mismo tipo. La comunicación con un objeto se realiza a través del
paso de mensajes. El envío de un mensaje a una instancia de una
clase produce la ejecución de un método (función miembro en
C++).
Una clase es una plantilla para crear un objeto. Incluye en su
descripción un nombre para el tipo de objeto, una lista de atributos
(y sus tipos), y una lista de mensajes con métodos correspondientes
al que un objeto de la clase puede responder. Entre los mensajes en
la descripción de la clase son usados para crear instancias de la
clase. Estos mensajes de creación de instancia son usualmente
enviados a el nombre de la clase. El mecanismo exacto para crear
54
Manual del Alumno
instancias de una clase es dependiente del lenguaje orientado a
objetos siendo usado.
En la POO se suele decir que los objetos son miembros o
pertenecen a una clase. Una clase es un tipo definido por e usuario
que determina las estructuras de datos y las operaciones asociadas
con ese tipo. Las clases son como plantillas o modelos que
describen cómo se construyen ciertos tipos de objetos. Cada vez
que se construye un objeto de una clase, se crea una instancia de
esa clase.
Instancia
Una instancia es un objeto con propiedades definidas en su
descripción de clase. Las propiedades que son únicas a la instancia
son los valores de sus atributos.
En general, los términos objetos e instancias de una clase se pueden
utilizar indistintamente. Una clase es una colección de objetos
similares y un objeto es una instancia de una definición de una
clase. Una clase puede tener muchas instancias y cada una es un
objeto independiente.
Mensajes
Los objetos, las clases y sus instancias se comunican a través del
paso de mensajes. Esto elimina la duplicación de datos y garantiza
que no se propaguen los efectos de los cambios en las estructuras
de datos encapsuladas dentro de objetos sobre otras partes del
sistema. A menudo los mensajes se realizan como llamadas a
funciones.
Se considera al mensaje como una llamada, posiblemente
comprendido en el tiempo de ejecución, al objeto o clase que pide
un servicio o la notificación de un suceso específico.
55
Manual del Alumno
Un mensaje es representado por un identificador, el símbolo
especial, o combinación de identificadores que implican una acción
para ser tomada por un objeto. Los mensajes pueden ser simples o
pueden incluir parámetros que afectan como el objeto receptor
responde. La manera en que un objeto responde al mensaje puede
también ser afectado por los valores de sus atributos.
Tipos de mensajes Los mensajes pueden ser principalmente de 3 tipos, cuando recurre
a la concurrencia y enlaces de control: secuencial, sincrono y
asincrono. El objeto servidor o la clase, determina cual de estos 3
tipos de mensajes puede exportar en la declaración del mensaje.
· Secuencial - Comprenden únicamente un enlace de control y
puede ser enviado al objeto secuencial o clase; el enlace de control
se pasa desde el cliente al servidor hasta que la operación
correspondiente se complete.
· Sincrono - Comprenden dos enlaces de control que deben
sincronizarse durante la ejecución de la correspondiente operación,
que puede ser exportado por un objeto o clase concurrente.
· Asincrono - Es lo contrario, no comprende lo sincrono y es
esencialmente una atención del enlace de control que lo requiera en
el tiempo que este disponible y olvida el mensaje; tal que mensaje
puede solamente ser exportado por un objeto concurrente y clase.
Reutilización
Una vez que una clase se ha escrito creado, depurado y utilizado, se
puede difundir entre otros programadores para que pueden
reutilizarla en sus programas. Un programador puede tomar la clase
existente, y sin modificarla añadir características adicionales a la
misma. Estas operaciones se realizan por derivación de una clase
nueva a partir de una clase ya existente.
56
Manual del Alumno
La reutilización del software es un término muy importante, que
Brad J. Cox ha definido con el concepto de Chip de Software (IC-
Software), queriendo significar que esta propiedad es similar a la
utilización del hardware mediante circuitos integrados.
Clases en C++
Concepto de Clase
Una clase es un tipo de dato que contiene uno o más elementos
datos llamados miembros dato, y cero, una o más funciones que
manipulan esos datos llamados funciones miembro o métodos. Una
clase se puede definir como una estructura (struct), una unión
(union) o una clase (clase). La sintaxis de la clase es la siguiente:
class nombre_clase {
miembro_1;
miembro_2;
miembro_3;
...
función_miembro_1( argumentos);
función membro_2 (argumentos);
función_miembro_3(argumentos);
...};
Dada una clase, pueden definirse uno o más objetos o instancias de
la misma, los cuales tendrán los datos y métodos definidos para
dicha clase.
nombre_clase objeto_1;
nombre_clase objeto_2;
Sin embargo, también pueden declararse apuntadores a una clase.
Un apuntador o referencia a una clase proporciona medios
indirectos para acceder a un objeto, pero no es una instancia de
clase.
57
Manual del Alumno
Así, un objeto es una instancia de clase y de modo similar una
variable es una instancia de un tipo predefinido. Una clase es un
tipo de estructura que encasilla datos y funciones en una entidad:
Componentes de una clase
Una definición de una clase consta de dos partes: una declaración y
una definición. La declaración lista los miembros de la clase. La
implementación o cuerpo define las funciones de la clase.
int angf;
int radio;
public:
rebanada(int X, int Y, Int angi, int ang f, int radio);
Rellena(int patron, int color);
Dibuja();
};
Así, si declaramos lo siguiente:
void main(void)
{ rebanada Rebanada1(10, 10, 0, 90, 50);
rebanada Rebanada2(10,10, 90, 180,50);
rebanada*ap_Rebanada;
}
Tanto Rebanada1 como Rebanada2 son objetos de la clase
rebanada, mientras que ap_rebanada es sólo un apuntador a
instancias de esa clase.
De acuerdo a su definición, C++ es una extensión del lenguaje C,
un superconjunto, esto implica para quién decide iniciar sus
estudios en C++ que para empezar dispone de un lenguaje
completo como soporte a los nuevos conceptos introducidos por
C++, esto significa que todo lo que podemos programar en ANSI-C
funciona en C++ y esto a su vez es una buena razón para afirmar la
importancia que tiene en el aprendizaje de C++ el conocimiento
58
Manual del Alumno
previo del lenguaje C, por lo tanto se supone que el lector está
familiarizado con todo lo expuesto en el curso en línea de la
sección programando en C.
Comentarios en C++
Es natural tratar en primer lugar la manera de hacer comentarios en
el código fuente de un programa escrito en C++, y para probar la
afirmación hecha en el párrafo anterior, el siguiente código escrito
en C debe compilar perfectamente. Le llamaremos al código
comenta1.cpp, habrá notado que la extensión *.cpp se refiere a C-
plus-plus. Un compilador de C++ sabe que el código está escrito en
C++ precisamente por la extensión del archivo que contiene el
código fuente.
/******************************************/
/* comenta1.cpp */
/******************************************/ #include <stdio.h>
int main()
{
float corriente, voltaje, resistencia; /* tres variables */
printf("Este programa calcula voltaje de acuerdo a la ley de
Ohm\n");
printf("Valor de la corriente, en amperes?\n");
scanf("%f", &corriente);
printf("Valor de la resistencia, en ohms?\n");
scanf("%f", &resistencia);
voltaje=corriente*resistencia; /* calculo de voltaje */
printf("El valor del voltaje es de %f voltios\n", voltaje);
return 0;
}
59
Manual del Alumno
Aunque se trata de un programa escrito en C, por la sencilla razón
de que el archivo de código fuente lleva la extensión *.cpp, el
compilador lo trata como un código C++ válido, esto demuestra la
total compatibilidad de que disponemos al programar en C++ con
respecto al lenguaje C. Empero C++ introduce una manera más
práctica de hacer comentarios al utilizar doblemente el símbolo de
barra inclinada para indicarle al compilador que el resto de la línea
de código que sigue a la doble barra inclinada es un comentario y
por lo tanto debe ignorarse en el proceso de compilación. Al
contrario del mecanismo para hacer comentarios en C que puede
abarcar varias líneas de código, el estilo utilizado en C++ sólo es
válido hasta el final del renglón en donde es utilizada la doble barra
inclinada. Ahora nuestro programa lo modificamos para utilizar
comentarios en C++:
//******************************************
// comenta2.cpp
//******************************************
#include <stdio.h>
int main()
{
float corriente, voltaje, resistencia; // tres variables
printf("Este programa calcula voltaje de acuerdo a la ley de
Ohm\n");
printf("Valor de la corriente, en amperes?\n");
scanf("%f", &corriente);
printf("Valor de la resistencia, en ohms?\n");
scanf("%f", &resistencia);
voltaje=corriente*resistencia; // calculo de voltaje
printf("El valor del voltaje es de %f voltios\n", voltaje);
return 0;
}
60
Manual del Alumno
Se aprecia de inmediato la comodidad del nuevo estilo de
introducir comentarios en el código fuente de C++ además del
doble beneficio que representa el poder seguir utilizando los
comentarios en C. Se debe tener ciertas precauciones al manejar los
comentarios pues aunque C++ reconoce ambos métodos, el
estándar ANSI-C en sí no reconoce la mecánica de C++ para los
comentarios, dicho en otras palabras, no escriba código en ANSI-C
pensando en términos de C++, lo contrario, como ya lo probamos,
es perfectamente válido.
La librería iostream.h
Aunque comenta2.cpp es un programa C++ válido sabemos que es
en realidad un programa escrito en C y nosotros estamos
interesados en aprender los aspectos nuevos introducidos en C++,
pues bien, un cambio muy importante es en la manera en que se
maneja la entrada y salida de datos en el programa. Al igual que en
C, C++ no incluye operaciones de entrada y salida como parte del
lenguaje en sí, en cambio define la librería iostream que agrega
funciones de entrada y salida muy elegantes. Al programar en C
nos familiarizamos con el archivo stdio.h en donde está definida
entre otras, la función printf() que como sabemos sirve para escribir
datos en el dispositivo estándar de salida, que en la gran mayoría de
los casos es el monitor de la computadora. Como la función printf()
se desarrolló casi desde los origenes de C, la función ha sufrido una
serie de transformaciones que la ha hecho crecer en tamaño, como
resultado de esto, cuando incluimos la función printf() en nuestro
programa C tambien incluimos todas las opciones agregadas con el
tiempo aunque no las utilicemos. Un caso similar sucede con la
función scanf().
61
Manual del Alumno
C++ propone una solución muy interesante al tratar las operaciones
de entrada-salida (E/S) no como una función, sino como una clase
que implementa una gran capacidad de formatos para entrada y
salida de datos, de ésta manera el programador C++ toma sólo las
características que el trabajo requiere.
Como ya hemos definido qué es una clase, tenga en cuenta que en
C++ las operaciones de E/S están definidas en una clase, éste es un
punto importante. Existen cuatro flujos de E/S (iostreams)
predefinidos:
TIPO DESCRIPCIÓN
cin asociado al dispositivo de entrada estándar.
cout asociado al dispositivo de salida estándar.
cerr asociado al dispositivo de error estándar.
clog salida almacenada temporalmente para el dispositivo de
error estándar.
Tomando en cuenta lo dicho estamos en condición de modificar el
programa anterior para utilizar los flujos de E/S, de ésta manera ya
no necesitamos el archivo de cabecera stdio.h el cual lo hemos
sustituido por el archivo iostream.h que nos permite utilizar los
flujos de E/S
//******************************************
// entsal.cpp
//******************************************
#include <iostream.h>
int main()
{
float corriente, voltaje, resistencia; // tres variables
cout << "Este programa calcula volaje de acuerdo a la ley de
Ohm\n";
cout << "Valor de la corriente, en amperes?\n";
cin >> corriente;
cout << "Valor de la resistencia, en ohms?\n";
62
Manual del Alumno
cin >> resistencia;
voltaje=corriente*resistencia; // calculo de voltaje
cout << "El valor del voltaje es de " << voltaje << " voltios\n";
return 0;
}
El programa entsal.cpp es idéntico a comenta2.cpp, sin embargo es
importante hacer notar lo siguiente, no se recomienda mezclar la
función printf() con el flujo de salida cout en el mismo programa ya
que se generarían dos flujos independientes hacia el mismo
dispositivo de salida con resultados impredecibles. Comparando los
dos últimos programas es fácil asimilar el uso de cin y cout.
Utilizamos el flujo de salida cout en conjunción con el operador de
inserción, << para mostrar un mensaje en la pantalla del monitor.
De manera similar, utilizamos el flujo de entrada cin en conjunción
con el operador de extracción, >> para asignar un valor a las
variables llamadas corriente y resistencia. Observe que no es
necesario utilizar el operador de dirección & con las variables de
entrada. La línea 19 demuestra el uso de varios operadores de
inserción en una sola línea de código, observe que las cadenas y los
datos se despliegan en el orden en que se escriben en la línea de
código, tambien observe el espacio en blanco al final de la primera
cadena y al principio de la segunda cadena de ésta línea de código.
En términos generales declaramos el archivo de cabecera
iostream.h para operaciones de flujos de E/S, éste suele incluir
otros archivos de cabecera adicionales que declaran funciones,
macros, clases y valores necesarios en las operaciones con flujos de
entrada (istream.h), flujos de salida (ostream.h), buffers de flujos
(streamb.h) y manipuladores de flujo (iomanip.h) principalmente.
Analizaremos éstas tres partes por separado.
63
Manual del Alumno
Utilizando manipuladores
Un manipulador es un valor que podemos extraer ó insertar de un
flujo de E/S para lograr un efecto especial en estos. Un
manipulador parece un objeto que insertamos ó extraemos de un
flujo pero generalmente solo cambia el estado del mismo. Existen
tres manipuladores comunes, dec, oct y hex los cuales permiten
desplegar un valor numérico en base decimal, octal y hexadecimal
respectivamente, y otros más declarados en el archivo iomanip.h
entre los que figuran setw(), setfill(), setprecision(), setiosflags() y
resetiosflags().
Manipuladores básicos
En el siguiente programa, manipu01.cpp, se demuestra el uso de los
manipuladores básicos oct, hex, y dec. El programa inicia
solicitándole introducir un número, mismo que se almacena en la
variable de tipo int llamada numero, en forma predeterminada, el
sistema despliega los valores numéricos utilizando la base 10, esto
se demuestra en el primer mensaje desplegado por el programa en
la línea 14. Enseguida se utiliza el operador de inserción (<<) para
introducir el manipulador oct que cambia la base para despliegue
numérico a octal por lo que el valor contenido en la variable
llamada numero se despliega en pantalla en su equivalente de base
8. Observe que el contenido de la variable no ha cambiado, solo se
modifica la forma para desplegar un valor numérico en el
dispositivo estándar de salida.
//*****************************************************
****
// manipu01.cpp
//*****************************************************
****
#include <iostream.h>
int main()
64
Manual del Alumno
{
int numero;
cout <<"Introduzca un numero" << endl;
cin >> numero;
cout <<"El numero introducido en base decimal es: "
<< numero << endl;
cout << oct; // Ahora trabajamos en base octal
cout << "Cambiando a base octal el numero introducido se "
"despliega asi: " << numero << endl;
cout << hex; // Trabajamos en base hexadecimal
cout << "El mismo numero en base hexadecimal es: ";
cout << numero << endl;
cout << "Introduzca otro numero" << endl;
cin >> numero;
cout << "El numero es: " << numero << endl;
cout << dec; // Restituimos la base predeterminada
return 0;
}
De manera similar se utiliza el operador de inserción para
introducir ahora el modificador hex que sirve para cambiar el
despliegue numérico a base 16 ó hexadecimal, entonces el
programa muestra el valor almacenado en numero pero
representado en base 16. Cuando utilizamos los manipuladores oct
ó hex le indicamos al sistema la base numérica que deseamos para
representar nuestros valores numéricos, una vez cambiada la
representación numérica, ésta permanece hasta que especifiquemos
una nueva base ó hasta que apaguemos el sistema, para demostrar
ésta situación, en la parte final del programa se le solicita introducir
otro número, observará que el despliegue en pantalla parece no
coincidir con el número introducido, éste fenómeno sucede porque
estamos acostumbrados a pensar en términos generales, utilizando
la base 10, pero con un análisis más cuidadoso encontramos que no
65
Manual del Alumno
existe error alguno, sencillamente el programa despliega los valores
numéricos utilizando el último modificador especificado, que en
éste caso es de tipo hexadecimal. El programa termina utilizando
una vez más el operador de inserción para especificar la base 10
como la deseada para representar los valores numéricos en el
dispositivo estándar de salida.
El archivo iomanip.h
El archivo de cabecera llamado iomanip.h define un conjunto de
manipuladores adicionales a los básicos tratados en el párrafo
anterior, generalmente se trata de los siguientes:
TIPO DESCRIPCIÓN
setw() se utiliza con flujos istream y ostream.
setfill() se utiliza con flujos istream y ostream.
setprecision() se utiliza con flujos istream y ostream.
setiosflags() se utiliza con flujos istream.
resetiosflags() se utiliza con flujos istream y ostream.
Empezamos estudiando el manipulador setw(), ya sea que se utilice
con flujos de entrada (istream) ó flujos de salida (ostream)
establece el ancho de campo al valor especificado dentro del
paréntesis del manipulador como se demuestra en el programa
manipu02.cpp.
//*****************************************************
****
// manipu02.cpp
//*****************************************************
****
#include <iostream.h> // para operaciones de E/S
#include <iomanip.h> // define diferentes manipuladores
66
Manual del Alumno
int main()
{
int i;
cout << " El ancho de este campo es de 36" << endl;
for(i=1; i<6; i++)
{
cout << setw(i);
cout << "1" << endl;
cout << setw(i);
cout << "12" << endl;
cout << setw(i);
cout << "123" << endl;
cout << endl;
}
cout << setw(36);
cout << "El ancho de este campo es de 36" << endl;
return 0;
}
El manipulador setw(), como ya se dijo, establece el ancho de un
campo de salida pero su efecto solo se aplica a la salida del valor
siguiente, la siguiente operación de inserción ó extracción
restablece el valor del ancho del campo a cero que es el valor
predeterminado, por ésta razón se utiliza varias veces el
manipulador setw() a lo largo del programa manipu02.cpp,
compilándolo podemos apreciar los espacios en blanco a la
izquierda del campo de salida que resultan de especificar diferentes
valores de tipo int.
En el siguiente programa de ejemplo, manipula03.cpp,
especificamos diferentes valores de ancho de campo para los datos
correspondientes a las calificaciones de un pequeño grupo ficticio
de estudiantes con el objeto de darle al reporte de calificaciones
una mejor apariencia, en éste programa utilizamos el manipulador
67
Manual del Alumno
setfill() para especificar que deseamos rellenar los espacios en
blanco, definidos por el manipulador setw(), con el carácter de
punto. Se puede observar que al utilizar el manipulador setfill() su
efecto permanece para todos los flujos de E/S hasta que se
selecciona otro carácter diferente. Al final del programa
especificamos utilizar el carácter de espacio en blanco para rellenar
los espacios, que es el carácter predeterminado.
//*****************************************************
****
// manipu03.cpp
//*****************************************************
****
#include <iostream.h> // para operaciones de E/S
#include <iomanip.h> // define diferentes manipuladores
int main()
{
cout << setfill('.'); // rellenar con puntos
cout << "Reporte de calificaciones\n" << endl;
cout << "Joanna Bialous" << setw(20) << "85" << endl;
cout << "Damian Garcia" << setw(19) << "89" << endl;
cout << "Alejandro Muradas" << setw(20) << "87" << endl;
cout << "Lucia Hernandez" << setw(19) << "75" << endl;
cout << "Doris Mendez" << setw(17) << "78" << endl;
cout << "Francisco Mora" << setw(15) << "91" << endl;
cout << setfill('\0'); // se restablece el carácter de llenado
return 0;
}
En el programa comenta1.cpp, escrito en C, se realizó un sencillo
cálculo utilizando valores de tipo float, al ejecutarlo observamos
que la salida mostrada en la pantalla invariablemente desplegaba
seis dígitos decimales, incluso introduciendo valores que dieran por
resultado un valor exacto. En comparación, el mismo programa
68
Manual del Alumno
escrito en C++, entsal.cpp, despliega automáticamente los dígitos
decimales de acuerdo al cálculo realizado, sin embargo, en ciertas
aplicaciones puede ser deseable especificar un número determinado
de dígitos a desplegar por el flujo de E/S. Para éste fin se utiliza el
manipulador setprecision() en donde podemos especificar mediante
un valor de tipo int el número de dígitos a la derecha del punto
decimal a desplegar en pantalla, estudie el programa llamado
manipu04.cpp
//*****************************************************
****
// manipu04.cpp
//*****************************************************
****
#include <iostream.h> // para operaciones de E/S
#include <iomanip.h> // define diferentes manipuladores
int main()
{
int i;
double variable1=22, variable2=7;
for(i=0; i<15; i++)
{
cout << setprecision(i);
cout << variable1/variable2 << endl;
}
return 0;
}
Ya habrá notado que los flujos de E/S tienen ciertos valores
predefinidos y que éstos valores se pueden modificar utilizando
manipuladores como oct y hex, sucede que los flujos de E/S tienen
un campo para especificar los valores correspondientes y éstos
campos se llaman banderas (flags). El archivo de cabecera ios.h
define diferentes especificaciones de banderas. Mediante el uso del
69
Manual del Alumno
manipulador setiosflags() nuestros programas pueden manipular
muchas de éstas banderas utilizando a su vez el operador de
alcance, identificado por un par de símbolos de dos puntos (::) y
que nos sirve para acceder a una variable de alcance global ó a los
miembros de una clase. En una determinada sección del archivo de
cabecera ios.h podemos encontrar las definiciones para las
diferentes banderas como un tipo de dato enum, generalmente de la
siguiente manera:
enum
{
skipws = 0x0001, // Salta espacios en blanco durante la entrada
left = 0x0002, // justifica a la izquierda
right = 0x0004, // justifica a la derecha
internal = 0x0008, // relleno después de signo ó indicador de base
dec = 0x0010, // base decimal
oct = 0x0020, // base octal
hex = 0x0040, // base hexadecimal
showbase = 0x0080, // muestra indicador de base durante la salida
showpoint = 0x0100, // punto decimal obligatorio en float
uppercase = 0x0200, // salida hex en mayúsculas
showpos = 0x0400, // signo positivo obligatorio
scientific = 0x0800, // notación de tipo 3.145363E23
fixed = 0x1000, // dígitos decimales predeterminados
unitbuf = 0x2000, // desaloja flujos después de inserción
stdio = 0x4000 // desaloja stdout, stderr después de inserción
};
Puede identificar fácilmente algunos de los manipuladores que ya
hemos estudiado y ahora veremos cómo se utilizan éstas banderas
mediante el manipulador setiosflags() y cómo restituimos las
condiciones originales mediante el manipulaodr resetiosflags(),
estudie y compile el siguiente ejemplo, manipu05.cpp, además
experimente con las banderas no incluidas en el programa, estoy
70
Manual del Alumno
seguro que no presentará problema alguno comprender el uso de las
diferentes banderas.
//***********************************************
// manipu05.cpp
//***********************************************
#include <iostream.h>
#include <iomanip.h>
int main()
{
int numero;
cout << "Introduzca un numero:\n";
cin >> numero;
cout << "El valor introducido en base 10 es: " << numero
<< endl;
cout << setiosflags(ios::oct);
cout << "en base octal es: " << numero << endl;
cout << setiosflags(ios::hex);
cout << "y en base hexadecimal es: " << numero << endl;
cout << setiosflags(ios::uppercase|ios::showbase);
cout << "utilizando los manipuladores uppercase y showbase"
" el valor es: " << numero << endl;
cout << resetiosflags(ios::hex);
cout << "Ahora el valor se representa en base octal asi: "
<< numero << endl;
cout << resetiosflags(ios::uppercase|ios::showbase|ios::oct);
cout << setiosflags(ios::showpos|ios::showpoint|ios::fixed);
cout << "Ahora el valor es: " << (float)numero << endl;
cout << resetiosflags(ios::showpos|ios::showpoint|
ios::fixed);
cout << "El valor es " << numero << endl;
return 0;
}
71
Manual del Alumno
El programa manipu05.cpp empieza solicitándole un número el
cual es desplegado inmediatamente para demostrar los ajustes
predeterminados para los flujos de E/S, después se utiliza la
bandera oct para representar el valor introducido en base octal,
luego de mostrar el valor en pantalla se vuelve a utilizar la bandera
hex ésta vez para mostrar el número introducido en base
hexadecimal. En ésta parte del deseamos darle a los flujos de E/S,
en éste orden, primero indicamos que deseamos la base octal y
posteriormente la base hexadecimal, si después de la línea 19
utilizamos el manipulador setiosflags(ios::oct) la representación de
los flujos de E/S no será en base octal sino en hexadecimal,
entonces, si deseamos nuevamente la representación del valor
numérico introducido en base octal necesitamos reestablecer la
bandera oct anteriormente establecida en la línea 15, para esto
utilizamos el manipulador resetiosflags() como está ilustrado en la
línea 23 y posteriormente en las líneas 26 y 29 del programa.
Cuando sea necesario especificar varias banderas podemos hacer
uso del operador OR tanto con el manipulador setiosflags() como
con resetiosflags() tal y como se puede apreciar en las líneas 20, 26,
27 y 30 del programa.
Funciones de salida
Sabemos que un programa C++ utiliza el operador de inserción
(<<) para mostrar datos de salida, también que el flujo de salida
cout está definido como una clase y por lo tanto contiene ciertas
funciones, llamadas miembro. En los programas que hemos visto
hasta éste momento utilizamos el flujo de salida cout en conjunto
con el operador de inserción << para mostrar datos de salida, sin
embargo podemos hacer uso de las funciones miembro de salida
definidas para el flujo cout en caso de necesitar un control más
preciso, las funciones principales para el flujo de salida están
definidas en el archivo ostream.h. Analizando éste archivo de
72
Manual del Alumno
cabecera podemos ver las funciones mencionadas, pero además
podemos apreciar que la clase ostream está derivada de la clase ios
y por lo tanto tambien podemos utilizar con los flujos de salida las
funciones propias de la clase ios. Veamos un ejemplo de las
funciones miembro de cout llamadas put y write.
//***********************************************
// salida01.cpp
//***********************************************
#include <iostream.h>
int main()
{
int letra;
char letras[]="Programacion en C++ por Virgilio"
"Gomez Negrete";
for(letra='A'; letra <= 'Z'; letra++)
{
cout << letra << " ";
}
cout << "\n\n";
for(letra='A'; letra <= 'Z'; letra++)
{
cout.put((char)letra);
cout << " ";
}
cout << "\n\n";
cout.write(letras, 21);
cout << endl;
return 0;
}
El programa contiene dos bucles que demuestran la diferencia entre
utilizar la función miembro put versus el mecanismo tradicional
cout <<. Observe que hemos declarado una variable de tipo int
73
Manual del Alumno
llamada letra, por ésta razón el primer bucle despliega las letras del
alfabeto, sólo que en sus respectivos equivalentes decimales del
código ASCII. El segundo bucle es prácticamente igual al primero
excepto por el uso de la función miembro de la clase ostream
llamada put que de acuerdo a su definición, su argumento debe ser
de tipo char, por lo tanto debemos moldear nuestra variable de tipo
int para que sea ahora de tipo char y así poder utilizarla en la
función miembro put. Habrá notado que para tener acceso a una
función miembro de una clase utilizamos el operador punto (.). Ya
en la parte final del programa encontramos un ejemplo del uso de la
función miembro llamada write, ésta despliega el número indicado
de carácteres, en éste caso 21, empezando por el principio del
arreglo de tipo char llamado letras. Estudiando el archivo de
cabecera ostream.h podemos determinar las funciones miembro de
la clase ostream y sus respectivos argumentos, comparando ésta
información con los ejemplos aquí presentados nos ayudará a
entender la manera en que se relacionan las definiciones de las
diferentes funciones con sus aplicaciones reales, práctica que
recomiendo ampliamente.
En el programa manipu05.cpp analizamos diferentes
especificaciones de banderas que establecimos mediante el uso del
manipulador setiosflags(), pues bien, podemos utilizar la función
miembro setf() para realizar la misma función que con el
manipulador setiosflags() y en forma similar, podemos utilizar la
función unsetf() para los mismos propósitos que el manipulador
resetiosflags(), incluso es posible combinar diferentes
especificaciones de bandera mediante el uso del operador OR, más
interesante aún es que podemos utilizar la función miembro flags()
para determinar el estado original de las banderas de flujo de E/S,
guardar el dato en una variable y posteriormente utilizarlo para
restituir la condición original de operación de las banderas de flujo,
como lo demuestra el siguiente programa, llamado salida02.cpp
74
Manual del Alumno
//***********************************************
// salida02.cpp
//***********************************************
#include <iostream.h>
#include <iomanip.h>
int main()
{
long banderas_originales;
int numero=64;
banderas_originales=cout.flags();
cout << "banderas originales es: " << cout.flags() << endl;
cout.setf(ios::showbase|ios::uppercase|ios::hex);
cout << "banderas modificadas es: " << cout.flags() << endl;
cout << "la variable numero en base 16 es: "
<< numero << endl;
cout.flags(banderas_originales);
cout << "restituidas las banderas originales, \nahora "
"la variable numero en base 10 es: " << numero << endl;
cout << "banderas originales es: " << cout.flags() << endl;
return 0;
}
Se aprecia cómo el uso adecuado de las funciones miembro puede
simplificar un código fuente y hacer el programa más eficiente, ésta
es una buena razón para tomarse un tiempo y examinar los
diferentes archivos de cabecera incluidos con nuestro compilador,
de pasada respondemos a la pregunta ¿Qué archivos de cabecera
debo utilizar en mi programa? Muy importante es familiarizarse
con la documentación incluida en el compilador que utilicemos,
además es una buena práctica como programador.
75
Manual del Alumno
Funciones de entrada
conjunto con el flujo cin para realizar operaciones de entrada. El
flujo de entrada cin tambien está definido como una clase y su
declaración la encontramos en el archivo de cabecera istream.h
junto con sus respectivas funciones miembro, algunas de las cuales
analizaremos en el siguiente programa, llamado entrada.cpp, sin
duda alguna no tendrá problema en comprender lo expuesto en el
siguiente código:
//***********************************************
// entrada.cpp
//***********************************************
#include <iostream.h>
#include <ctype.h>
int contador; // automáticamente inicializada en cero
int main()
{
char texto[64], letra;
cout << "Teclee una linea de texto y presione enter\n\n";
cin.getline(texto, sizeof(texto));
cout << "\n\nEl texto introducido es:\n\n" << texto
<< "\n" << "el cual contiene " << cin.gcount()-1
<< " carácteres" << endl;
cout << "\n\nTeclee otra linea de texto que incluya una x\n";
do
{
letra=cin.get();
cout << (char)toupper(letra);
contador++;
}
while(letra!='x');
cout << "\n\nUsted escribio " << contador-1 << " carácteres "
"antes de la x" << endl;
76
Manual del Alumno
return 0;
}
No está de más recordar que una variable de tipo global es
automáticamente inicializada a cero. El programa entrada.cpp
demuestra el uso de algunas funciones miembro de la clase istream,
empezando por la función llamada getline() que lee una línea de
texto hasta encontrar un carácter de retorno de carro ("\n"),
similarmente se hace uso de la función miembro gcount() para
determinar el número de caracteres leídos, ésta función incluye el
carácter de retorno de carro, por lo tanto restamos uno al valor
retornado por la función para que el resultado desplegado sea el
correcto (línea 17). En la segunda parte del programa se utiliza la
función miembro llamada get(), ésta lee un solo carácter borrándolo
de la memoria de almacenamiento temporal, se convierte a
mayúscula utilizando la función toupper() moldeando la salida de
ésta a tipo char y se despliega el resultado en pantalla. Éste proceso
se repite debido a un bucle do-while hasta encontrar un carácter 'x'.
Compare el resultado obtenido de la cuenta efectuada en el bucle
do-while por la variable llamada contador con el resultado obtenido
al utilizar la función miembro gcount().
Es importante experimentar con lo expuesto en éste capítulo, con
frecuencia aprendemos detalles que por alguna causa nos pasan
inadvertidos con la simple lectura del texto, además cada
compilador tiene sus detalles particulares que conviene conocer
para conseguir el máximo provecho del mismo.
CUATRO PROPIEDADES ESENCIALES
Existen cuatro propiedades esenciales soportadas por el paradigma
orientado a objetos : Abstracción, Encapsulación, Herencia, y
Polimorfismo. Juntos representan un poderoso conjunto de
aspectos, que pueden ser aplicados para resolver un problema
mediante una conveniente aplicación de estos aspectos, uno puede
77
Manual del Alumno
construir una estructura para resolver problemas nuevos que usan
componentes (las clases) desarrolladas para soluciones previas del
problema.
Abstracción de Datos.
Consiste en la extracción de las propiedades fundamentales de un
concepto. Permite no preocuparse por los detalles no esenciales, es
decir, no es necesario conocer los detalles exactos de la
implementación. Existe casi en todos los lenguajes de
programación. Las estructuras de datos y los tipos de datos son un
ejemplo de abstracción.
Una abstracción tiene existencia conceptual más bien que concreta.
Representa ideas, conceptos, y propiedades generales sin la
atención a detalles. Para el software de computadora, esto significa
sin la atención a los detalles de implementación, evitando así la
necesidad de confundirse con la sintaxis del lenguaje o la elección
de un lenguaje. El único interés es que un lenguaje particular
soporte la abstracción. La abstracción es muy importante en las fases iniciales de una
solución del problema (con el que uno se se esta enfrentando),
donde se hace un intento para comprender el espacio del problema
y las técnicas requeridas para una solución. Aunque se distribuyen
eventualmente con detalles, la abstracción hace lo posible para
delegar esos detalles y para organizar los a todos de una manera
manejable mediante el uso de abstracciones de capa. Una
abstracción de capa es una abstracción que esta variando los
niveles de detalle. En el nivel más alto o primer nivel hay muy
poco detalle. La abstracción del nivel más alto se expresan desde el
punto de vista de un número pequeño de abstracciones de niveles
más inferiores. La particionalidad continúa en cada nivel hasta que
todos los detalles han sido incluidos.
78
Manual del Alumno
Es por lo tanto la abstracción un modelo que incluye todas las
capacidades esenciales, propiedades, o los aspectos que están
siendo modelado sin algunos detalles extraños.
Hay varios tipos importantes de abstracción, dependiendo sobre
qué esta siendo modelado.
1. Abstracciones de objeto - Los objetos son las abstracciones
que modelan la aplicación individual del dominio o
entidades del espacio de solución.
2. Abstracciones de clases - Las clases son las plantillas
abstractas que modelan la aplicación similar del dominio o
entidades del espacio de solución.
3. Abstracciones de datos - Modela los datos que son usados
cuando se comunican con el atributo de objetos y las clases.
4. Abstracciones funcionales - Modela operaciones
secuenciales considerando los procesos de abstracciones.
5. Abstracciones de procesos - Modelan las operaciones
concurrentes. Ambas abstracciones funcional y de procesos
se usan cuando se comunican con las operaciones de
objetos y las clases.
6. Abstracciones de excepción - Las condiciones del error del
modelo de excepción son el error manejado y se usan para
crear objetos robustos y clases.
Por lo tanto la abstracción se define como la extracción de las
propiedades esenciales de un concepto. En un programa
estructurado, es suficiente conocer que un procedimiento dado
realiza una tarea específica. El cómo se realiza la tarea no es
importante; mientras el procedimiento sea fiable, se puede utilizar
sin tener que conocer como funciona su interior. Esto se conoce
como una abstracción funcional. Con la abstracción de datos, las
estructuras de datos e ítems se pueden utilizar sin preocuparse
sobre los detalles exactos de la implementación.
79
Manual del Alumno
Polimorfismo
En la POO el polimorfismo se refiere al hecho de que una misma
operación puede tener diferente comportamiento en diferentes
objetos. En otras palabras, diferentes objetos reaccionan al mismo
mensaje de manera diferente.
Por ejemplo, supongamos un número de figuras geométricas que
responden todas al mensaje Dibujar. Cada objeto reacciona a este
mensaje visualizando su figura en la pantalla. Obviamente, el
mecanismo real para dibujar los objetos difiere de una figura a otra,
pero todas las figuras realizan esta tarea en respuesta al mismo
mensaje.
Polimorfismo se define como la calidad o estado de ser capaz de
asumir formas diferentes. En la solución de un problema orientado
a objetos, polimorfismo puede aplicarse a cualquier objetos u
operaciones. El uso más común es la operación polimorfismo, que
es representada por enviar el mismo mensaje, imprimirlo, a objetos
diferentes y cada uno puede ser responder en su propia manera.
Polimorfismo puede examinarse desde el punto de vista de sus
propiedades suplementarias. Una primera propiedad del
polimorfismo es el sobrecarga de identificadores de mensaje y
operadores. Polimorfismo es apoyado por la ligadura de un método
particular al identificador del mensaje durante la ejecución de un
sistema de software. Esta ligadura lenta, o ligadura dinámica, es un
aspecto importante de la solución de un problema orientado a
objetos.
Una de las características más importantes de la programación
orientada a objetos es la capacidad de que diferentes objetos
responden a órdenes similares de modo diferentes.
Identificador de mensaje y operadores sobrecargados. El
identificador de mensaje y operador invocan una operación
específica sobre un objeto. Cada uno establece la selección del
significado uniforme de la operación en particular que se desea
80
Manual del Alumno
realizar, es decir, son utilizados para representar el concepto de una
operación e identificar que se ejecutara o realizará.
Herencia
Es la propiedad que permite a los objetos construirse a partir de
otros objetos. Este principio consiste en que cada clase puede
dividirse en subclases, es decir, a partir de una clase base se pueden
derivar otras clases (clases derivadas) que comparten características
comunes con la clase de la que se derivan, además de tener sus
propias características particulares.
La Herencia permite definir nuevas clase a partir de clases ya
existentes. Si una clase sólo recibe características de una clase base,
la herencia es simple.
Si una clase recibe propiedades de más de una clase base, la
herencia es múltiple.
Las instancias heredan (usualmente) todas, y únicamente, las
características de las clases a las que pertenecen, pero, también, es
posible, en un sistema orientado a objetos, que se permita que las
clases hereden características de superclases más generales. En ese
caso, las características heredadas pueden ser ignoradas (overriden)
y se pueden agregar características adicionales para tratar
excepciones.
La herencia es el acto de adquirir una posesión, condición, o
característica de generaciones pasadas. En la solución de un
problema de computadora se habla de componentes de software
que heredan propiedades que describen otros componentes de
software. En la solución de un problema orientado a objetos un tipo
de objeto hereda propiedades que caracterizan otro tipo de objeto.
81
Manual del Alumno
Desde las propiedades de objetos son dadas por la descripción de la
clase, esto implica una jerarquía de clases, donde una clase es un
subclase de otra, la clase padre. Los objetos que son los instancias
de las subclases tienen propiedades dadas dentro de la descripción
de la subclase así como también propiedades heredadas dadas
dentro de la clase padre y todas las clases antecesoras.
Así la herencia provee la potencialidad para construir soluciones
nuevas a problemas agregando el incremento de la capacidad a
soluciones existentes del problema mediante subclases.
Las instancias de una subclase representan una especialización de
instancias descritas por una clase padre. La instancia de la subclase
tiene todos los atributos dados por la clase padre, más los atributos
adicionales o agregados de la subclase. La instancia de la subclase
responde al mismo conjunto de mensajes dados en la clase padre,
los mensajes adicionales se dan en la descripción de la subclase.
La respuesta de la instancia de la subclase a los mensajes en la
clase padre puede ser diferente de la respuesta de una clase padre
de una instancia al mismo mensaje. No es valido considerar
subclases de objetos que tengan menos atributos que los objetos
descritos por la clase padre.
Concepturalización de la herencia
Una clase utilizada para derivar nuevas clases se conoce como
clase base (padre, ascendiente), y una clase creada de otra clase se
llama clase derivada (hija, descendiente, subclase).
En un lenguaje Orientado a Objetos la herencia se manifiesta con la
creación de un tipo definido por el usuario (Clase), que puede
heredar las características de otra clase ya existente o derivar las
suyas a otra nueva clase. Cuando se hereda, las clases derivadas
reciben las características (estructuras de datos y funciones) de la
clase original , a las que se pueden añadir nuevas características o
modificar las características heredadas.
La herencia se aplica para extender y reutilizar el código existente:
82
Manual del Alumno
Los objetos se pueden construir de modo incremental a
partir de otros objetos y pueden compartir código y
estructuras de datos
Los algoritmos generales se pueden escribir de modo que se
pueden reutilizar para nuevos tipos de objetos, incluso
después que los algoritmos originales se han compilado.
Herencia simple :
Se realiza tomando una clase existente y derivando nuevas clases
de ella (Figura 2.5 ). La clase derivada hereda las estructuras de
datos y funciones de la clase original, Además, se pueden añadir
nuevos miembros a las clases derivadas y los miembros heredados
pueden ser modificados. Una clase utilizada para derivar nuevas
clases se denomina clase base (padre, superclase, ascendiente). una
clase creada de otra clase se denomina clase derivada o subclase. A
su vez una clase derivada puede ser utilizada como una clase base
para derivar más clases. Por consiguiente, se pueden construir
jerarquías de clases, en las que cada clase sirve como padre o raíz
de una nueva clase
Herencia Múltiple :
Es aquella en la cual una clase derivada tiene más de una clase
base. Aunque el concepto de herencia múltiple es muy útil, el
diseño de clases suele ser más complejo, y en ocasiones es
preferible realizar la aplicación con herencia múltiple mediante
emulación de herencia simple.
Encapsulamiento
Las estructuras de datos y los detalles de la realización de un objeto
se hallan ocultos de otros objetos del sistema. La única forma de
acceder al estado de un objeto es enviar un mensaje que haga que
uno de los métodos se ejecute. Estrictamente hablando, los
atributos son escrituras taquigráficas para los métodos que obtienen
y colocan valores. Esto hace que los tipos de objetos sean
83
Manual del Alumno
equivalentes a los tipos de datos abstractos en programación, en
términos generales.
El resultado de encapsulación es una entidad con fronteras
distintas, una interface bien definida, y una representación interna
protegida. Para el software de computadora, una encapsulación es
un componente de software. La integridad del componente de
software como una encapsulación es dependiente de aspectos del
lenguaje de computadora en el que se implementa el componente.
Encapsulación es un concepto importante para el desarrollo de
soluciones del problema que son menos susceptibles a los errores.
Un problema es particionado en un número de componentes. Cada
componente es encapsulado para interactuar recíprocamente con
los otros componentes únicos de manera cuidadosamente
prescribidas, como definidas por su interface.
En un problema orientado a objetos que resuelve la unidad de
encapsulación es el objeto. Los objetos son abstracciones
encapsuladas.
Combinación de principios
La encapsulación es la combinación de los principios de ingeniería
de software de modularidad, localización, y ocultamiento de
información. Cada una de estas es muy importante para el
desarrollo de un sistema eficiente, formal y sostenible.
Modularidad - El propósito determinado es particionar
requerimientos, el diseño, y software en colecciones o
grupos de complejidad y tamaño apropiado, uniforme con
las metas de ingeniería de software. Modularidad se usa
para construir componentes más grandes y operaciones más
pequeñas en el diseño orientado a objetos que cuando se
usan métodos funcionales debido a que:
- Los módulos - Son creados para abstracciones del objeto
más que abstracciones funcionales
84
Manual del Alumno
- Las operaciones - Se deben asignarse y encapsularse
dentro del objeto y clase.
Localización - El propósito determinado es particionar
requerimientos, el diseño y software así que los recursos
lógicamente ligados son físicamente agrupados para
aumentar la cohesión y para decrecer el acoplamiento. Es
más importante localizar una abstracción completa que
crear un módulo pequeño.
Ocultamiento de Información - Es la consideración del
ocultamiento de información (p. ej. diseño, la decisión, los
detalles de implementación) a fin de promover la
abstracción, soporte al desarrollo ascendente/descendente
(top-down) , protege contra la corrupción accidental, y
promociona el logro de metas de ingeniería de software.
Los recursos de un componente deberían ocultarse a menos
que específicamente se necesiten por otro componente. Los
datos globales comunes no son usados, pero más bien los
atributos se ocultan dentro de objetos y las clases. Cada
componente debería dividirse en 2 partes desarrolladas
separadamente (y compilables): la especificación (o
interface) y el cuerpo (o implementación)
Control de acceso a una clase
Una de las características fundamentales de una clase es ocultar
tanta información como sea posible. Por consiguiente es necesario
imponer ciertas restricciones de acceso a los datos y funciones de
una clase.
Una clase puede contener partes públicas y partes privadas. Por
defecto, todos los miembros definidos en una clase son privados,
aunque se puede especificar la palabra reservada private. Si se
desea hacer algunas o todas la partes de una clase públicas, es
necesario definirlas después de la palabra reservada public.
85
Manual del Alumno
Todas los datos o funciones definidos como públicos pueden ser
accesados solamente por funciones de la propia clase. No así
aquellas que se definen como publicas, que pueden ser accesadas
desde cualquier función del programa.
Dada que una característica clave en la Programación Orientada a
Objetos es el ocultamiento de información, se debe tratar de
eliminar o limitar al máximo tener variables públicas. Las
funciones de una clase, sin embargo, generalmente se hacen
privadas para poder manipular al objeto desde el lugar del
programa que el programador desee. Ver ejemplo clase rebanada.
¿Quiénes pueden usar las clases?
Existen tres clases de usuarios de una clase: la propia clase,
usuarios genéricos y las clases derivadas. Cada uno de ellos tiene
diferentes privilegios de acceso que se asocia con las palabras
reservadas:
1.- private ------------- La propia clase
2.- public -------------- Usuarios genéricos 3.- protected ---------- Clases derivadas.
Declaración de variables y funciones miembro de una clase class A {
int x;
float y;
public:
int z;
void f();
};
void main(void)
{
A vardemo;
}
86
Manual del Alumno
vardemo es un objeto de tipo <> que se compone de tres campos de
datos (x, y, z) y de una función de acceso (f). Los campos x,y sólo
podrían ser modificadas a través de la función f, para lo cual se
escribirá: vardemo.f();
Esto en términos de programación orientada a objetos se leería: se
envía el mensaje f() al objeto vardemo. En otras palabras: se
ejecuta la función f aplicada al objeto vardemo.
La definición de las funciones miembro es muy similar a la
definición ordinaria, ya conocida, de función. Tienen un
encabezado y un cuerpo y pueden tener tipos y argumentos. sin
embargo tienen dos características muy especiales:
Cuando se define una función miembro, se utiliza el operador de
resolución de ámbito :: para identificar la clase a la que pertenece la
función. Las funciones miembro (métodos) de las clase pueden
acceder a los componentes privados de la clase.
class CLS {
int x;
int y;
public:
void f() { cout << "\nX=" << x << " Y=" << y; }
};
class CLS {
int x;
int y;
public:
void f();
}
void CLS::f()
{
cout << "\nX=" << x << " Y=" << y;
}
87
Manual del Alumno
En el primer caso la función está en línea (inline). Por cada llamada
a esta función, el compilador genera (vuelve a copiar) las diferentes
instrucciones de la función. En el segundo caso la función se
llamará con una llamada verdadera a función.
Las Clases con Estructuras
Una estructura (struct) en C++ es también una clase en la que todos
sus miembros son por defecto públicos, a menos que sean
modificados por las palabras reservadas private o protected.
struct alfa {
private:
int x, y;
public:
//resto de la estructura, prototipos de funciones
}
Objetos En C++, un objeto es un elemento declarado de un tipo de clase. Se
conoce como una instancia de una clase.
class rectangulo {
int base, altura;
public:
void dimensiones (int b, int h) { base=b; altura=h; }
int area(void) { return base * altura; }
};
main ()
{
rectangulo MiRectangulo; //Se declara el objeto
MiRectangulo.dimensiones(10, 5); //Se define el tamaño del objeto
cout << "Area= " << MiRectangulo.area(); //Se calcula el área del
objeto y se imprime
88
Manual del Alumno
rectangulo * ap_TuRectangulo = new rectangulo; //Se declara
apuntador al obj.
ap_TuRectangulo->dimensiones(10, 20); //Se define el tamaño del
objeto
cout << "Area= " << ap_TuRectangulo->area(); //Se calcula el área
del obj. se imprime
}
Puede observarse en el ejemplo anterior que los miembros a una
clase se accesan por medio del operador punto (.) si se trata de una
variable de una variable (objeto) del tipo de la clase y por el
operador flecha (->) si se trata de un apuntador un objeto de la
clase.
Clases vacías class vacia {};
con frecuencia en el desarrollo de proyectos grandes, se necesitan
comprobar implementaciones de primeras versiones en las que
algunas clases todavía no están totalmente definidas o
implementadas.
Clases anidadas
Una clase declarada en el interior de otra clase se denomina clase
anidada, y se puede considerar como una clase miembro. Si una
clase anidada es declarada en la sección private de la clase
circúndate, será solo utilizable por miembros de datos de la clase
que la circunde. Si un nombre de una clase anidada es accesible a
una clase o función que no la circunda, se debe aplicar el operador
de resolución de ámbito (::) para utilizar su nombre.
class Externa {
public:
class Interna {
public:
int x;
};
89
Manual del Alumno
};
void main(void)
{
Externa::Interna valor;
int v = valor.x;
}
class clase_2 {
public:
int i;
};
class clase_1{
public:
int j;
clase_2 variable_2; //variable_2 es una clase anidada
};
void main(void)
{
clase_1 variable_1;
variable_1.variable_2.i=125;
}
Miembros estáticos de una clase
Para un miembro dato, la designación static significa que sólo
existe una instancia de ese miembro. Es compartido por todos los
objetos de una misma clase y existe incluso si ningún objeto de esa
clase existe. Se le asigna una zona fija de almacenamiento.
class Ejemplo {
static int varest; //se declara miembro estático privado
public:
static int valor; //se declara miembro estático público
};
int Ejemplo:: valor; //se define miembros estático
int Ejemplo::varest;
90
Manual del Alumno
void main(void)
{ Ejemplo objeto1;
objeto1.valor=1;
objeto1.valor=3;
Ejemplo::valor=3; //se puede referenciar usando el identificador de
clase. Es la manera ideal
de hacerlo
Ejemplo::valres=5; //Acceso válido
objeto1.valres=6; // Acceso no válido
}
Tipos de funciones miembro
1.- Simples
2.- Estáticas
3.- const
4.-volatile
5.- inline
6.- const this
7.- volatile this
8.- Virtuales
9.- Especiales: constructor y destructor
10.- Amigas
11.- opeator
El apuntador this
Dentro de una función miembro, this apunta al objeto asociado con
la invocación de la función miembro. A través de este apuntador,
una función puede saber qué objeto la está llamando.
complejo::complejo(float 1, float b)
{
this->r=a;
this->i=a;
}
91
Manual del Alumno
Aunque su uso se considera redundante e innecesario, puede ser
útil en ciertas situaciones, como por ejemplo para hacer una copia
del objeto asociado con la invocación o asignar un nuevo valor al
objeto.
void clase_x :: funcion_x(clase_x& a, clase_x &b)
{ ...
a=*this; //Asigna el valor del objeto asociado al objeto a
...
*this=b; //Modifica el valor del objeto asociado
}
Acceso al objeto mediante this
this -> nombre_miembro
*this es el objeto total real
this es la dirección del objeto apuntado.
Funciones miembro estáticas
Sólo pueden accesar a otras funciones y datos estáticos declarados
en una clase, pero no pueden manipular funciones ni datos no
estáticos, debido a que no tienen asignado un apuntador this, a
menos que se pase explícitamente este apuntador this.
int v1, v2, v3; //Al ser globales son estáticas
class Prueba{
public:
static void suma():
};
void Prueba::suma() { v1=v2+v3; }
void main(void)
{ Prueba p1; p1.suma(); Prueba::suma(); }
Funciones miembro const
Devuelven objetos del tipo const.
92
Manual del Alumno
class FunConst{
public:
const int f() { return 5; }
}
void main()
{ FunConst s; cons int i=s.f(); int y=s.f(); }
Funciones miembro en línea (inline)
Al igual que cualquier otra función de C++, las funciones miembro
pueden ser funciones en línea. Existen dos medios para hacer
funciones miembro en línea: aplicando la palabra reservada inline a
la definición o definiendo la función dentro de la declaración de la
clase.
class clase_xy {
int x,y;
public:
void Despliega_x (void) { cout << "X= " << x; } //Función
miembro en línea
void Despliega_y(void);
};
inline void clase_xy::Despliega_y(void) //Función miebro en línea
{
cout << "Y= " << y;
}
Constructores
En C++ la inicialización de objetos no se puede realizar en el
momento en que son declarados. Para ello se dispone de los
constructores. Un constructor es una función que sirve para
construir un nuevo objeto y/o inicializarlo, asignando valores a sus
miembros dato.
93
Manual del Alumno
Características de los constructores:
- Tiene el mismo nombre que la clase que inicializa.
- Puede definirse inline o fuera de la clase que inicializa.
- No devuelven valores
- Puede admitir parámetros como cualquier otra función.
- Pueden existir cero, uno o más constructores para una misma
clase.
class rebanada {
int x_centro, y_centro, angulo_i, angulo_f, radio;
public:
rebanada(int x, int y, int ai, int af, int r) } //Constructor
x_centro=x; y=centro=y; angulo_i= ai; angulo_f=af; radio=r;
}
...// Otros métodos
}
Los constructores pueden implicar diferentes escenarios: Crear
objetos por inicialización por defecto, crear objetos con
inicialización específica, crear objetos por copia de otro objeto.
Los constructores no se pueden declarar static o virtual. Se declaran
normalmente en la sección pública, pero pueden declararse en
cualquier parte de una clase. Si no se declara específicamente un
constructor, C++ inserta automática e invisiblemente un
constructor por defecto que no acepta argumentos. Este constructor
por defecto asigna espacio de almacenamiento para construir un
objeto de su clase y lo pone en ceros.
Constructores con argumentos La mayoría de los constructores en C++ toman argumentos. La
función básica de un constructor es inicializar un objeto antes de
utilizarlo. Se pueden declarar constructores múltiples, mientras
tomen tipos o números de argumentos.
class complejo {
private:
94
Manual del Alumno
double real,imag;
públic:
complejo() { real=0; imag=0 } //son constructores sobrecargados
complejo(double x) { real=x; imag=0; }
complejo( double x, double y ) { real=x; imag=y }
complejo(complejo& c) {real=c.real; imag=c.omag} //constructor
copiador
}
void main(void)
{
complejo c1;
complejo c2(1.2);
complejo c3(1.2, 2.4);
complejo c4=c2; //llama al constructor copiador
}
Mecanismo alternativo de paso de argumentos Consiste en inicializar miembros dato como parámetros. Los
constructores de complejo quedarían:
complejo(double x=0, double y=0) : real(x), imag(y) { }
complejo(complejo& c): real(c.real), imag(c.imag) { }
Constructores sobrecargados Un constructor puede tener el mismo nombre pero diferentes tipos
de datos como argumentos. El constructor correcto es llamado por
el compilador según sean los datos de sus argumentos. Ver ejemplo
complejo.
Constructores copiadores Es un constructor que crea un objeto a partir de uno ya existente.
Tiene sólo un argumento: una referencia constante a un argumento
de la misma clase.
class fibonacci {
public:
95
Manual del Alumno
fibonacci() { var1=0; var2=1; resultado= var1+var2; } //constructor
ordinario
fibonacci( const fibonacci &p ) { var1=p.var1; var2=p.var2;
resultado=p.resultado; }
}
Paso de objetos por valor Cuando se llama a una función que recibe a un objeto por valor, el
compilador utiliza el constructor de copias de los objetos en la pila
como un argumento. El paso por valor no significa que la función
obtenga necesariamente una copia byte a byte de un objeto.
void lee_complejo( Cuenta objeto)
{
cout << "El objeto Cuenta es: " << objeto.LeerValor());
}
void main(void)
{
Contador objeto(10);
informe_cuenta(objeto);
}
Creación de objetos
Forma abreviada: Complejo c1, c2(10), c3(3.4); Complejo c4=c1;
Forma explícita: Complejo c1=complejo(); Complejo
c2=complejo(10); Complejo
c3=complejo(3.4);
Destructores
Un destructor es una función miembro con igual nombre que la
clase, pero precedido por un caracter tilde ^ .Una clase sólo tiene
una función destructor, no tiene argumentos y no devuelve ningún
tipo. Al igual que las demás funciones miembro puede estar
definido dentro o fuera de la clase.
class String {
96
Manual del Alumno
private:
unsigned lon;
char *sstr;
public:
String(const char* s) { strcpy(sstr, s); } //constructor
^String() { delete[] sstr; } //destructor
// Otros miembros
}
Los destructores pueden ser públicos o privados, según si se
declaran en la parte private o public de la clase. Si es público se
puede llamar desde cualquier parte del programa para destruir el
objeto. Si es privado no se permite la destrucción del objeto por el
usuario.
Creación y supresión dinámica de objetos
Los operadores new y delete se pueden usar para crear y destruir
objetos de una clase, así como dentro de funciones constructoreas y
destructoras. la expresión new devuelve un apuntador a la dirección
base del objeto.
p=new int(9);
Cadena *Cad2 = new Cadena;
Un objeto creado con new se destruye explícitamente mediante el
operador delete. Si new falla al asignar almacenamiento, devuelve
el valor 0.
class cadena {
char *datos;
public:
cadena (int lon) { datos= new char [lon]; } //constructor
^cadena(void) { delete datos}; } //destructor
}
Argumentos por defecto (omisión)
97
Manual del Alumno
En C++, se pueden especificar los valores por defecto cuando se
proporciona un prototipo de una función. Estos argumentos por
defecto son proporcionados al compialdor si no se da ningún tipo
de argumento a la llamada a la función. Si se pasa un valor a uno de
los argumentos, se utiliza ese valor, si no se utiliza el valor por
defecto como argumento. Deben cumplirse las siguientes reglas:
- Los argumentos por defecto se pasan por valor, no por referencia.
- Los valores por defecto pueden ser valores literales o
declaraciones const. No pueden
ser variables.
- Todos los argumentos por defecto deben estar situados al final del
prototipo de la
función. Después del primer argumento, todos los demás
argumentos deben incluir
valores por defecto.
void Visualizar (int n, int base = 10 ) { cout << n << "," << base; }
v
void Visualizar (int n, int base = 10 ) { cout << n << "," << base; }
void f()
{
Visualizar(47);
Visualizar(47,10);
Visualizar(50, 16);
}
Otro ejemplo:
void funcion(int i, int j=2, k=3, h=4);
void main()
{
funcion (1);
funcion (0, 1);
}
98
Manual del Alumno
void funcion(int i, int j, int k, int h) { cout << i << "," << j << ","
<< k << "," << l; }
Funciones y clases Amigas
Una función amiga es una función no miembro de una clase que
puede tener acceso a los miembros privados de dicha clase. Estas
funciones se declaran situando su prototipo de función dentro de la
clase y mediante la palabra reservada friend. Asi, en la propia clase
se determina qué funciones son amigas de ella.
class Cosa {
friend void cargar_dato( Cosa &, int); // función amiga
private:
int dato;
public:
Cosa (int d) { dato=d ; }
void prinf() { cout << "Dato = " << dato; }
}
void cargar_dato (Cosa & t, int d) { t.dato=x; } void carga_otro_dato(Cosa &, int );
void main(void)
{
Cosa objeto1(0);
Cosa *ap_objeto;
ap_objeto = new cosa (1);
cargar_dato(objeto1, 2);
objeto1.print();
ap_objeto->print();
cargar_otro_dato(objeto1, 3);
}
void caga_otro_dato(Cosa & c, int y)
{
c.dato = y; // ERROR. NO ES FUNCION AMIGA
99
Manual del Alumno
}
Además de las funciones amigas, se pueden tener clases completas
como amigas de otra clase. En este caso, todas las funciones de la
clase declarada como amiga pueden accesar las partes privadas de
la otra clase. También se utiliza la palabra friend para declarar una
clase como amiga de otra. Obviamente, la clase amiga debe estar
declarada.
class nodo_bin {
friend arbol_bin ;
int inf;
nodo_bin *hizq, *hder, *padre;
public:
nodo_bin(T dato) { inf=dato; hizq=hder=padre=NULL; bal=0; }
nodo_bin *Leer_hizq() { return hizq; }
nodo_bin *Leer_hder() { return hder; }
}
class arbol_bin {
nodo_bin *raiz;
public:
arbol_bin() { raiz=NULL; };
nodo_bin Raiz() {return raiz; }
T info(nodo_bin *nodo) { return nodo->Leer_inf(); }
// nodo_bin * Left(nodo_bin *nodo) { return nodo->Leer_hizq(); }
nodo_bin * Left(nodo_bin *nodo) { return nodo->hizq; }
//nodo_bin * Right(nodo_bin *nodo) { return nodo->Leer_hder(); }
nodo_bin * Right(nodo_bin *nodo) { return nodo->hder; }
};
Sobrecarga de Funciones
El término sobrecarga se refiere al uso de un mismo nombre para
múltiples significados de un operador o función. En C++ es posible
dar el mismo nombre a varias funciones, la decisión de ejecutar una
100
Manual del Alumno
u otra la realiza el compilador de acuerdo al tipo y/o número de
argumentos. Los prototipos de estas funciones especifican sus tipos
de retorno y argumentos para cada función en particular.
#include
int cuadrado (int i) { return i*i; }
float cuadrado (float d) { return d*d; }
long cuadrado (long l) { return l*l; }
void main(void)
{ int x;
long y;
float z;
x = cuadrado(5);
y = cuadrado(50000);
z = cuadrado(3.1416);
}
Las funciones sobrecargadas pueden ser miembros de alguna clase
o no. Si una función sobrecargada no es miembro de alguna clase,
su ámbito es todo el programa.
#include
void Visualizar(char *string) { cout << string ; }
void Visualizar(long valor) { cout << valor ; }
void Visualizar(double valor) { cout << valot ; }
void main(void)
{ Visualizar("\n Este es un \"string\" ");
Visualizar(1234567890);
Visualizar(3.141592);
}
Funciones miembro sobrecargadas El principal uso de la sobrecarga es con las funciones miembro de
una clase. Cuando más de una función miembro con igual nombre
se declara en una clase, se dice que el nombre de la función está
sobrecargado en esa clase. El ámbito del nombre sobrecargado es el
101
Manual del Alumno
de la clase. Es importante recordar que, los constructores, como
funciones miembros de una clase, son susceptibles de
sobrecargarse.
class figura {
char *tipo;
float Area;
int color;
public:
char * LeeAtributo() { return tipo; }
float LeeAtributo() { return Area; }
int LeeAtributo () { return color; }
void PonAtributo(int c) { color = c ; }
void PonAtributo(float A, int c) { Area= A; color=c ; }
}
Funciones amigas sobrecargadas La sobrecarga de funciones amigas es similar a la de funciones
miembro con la diferencia de que es necesaria la palabra reservada
friend al igual que en la declaración de cualquier función amiga.
class figura {
char *tipo;
float Area;
int color;
public:
char * LeeAtributo() { return tipo; }
float LeeAtributo() { return Area; }
int LeeAtributo () { return color; }
friend PonAtributo(int);
friend PonAtributo(float, int );
};
void PonAtributo(int c) { color = c ; }
void PonAtributo(float A, int c) { Area= A; color=c ; }
102
Manual del Alumno
Las funciones sobrecargadas necesitan diferir por lo menos en uno
de los siguientes puntos:
- Deben recibir un número diferente de argumentos.
- Al menos uno de los argumentos debe ser diferente en cuanto a su
tipo.
La única función miembro que no se puede sobrecargar es el
destructor, debido a las reglas con que éstos se forman.
La sobrecarga de funciones evita la difícil tarea de dar nombres
diferentes a operaciones similares. Las operaciones similares
pueden tener un mismo nombre sin que haya conflictos, siempre y
cuando se defina apropiadamente la sobrecarga de las funciones
correspondientes.
Sobrecarga de Operadores
La sobrecarga de operadores, de modo similar que la sobrecarga de
funciones, permite al programador dar nuevos significados a los
símbolos de los operadores ya existentes en C++ para tipos de
datos abstractos (tipos de datos definidos por el programador). El resultado puede ser la creación de un código más fácil de leer y
depurar, con la desventaja de que si no se utiliza con el cuidado
suficiente pueden producirse graves problemas a sus programas.
La sobrecarga es una de las características más importantes de C++
y de la programación orientada a objetos. Con ella se puede
redefinir el lenguaje C++ y proporciona a los programadores una
herramienta que les facilitará manipular los tipos definidos por el
usuario y nuevos tipos de datos abstractos de igual forma como se
hace con los tipos incorporados al lenguaje.
Así por ejemplo, se pueden utilizar expresiones tales como:
objeto_1 = objeto_1 + objeto_2;
objeto_1 = objeto_1 * objeto_2;
103
Manual del Alumno
Operadores que se pueden Sobrecargar
Unarios ++ , --
Binarios / , % , () , [] , new , delete , += , -= , *= , /= , | , ^ , || , && ,
< , <= , > , >= , << , >> ,
|= , ^= , &= , <<= , >>= , == , = , -> , = , %= , ´ , ->*
Unarios o Binarios + , - , * , &
No está por demás mencionar que un operador unitario actúa sobre
un sólo objeto y un operador binario sobre dos objetos.
Operadores que no se pueden sobrecargar . , .* , :: , ?: , sizeof
La sobrecarga de un operador es una función incorporada a C++
que permite al programador sustituir alguno de los operadores ya
existentes por una función definida por el usuario. Las funciones
operadoras sobrecargadas se eligen por el compilador utilizando el
mismo mecanismo que se utiliza para elegir entre diferentes
funciones sobrecargadas. Cuando se encuentra un operador no
intrínseco, el compilador busca una función operador busca una
función operador que tenga parámetros que correspondan con los
operandos que se encuentran. Las funciones operador tienen la
misma prioridad que las operaciones intrínsecas que utilizan el
mismo operador. Por ejemplo: wl operador * siempre tiene mayor
prioridad que el operador +.
Restricciones en los operadores sobrecargados 1.- Sólo se pueden sobrecargar operadores definidos en C++.
2.- La sobrecarga de operadores sólo funciona con objetos del
alguna clase.
3.- No se puede cambiar la preferencia o asociatividad de los
operadores definidos en C++.
4.- No se puede hacer funcionar a un operador binario como
unitario ni viceversa.
5.- No se puede sobre cargar un operador que funcione
exclusivamente con apuntadores.
104
Manual del Alumno
6.- Es responsabilidad del programador definir correctamente el
significado de las funciones sobrecargadas.
7.- No se pueden derivar operadores sobrecargados complejos de
otros más simples. Por ej: Si se definen operator* y operator= y se
desea realizar a*=b, es necesario
sobrecargar el operador *=.
Mecanismo de sobrecarga de operadores
El formato general de la sobrecarga de un operador es:
tipo nombre_clase :: operator <op> (lista_de_args) { . . . }
tipo es tipo de valor devuelto por la operación especificada
(función operador). <op> es el operador que se está sobrecargando.
Las funciones operador deben ser miembros o amigas de la clase
que se está utilizando.
Sobrecarga de Operadores Unitarios
Ejemplo:
class vector {
double x, y; public:
vector (double xi, double yi) { x=xi; y=y1; }
void Visualizar() { cout << "Vector x= " << x << " y = " y; }
doble operator++()
{x++; y++;}
};
void main(void)
{ vector v1(10.5, 20.6);
v1.Visualizar();
v1++;
v1.Visualizar();
}
Con v++; se llama la función operator++() que incrementa los
datos del objeto vector v1 en 1.
105
Manual del Alumno
Sobrecarga de los operadores ++ y -- en forma prefija y posfija. La versión posfija de los operadores ++ y -- sobrecargados dentro
de una clase recibe un argumento int, mientras que la versión
prefija no.
Ejemplo:
class vector {
int x, y;
public:
vector() {x=0; y=0; }
vector& operator++() { x+=2; y+=2; return *this; } //prefija
vector& operator++(int) { x++; y++; return *this; } //posfija
ImprimeVector() { cout << "v " << x << " " << y << endl; }
} ;
void main(void)
{
vector v1;
v1++; // Se ejecuta el método operator++(int)
v1.ImpVector();
++v1; // Se ejecuta el método operator++()
v1.ImpVector();
v1=v1.operator++(); //Equivale a ++v1;
v1.ImpVector();
v1=v1.operator++(1); //Equivale a v1++;
v1.ImpVector();
}
Sobrecarga de operadores unitarios que no son miembros de
alguna clase class vector {
Int x, y;
public:
vector() {x=0; y=0; }
void Incrementa(int incr) { x+=incr; y+=incr; }
106
Manual del Alumno
ImprimeVector() { cout << "v " << x << " " << y << endl; }
} ;
//posfija
vector& operator++(vector& vaux, int ) { vaux.Incrementa(1);
return vaux; }
//prefija
vector& operator++(vector& vaux) { vaux.Incrementa(2); return
vaux; }
void main(void)
{
vector v1;
v1++; // Se ejecuta la función operator++(int)
v1.ImpVector();
++v1; // Se ejecuta la función operator++()
v1.ImpVector();
v1=operator++(v1); // Equivale a ++v1;
v1.ImpVector();
v1=operator++(v1, 0); //Equivale a v1++;
v1.ImpVector(); }
Sobrecarga de Operadores Binarios
Los operadores binarios se pueden sobrecargar tan fácilmente como
los operadores unitarios.
Ejemplo:
class vector {
int x, y;
public:
vector(int xi = 0, yi = 0) {x = xi; y = yi; }
ImprimeVector() { cout << "v " << x << " " << y << endl; }
vector operator+(vector v) { vector vaux; vaux.x= x + v.x; vaux.y =
y + v.y; return vaux; }
107
Manual del Alumno
vector& operator-(vector& v) { vector *vaux= new vector; vaux-
>x= x - v.x; vaux->y = y - v.y;
return *vaux; }
};
void main(void)
{
vector v1, v2(2,3), v3(4,6);
v1 = v2 + v3; //Se ejecuta el método operator+(vector v)
v1.ImpVector();
v1 = v2 - v3; //Se ejecuta el método operator-(vector &v)
v1.ImpVector();
}
Sobrecarga del Operador de Asignación
class vector {
int x, y;
public:
vector() {x=0; y=0; } vector(int xi = 0, yi = 0) {x = xi; y = yi; }
vector& operator=(vector& v) {x=v.x; y=v.y; return *this; }
void Incrementa(int incr) { x+=incr; y+=incr; }
ImprimeVector() { cout << "v " << x << " " << y << endl; }
} ;
void main(void)
{
vector v1, v2(2,3), v3(4,6);
v1 = v2 ; //Se ejecuta el método operator=(vector v&)
v1.ImpVector();
v1 = v3; //Se ejecuta el método operator=(vector &v)
v1.ImpVector();
}
108
Manual del Alumno
Sobrecarga del operador de llamada a función ( ) y el operador
subíndice [ ] La llamada a función se considera como un operador
binario. La función operador correspondiente es operator() y puede
ser definida por el usuario para una clase (y cualquier clase
derivada) sólo mediante una función miembro no estática.
El operador [ ] se utiliza normalmente como índice.
Ejemplo:
class string {
char *sPtr;
int length;
public:
String(const String &); // Constructor
String &operator() (int, int); //regresa un substring
char &operator[](int); //regresa una referencia a char
};
String::String(const char *s) { length=strlen(s); sPtr= new
char[length+1]; strcpy(sPtr, s); }
char * String::operator()(int c) { return(strchr(sPtr, c)); }
char & String::operator[](int i) { return sPtr[i]; }
void main(void)
{
String s1("Hola Mundo!");
cout <};
TEMPLATES (PLANTILLAS)
C++ tiene una característica llamada template que permite crear
tipos parametrizados o declarar una familia completa de clases o
funciones en lugar de sólo una.
Clases Genéricas
El propósito de la genericidad es definir una clase o una función sin
especificar el tipo de uno o más de sus miembros (parámetros). A
una clase de este tipo se le llama clase contenedora. La genericidad
109
Manual del Alumno
se puede emular utilizando herencia y polimorfismo, A
continuación veremos un
Ejemplo.
#include
#include
#include
class ObjetoGenerico {
public:
virtual void Dibuja() {};
};
class Circulo : public ObjetoGenerico {
int x, y, r;
public:
Circulo(int xi=100, int yi=100, int ri=100) { x=xi ; y=yi; r=ri;}
void Dibuja() { circle(x, y, r); }
};
class Elipse : public ObjetoGenerico {
int x, y, rx, ry;
public:
Elipse(int xi=100, int yi=100, int rxi=60, int ryi=80) { x=xi; y=yi;
rx=rxi; ry=ryi; }
void Dibuja() { ellipse(x, y, 0, 360, rx, ry) ; }
};
class Rectangulo : public ObjetoGenerico {
int xizq, yarriba, ancho, alto;
public:
Rectangulo(int xi=10, int yi=10, int an=40, int al=60) { xizq=xi;
yarriba=yi;ancho=an; alto=al; }
void Dibuja() { rectangle(xizq, yarriba, xizq+ancho, yarriba+alto);
}
};
class Nodo {
110
Manual del Alumno
ObjetoGenerico *figura;
Nodo *sig;
public:
Nodo(ObjetoGenerico *fig) { figura=fig; }
Nodo * RecuperaSiguiente() { return sig; }
void PonerSiguiente(Nodo *nodosig) { sig=nodosig; }
void Muestra(){ figura->Dibuja(); }
};
class Lista {
Nodo *cabeza;
public:
Lista(){ cabeza=new Nodo(NULL); cabeza-
>PonerSiguiente(NULL); }
void Insertar (ObjetoGenerico *fig);
void Desplegar();
};
void Lista::Insertar(ObjetoGenerico *fig)
{
Nodo *aux;
for(aux=cabeza; aux->RecuperaSiguiente()!=NULL; aux=aux-
>RecuperaSiguiente());
Nodo *nuevo = new Nodo(fig);
nuevo->PonerSiguiente(NULL);
aux->PonerSiguiente(nuevo);
}
void Lista::Desplegar()
{ Nodo *aux;
for(aux=cabeza->RecuperaSiguiente(); aux!=NULL; aux=aux-
>RecuperaSiguiente())
aux->Muestra();
}
void main()
111
Manual del Alumno
{
int graphdriver = DETECT, graphmode;
initgraph(&graphdriver, &graphmode, "..\\bgi");
Lista ListaFig;
Circulo *c1 = new Circulo(50,50,80);
ListaFig.Insertar(c1);
Elipse *e1 = new Elipse;
ListaFig.Insertar(e1);
Rectangulo *r1 = new Rectangulo(30,50);
ListaFig.Insertar(r1);
ListaFig.Desplegar();
closegraph();
}
Templates en Funciones
Los templates nos proporcionan una solución elegante que nos
evita escribir varias veces el mismo código para una función.
Especifica un conjunto infinito de funciones sobrecargadas, pero
permitiendo realizar la verificación de los tipos efectuados por
C++.
Formato:
template declaración o definición de funciones
Los paramétros puden ser de dos categorías: tipos y constantes.
Los tipos se representan poneindo el prefijo class. Sin el prefijo,
se trata de cómo constante.
Ejemplos:
template <class T>
template <int LongArray, int LongElem>
template <class TipoElem, long Longitud, class TipoComparador>
Ejemplo:
#include
template T min(T a, T b) { return a < b ? a: b; }
112
Manual del Alumno
void main()
{ int ia=1, ib=5; cout << "Menor: " << min(ia, ib) << endl;
char la=‘a’, lb=‘n’; cout << "Menor: " << min(la, lb) << endl;
float fa=88.4, fb=19.9; cout << "Menor: " << min(fa, fb) << endl;
}
Templates en Clases
Nos permiten definir clases genéricas que pueden manipular varios
tipos de datos. Son útiles para implementar contenedores que son
clasesque contienen objetos de un tipo dado: las clases
contenedores permiten administrar listas ligadas de objetos, tablas
cuyo tamaño puede variar dinámicamente, conjuntos, etc.
Ejemplo:
#include
template class stackvector {
T *data; int tope;
unsigned int size;
public:
stackvector (unsigned int tam);
T pop();
void push(T value);
};
template stackvector :: stackvector(unsigned int tam): size(tam)
{ data = new T[size]; tope = size +1; }
template void stackvector :: push(T val)
{ if ( tope == 1) cout << "Pila llena";
else data[--tope]=val;
}
template void stackvector :: pop()
113
Manual del Alumno
{ if (tope == (size+1) return -1;
else return data[tope +1];
}
void main()
{
stackvector st(5);
st.push(5);
st.push(3);
st.push(2);
for(i=0; i<3; i++) cout << " " << st.pop();
stackvector st(6);
st.push(3.1416);
st.push(1.7178);
st.push(0.0001);
st.push(12345.123);
for(i=0; i<3; i++) cout << " "<< st.pop();
}
Ejemplo:
class alfa {
public:
double suma; //funciones púlicas
};
class beta: public alfa {
public:
int n; //funciones públicas
};
Clases de Derivación
Derivación públca (public clase base)
Todos los miembros public y protected de la clase base son
accesibles en la clase derivada, mientras que los miembros private
de la clase base son inaccesibles en la clase derivada.
114
Manual del Alumno
class Base {
public:
void f();
int a, b;
private:
float c, d;
};
class Derivada: public Base {
public:
void g();
};
void main()
{ Derivada d;
d.a=50;
d.b=100;
d.f();
d.c=100; //Error: miembro privado de la clase base
}
Derivación Privada (private clase base)
Todos los miembros de la clase base se comportan como miembros
privados de la clase derivada. En este caso, los miembros public y
protected de la clase base no son accesibles mas que por las
funciones miembro de la clase derivada. Los miembros privados de
la clase base siguen siendo incaccesibles desde la clase derivada.
class Base {
public:
void f1();
void f2();
int a, b;
private:
float c, d;
115
Manual del Alumno
};
class Derivada: private Base {
public:
Base::f1(); //acceso autorizado de modo explícito
Base::a;
};
void main()
{ Derivada d;
d.a =50; //Válido
d.b=100; //Error, acceso prohibido
d.f1(); // Válido
d.f2(); //Error, acceso prohibido
d.c=100, //Error, miembro privado de la clase
}
Derivación Protegida (public clase base)
Se utiliza de modo similar a la derivación private. Todos los
miembros public y protected de la clase base se comportan como
miembros protected en la clase derivada. Estos miembros no son
pues, accesibles al programa exterior, pero las clases que se deriven
a continuación podrán acceder normalmente a estos miembros.
class Derivada: protected Base {
public:
// ...
};
Ventajas e inconvenientes de la derivación privada y protegida
Las derivaciones private y protected son útiles cuando se desea
asegurar que solamente la interfase de nuestra clase derivada pueda
ser utilizada por el programa principal y/o por las otras clases
derivadas de ésta. Como inveconvenientes, la utilización de las
derivaciones private y protected complican el árbol de herencia,
volviéndose más compleja la determinación de derecho de acceso.
116
Manual del Alumno
a los miembros de cada clase, a partir de la simple lectura de su
programa.
Es aconsejable usar estos dos tipos de derivación en que puedan
existir razones de peso para su uso. Cuando no se utiliza un
especificador de acceso, C++ considera por defecto una derivación
privada.
class Derivada: Base { //La derivación es privada por defecto
public:
// ...
};
Jerarquia de Clases
class ObjetoGenerico {
public:
virtual void Dibuja() {};
};
class Circulo : public ObjetoGenerico {
int x, y, r;
public:
Circulo(int xi=100, int yi=100, int ri=100) { x=xi ; y=yi; r=ri;}
void Dibuja() { circle(x, y, r); }
};
class Elipse : public ObjetoGenerico {
int x, y, rx, ry;
public:
Elipse(int xi=100, int yi=100, int rxi=60, int ryi=80) { x=xi; y=yi;
rx=rxi; ry=ryi; }
void Dibuja() { ellipse(x, y, 0, 360, rx, ry) ; }
};
class Rectangulo : public ObjetoGenerico {
int xizq, yarriba, ancho, alto;
public:
117
Manual del Alumno
Rectangulo(int xi=10, int yi=10, int an=40, int al=60) { xizq=xi;
yarriba=yi; ancho=an; alto=al; }
void Dibuja() { rectangle(xizq, yarriba, xizq+ancho, yarriba+alto);
}
};
Herencia Múltiple
La herencia múltiple es la propiedad con la cual una clase derivada
puede tener más de una clase base o padre. También se conoce
como derivación de clase con clase base múltiple.
class ObjetoGenerico {
public:
virtual void Dibuja() {};
};
class PropiedadesGenerales {
protected: char etiqueta[32];
public:
char TipoFigura[32];
void PonerTipoFigura(int tipo) {
if(tipo == 1) strcpy((char *)TipoFigura, (const char *)"Circulo");
if(tipo == 2) strcpy((char *)TipoFigura, (const char *)"Elipse");
if(tipo == 3) strcpy((char *)TipoFigura, (const char
*)"Rectángulo");
}
};
class Circulo : public ObjetoGenerico, public
PropiedadesGenerales {
int x, y, r;
public:
118
Manual del Alumno
Circulo(int xi=100, int yi=100, int ri=100, char *etiq) { x=xi ; y=yi;
r=ri;
strcpy((char *)etiqueta, (const char *)etiq); }
void Dibuja() { circle(x, y, r); }
};
void main()
{
Circulo *c1 = new Circulo(50,50,80, "MiCirculo");
c1->PonerTipoFigura(1);
}
Ambigüedades en herencia múltiple
Una de las ambigüedades más comunes se da cuando dos clases
base tienen funciones con el mismo nombre, y sin embargo, una
clase derivada de ambas no tiene una función con ese nombre. Para
resolver la ambigüedad se usa el operador de ámbito.
class A {
public:
void mostrar() { cout << "\n Clase A"; }
};
class B {
public:
void mostrar() { cout << "\n Clase B"; }
};
clase C : public A, public B
{ };
void main()
{
C obietoC; // Se crea un objeto de la clase C
objetoC.mostrar( ) // Error. Se tiene ambigüedad
objetoC.A::mostrar( ); // Correcto
119
Manual del Alumno
objetoC.B::mostrar( ); // Correcto }
Constructores y destructores en herencia múltiple
Durante la construcción de un objeto perteneciente a una clase
derivada , se llaman sucesivamente los constructores de sus clases
base en el orden de sus declaraciones. Al final se llama el
constructor de la clase derivada.
class A {
protected:
int x;
void f(int i) { x = i ; }
public:
A(int a = 1) { x = a ; }
};
class B {
protected:
int y;
void g(int i) { y = i ; }
public:
B(int b = 1) { x = b ; }
};
class C: public A, public B {
protected:
int z;
void h(int i) { cout << x << " " << y << " " << z ; }
public:
C(int c = 1) ;
};
C::C(int c) : A( 2 * a ) , B( 3 * a)
{ x = c ; }
void main()
{
C ObjetoC(5) ;
120
Manual del Alumno
ObjetoC.h() ; }