triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los...

153
Estructura de un programa en C++ El formato general de un programa en C++ es el siguiente : / | Definición de constantes y macros, ENCABEZADO < declaración de variables globales, | inclusión de archivos. \ void main(void) <---linea de cabecera de la función principal. { <-------------Inicio de bloque. ......... ......... ......... <---Cuerpo de la función principal. ......... } <-------------Fin de bloque. En el listado 1.1 se muestra un programa que despliega en la pantalla del monitor el mensaje: El Lenguaje C++ es poderoso // PRIMER.CPP // // Despliega un mensaje en la pantalla. #include <stdio.h> // Para utilizar la función printf() void main(void) { printf("El lenguaje C++ es poderoso\n"); } Listado 1.1.- Primer programa en C++

Transcript of triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los...

Page 1: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Estructura de un programa en C++

El formato general de un programa en C++ es el siguiente :

/ | Definición de constantes y macros, ENCABEZADO < declaración de variables globales, | inclusión de archivos. \

void main(void) <---linea de cabecera de la función principal. { <-------------Inicio de bloque. ......... ......... ......... <---Cuerpo de la función principal. ......... } <-------------Fin de bloque.

En el listado 1.1 se muestra un programa que despliega en la pantalla del monitor el mensaje:El Lenguaje C++ es poderoso

// PRIMER.CPP // // Despliega un mensaje en la pantalla.

#include <stdio.h> // Para utilizar la función printf()

void main(void) { printf("El lenguaje C++ es poderoso\n"); }

Listado 1.1.- Primer programa en C++

Page 2: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

El listado 1.1 contiene la línea :

#include <stdio.h>En ella, stdio.h es el nombre de un archivo de cabecera en el cual se encuentran declarados los prototipos de las funciones y macros que se utilizan para la entrada/salida estándar.Existe un extenso grupo de archivos de cabecera, y cada uno de ellos sirve para manejar un grupo de funciones relacionadas. La extensión h de estos archivos es la inicial de la palabra header.Generalmente estos archivos se incluyen en las primeras líneas de los archivos que contienen el código fuente de los programas en C++ .El compilador de C++ desplegará un mensaje de error cuando se trate de utilizar una función sin antes haber incluido el archivo de cabecera que contiene el prototipo de ella.

Los archivos de cabecera son almacenados en el directorio INCLUDE cuando se instala el paquete del compilador.

1.3.- Comentarios.

En el C++ existen dos formas de manejar los comentarios:

La primera sirve para escribir comentarios que abarcan varias líneas, y utiliza los caracteres /* para marcar el inicio de un comentario, y los caracteres */ para marcar el final, como se ve en los siguientes ejemplos:

/* Este es un pequeño comentario */

/* Este es otro comentario, pero a diferencia del

Page 3: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

anterior, utiliza varias líneas.

Observe que también puede incluir líneas en blanco (como la anterior y la siguiente).

*/

La segunda forma solo sirve para comentarios que abarcan una línea o parte de ella, y utiliza los caracteres // al principio del comentario. El fin del comentario queda marcado por el fin de la línea, como se muestra a continuación:

// Este es un comentario de una línea, // y este es otro.

void main(void) // Este comentario ocupa parte de la línea.

Los comentarios de una sola línea pueden anidarse dentro de los comentarios de varias líneas, como en :

/* El principio del comentario de varias líneas.

// Primer comentario de una sola línea.

// Segundo comentario de una sola línea. */ Fin del comentario de varias líneas.

El preprocesador es un programa que viene incluido en los compiladores de C++, y se encarga de ejecutar ciertas órdenes llamadas DIRECTIVAS DEL PREPROCESADOR. Estas directivas empiezan con el carácter # ( Numeral ). Al inicio de la compilación, el compilador invoca al preprocesador, quien actúa de acuerdo a la directiva correspondiente.

Las directivas del preprocesador pueden clasificarse en tres grupos principales :

1.4.1.- Inclusión de Archivos. 1.4.2.- Reemplazamiento de Cadenas y Procesamiento de Macros. 1.4.3.- Compilación Condicional.

1.4.1.- Inclusión de archivos

Pueden incluirse los contenidos de otros archivos de texto en el archivo que contiene código en C++ . Esto se logra por medio de la directiva #include en cualquiera de las tres formas siguientes :

Page 4: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

#include <nombre_archivo> /* nombre_archivo se buscará solamente en el directorio INCLUDE */

#include "nombre_archivo" /* nombre_archivo se buscará en el directorio de trabajo actual */

#include "d:\trayectoria\nombre_archivo" /* En este caso, nombre_archivo será buscado en el directorio especificado en trayectoria */

Ejemplo:

#include "C:\DATOSCPP\PANTALLA.C"

1.4.2.- Reemplazamiento de cadenas y procesamiento de macros

El reemplazamiento de cadenas consiste en sustituir (antes de efectuar la compilación) cada una de las ocurrencias de cierta cadena (llamada constante simbólica) por otra cadena equivalente. Esto de logra por medio de la directiva #define, como se muestra en los siguientes ejemplos:

#include <stdio.h> #define CADENA "El Lenguaje C++ es poderoso\n" void main(void) { printf(CADENA); }

En este caso, el preprocesador sustituirá CADENA por El Lenguaje C++ es poderoso, de tal manera que el compilador se encontrará con la instrucción: printf("El Lenguaje C++ es poderoso\n");

#define VALMAX 1000

Aquí, se está definiendo la constante simbólica VALMAX, cuyo valor será el entero 1000.

La directiva #define también sirve para declarar macros, las cuales incluyen una ó más instrucciones del Lenguaje C++ , como en:

Page 5: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

#define CUBO(N) ((N)*(N)*(N))

Esta macro puede utilizarse en una instrucción de la siguiente forma:

r = CUBO(5);

El preprocesador sustituye lo anterior por:

r = ((5)*(5)*(5));

Las macros pueden tomar formas complejas y ocupar varias líneas de código, como se muestra en el siguiente ejemplo:

#define REDOND(x) ((x)>=0?((x)+1/VALOR1)*(1-1/VALOR1): \ ((x)-1/VALOR1)*(1+1/VALOR1))

Obsérvese que al final de la primera línea se escribió una diagonal invertida ( \ ), la cual

1.4.3.- Compilación condicional

Existe un grupo de directivas del preprocesador que permiten controlar cuales partes del código fuente se van a compilar al cumplirse ciertas condiciones. A continuación se muestra una lista de tales directivas; dejándose la ejemplificación de su uso para unidades más avanzadas de este trabajo.

#if#ifdef #if defined(algo)#ifndef#else#elif#endif

Además de las directivas vistas hasta aquí, existen las siguientes:

#error#pragma inline#pragma warn#pragma saveregs

1.5.- La Función main()

Page 6: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Todo programa de C++ debe contener una función llamada main() (principal), la cual sirve de enlace con el sistema operativo que se esté utilizando. Cuando se intente obtener un programa ejecutable(.EXE ) a partir de un archivo que no contenga una función main(), el compilador desplegará un mensaje de error.

Al invocar a un programa escrito en C++, desde el indicador de presencia del sistema operativo, se le pueden pasar ciertos valores que se llaman argumentos, los cuales van a ser recibidos por la función principal. Esta, a su vez, va a regresar un valor de retorno al sistema operativo que la invocó. La devolución del valor de retorno se hace por medio de la instrucción return, por ejemplo:

return 0 ;

La explicación acerca de la utilización de argumentos en la función main() requiere del conocimiento de apuntadores, por lo que se tratará en la unidad 6.

.6.- Identificadores

Los nombres que son utilizados para referenciar variables, funciones, etiquetas y demás objetos que se manejan con el Lenguaje C++, son referidos en forma genérica como identificadores.

1.6.1.- Identificadores Creados por el Usuario.1.6.2.- Palabras Reservadas.

1.6.1.- Identificadores creados por el usuario

El usuario debe cuidar que los identificadores creados por él cumplan las siguientes:

Reglas generales para los identificadores:

1.- Pueden estar formados por:

a.- Letras mayúsculas y/o minúsculas. b.- El carácter de subrayado. c.- Los dígitos del 0 al 9.

2.- El primer carácter debe ser una letra o el carácter de subrayado.

Page 7: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

3.- Pueden contener un número arbitrario de caracteres; pero Borland C++ considera como significativos los primeros 32 o los n primeros que decida el programador, donde:

1 <= n <="32"

4.- Se toma en cuenta la diferencia entre letras mayúsculas y minúsculas, por lo que : SUMA, Suma y suma son identificadores diferentes.

5.- No puede utilizarse el mismo identificador para dos objetos que se encuentren dentro del mismo ámbito.

1.6.2.- Palabras reservadas

Las palabras reservadas, como su nombre lo indica, son identificadores que tienen asignado un uso específico y no podrán ser utilizadas como identificadores creados por el usuario.

La tabla 1.1 muestra las palabras reservadas de Borland C++

_asm @ _ds int @ _seg + asm else _interrupt short auto enum @ interrupt signed break @ _es @ _loadds sizeof case @ _export long @ _ss

Page 8: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

@ _cdecl extern @ _near static @ cdecl @ _far @ near struct char @ far + new switch + class @ _fastcall + operator + template const float @ _pascal + this continue for @ pascal typedef @ _cs + friend + private union default goto + protected unsigned + delete _huge + public + virtual do @ huge register void double if return volatile + inline @ _saveregs while

Tabla 1.1.- Palabras reservadas de Borland C++.

Las palabras marcadas con + en la tabla 1.1 son las palabras reservadas específicas de C++, las marcadas con @ son las palabras añadidas por Borland al C, y las palabras sin marca son las palabras reservadas del C estándar.

1.7.- Entrada/Salida

En el Lenguaje C++, la entrada/salida se maneja de dos formas.

La primera consiste en la utilización de las funciones cuyos prototipos vienen declarados en el archivo stdio.h, y es la forma que tradicionalmente ha utilizado el Lenguaje C.

La segunda es propia del C++ y consiste en el manejo de objetos y operadores cuyas definiciones se encuentran en el archivo iostream.h .

A continuación explicaremos cada una de estas dos formas.

1.7.1.- Funciones Declaradas en stdio.h 1.7.2.- Funciones Declaradas en conio.h 1.7.3.- Objetos Declarados en iostream.h

1.7.1.- Funciones declaradas en stdio.h

En esta forma, se requiere la inclusión de la siguiente línea al principio del programa fuente:

Page 9: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

#include &ltstdio.h>

En el archivo stdio.h se encuentran definidos, entre otros, los prototipos de las funciones printf(), scanf(), gets() y puts() además de las macros getc(x) y putc(). A continuación analizaremos cada una de estas funciones y macros:

1.7.1.1.- La Función printf() 1.7.1.2.- La Función scanf() 1.7.1.3.- Las Funciones gets() y puts() 1.7.1.4.- Las Macros getc() y putc()

1.7.1.1.- La función printf()

Esta función es la mas utilizada en C para desplegar información en la pantalla, más no en el lenguaje C++ como veremos más adelante.

El formato general para la función printf() es:

printf(<cadena_formato> , <elem> , <elem> , ... );

donde:

cadena_formato es una cadena de caracteres encerrada entre comillas dobles. Esta cadena es enviada a la pantalla ( la salida estándar ) después de haber sustituidos los elementos listados después de la cadena.

La sustitución de los elementos se hace de acuerdo a los comandos de formato incluidos en cadena_formato, los cuales empiezan con un símbolo de porciento ( % ) .

En la tabla 1.2 se muestran los comandos de formato más usuales:

Comando Sirve para:%d entero%u entero sin signo%ld entero largo%p valor de apuntador%f número de punto flotante%e número de punto flotante en valor exponencial

Page 10: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

%c carácter%s cadena de caracteres%x entero en formato hexadecimal%o entero en formato octal

Tabla 1.2.- Comandos de formato.

A fin de poder mostrar algunos ejemplos de aplicación de la función printf(), se presenta aquí la tabla 1.3 que contiene los tipos de datos disponibles en el compilador de Borland C++ 3.1.

Tipo Tamaño (bits) Precisión Rango de valores:char 8 . -128 a 127

unsigned char 8 . 0 a 255enum 16 . -32,768 a 32,767

int 16 . -32,768 a 32,767short int 16 . -32,768 a 32,767

unsigned int 16 . 0 a 65,535long 32 . -2,147'483,648 a 2,147'483,647

unsigned long 32 . 0 a 4,294'967,295float 32 7 dígitos 3.4E-38 a 3.4E+38

double 64 15 digitos 1.7E-308 a 1.7E+308long double 80 19 digitos 3.4E-4932 a 3.4E+3932

void . . .near pointer 16 . Direcciones de memoriafar pointer 16 . Direcciones de memoria

Tabla 1.3.- Tipos de datos, tamaños y rangos en Borland C++ 3.1

Los listados 1.2 a 1.8 muestran ejemplos de utilización de formatos por medio de la función

// IMPRIME ENTEROS EN "DECIMAL" #include <stdio.h> int main()

Page 11: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

{ int y=12345; printf("%13d\n",y); return 0; }

Listado 1.2

// IMPRIME ENTEROS EN OCTAL #include <stdio.h> int main() { int y=12345; printf("y = %13d EN DECIMAL\n",y); printf("y = %13o EN OCTAL\n",y); return 0; }

Listado 1.3

// IMPRIME ENTEROS EN HEXADECIMAL #include <stdio.h> int main() { int y=12345; printf("y = %13d EN DECIMAL\n",y); printf("y = %13x EN HEXADECIMAL\n",y); return 0; }

Listado 1.4

// IMPRIME ENTEROS SIN SIGNO #include <stdio.h> int main() { int y= -12345; printf("y = %13d ENTERO CON SIGNO\n",y); printf("y = %13u ENTERO SIN SIGNO\n",y); return 0; }

Listado 1.5

// IMPRIME NUMEROS CON PUNTO FLOTANTE #include <stdio.h> int main() { float x=123.45; double y=678.90;

Page 12: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

printf("x = %12.2f\ny = %12.1f\n",x,y); return 0; }

Listado 1.6

// IMPRIME CARACTERES #include <stdio.h> int main() { char a='X', b='Y', c='Z' ; printf("%c%c%c\n",a,b,c); return 0; }

Listado 1.7

// IMPRIME CADENAS DE CARACTERES #include <stdio.h> int main() { /* Formato: %[-a.b]s donde: - JUSTIFICA A LA IZQUIERDA a NUMERO DE ESPACIOS DISPONIBLES PARA DESPLEGAR b NUMERO MAXIMO DE CARACTERES QUE PUEDEN DESPLEGARSE */ printf("\n%10s","DESPLEGADO DE CADENAS EN LENGUAJE C++"); printf("\n%-10s","DESPLEGADO DE CADENAS EN LENGUAJE C++"); printf("\n%25.10s","DESPLEGADO DE CADENAS EN LENGUAJE C++") printf("\n%-25.10s","DESPLEGADO DE CADENAS EN LENGUAJE C++"; printf("\n%.10s","DESPLEGADO DE CADENAS EN LENGUAJE C++"); return 0; }

Listado 1.8

Para desplegar el símbolo de porciento puede escribirse en cadena_formato : % . Por ejemplo:

..........

..........int utilidad;utilidad = 30;printf("La utilidad es del %2d %\n", utilidad);..........

desplegará :

Page 13: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

La utilidad es del 30 %

1.7.1.2.- La función scanf()

Esta función toma la entrada, normalmente del teclado, y la almacena en variables previamente declaradas. El formato para la función scanf() es :

scanf( <cadena_formato> , <elem> , <elem> , ... ) ;

Los significados de <cadena_formato> y <elem> son los mismos que en el caso de la función printf() . La función scanf() utiliza la mayoría de los comandos mostrados en la tabla 1.2 La función scanf() debe utilizarse con cautela cuando se van a introducir valores numéricos y de cadenas de caracteres en una sola instrucción.

1.7.1.3.- Las funciones gets() y puts()

Las funciones scanf() y printf() sirven para varios formatos, por lo cual son muy voluminosas. Es por esto que se han desarrollado las funciones gets() y puts(), específicas para la entrada/salida de cadenas de caracteres.

La función gets() toma una cadena de caracteres desde el teclado y la almacena en la variable cuyo identificador aparece como argumento en la invocación a gets(), como en:

gets(nombre);

La función puts() despliega en la pantalla una cadena de caracteres que se encuentra almacenada en la variable cuyo identificador aparece como argumento en la invocación a la función puts().

1.7.1.4.- Las macros getc() y putc()

La macro getc(c) toma un caracter desde el teclado y lo almacena en la variable c.

Por su parte, putc() despliega un caracter en la pantalla.

Page 14: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Estas macros están definidas en el archivo stdio.h .

1.7.2.- Funciones declaradas en conio.h.

La consola es un concepto heredado de los ambientes multiusuario, y está formada por el teclado y el monitor.En las implementaciones de Borland C++ existe una serie de funciones que manejan la entrada/salida por la consola, entre otras:

clrscr(), que limpia la pantalla y posiciona el cursor en la esquina superior izquierda de la ventana actual.

gotoxy(a,b), posiciona el cursor en la posición establecida por la columna a y el renglón b.

window(a,b,x,y), establece la ventana actual. Los valores a,b pertenecen a la columna y el renglón de la esquina superior izquierda; los valores x,y corresponden a la columna y el renglón de la esquina inferior derecha de la nueva ventana.

getch(), toma un carácter desde el teclado y no lo despliega en la pantalla.

getche(), toma un carácter desde el teclado y lo despliega en la pantalla.

Para poder utilizar las funciones relacionadas con la consola, es necesario incluir la linea: #include <conio.h>

Donde conio.h es el archivo de cabecera donde se encuentran declarados los prototipos de las funciones que manejan la entrada/salida estándar por la consola.

1.7.3.- Objetos declarados en iostream.h

C++ provee una nueva forma para manejar la entrada/salida, proporcionando una sintaxis más sencilla, elegante e intuitiva que la de stdio.h , además de un mecanismo más eficiente y flexible. La palabra iostream tiene su origen en las palabras: input output stream , por lo que puede traducirse como flujo de entrada/salida.

El flujo de entrada/salida es utilizado para enviar datos desde una fuente ( productor ) a un destino ( consumidor ) . En esta forma se tiene una vasta jerarquía de clases para el manejo de entrada/salida para archivos y dispositivos. En el archivo iostream.h se

Page 15: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

encuentran definidos los objetos cin y cout.

El objeto cin utiliza al operador sobrecargado >> para tomar datos desde la entrada estándar( habitualmente el teclado ). El objeto cout utiliza al operador sobrecargado << para enviar datos hacia la salida estándar( normalmente la pantalla ).

Veamos como se utilizan estos elementos en un pequeño ejemplo:

#include <iostream.h> int main() { char nombre[30]; cout << "CUAL ES TU NOMBRE ? " ; cin>> nombre ; cout << "\n" ; cout << "!! HOLA , " << nombre << " !!" ; return 0 ; }

Listado 1.9.- Ejemplo de utilización de cin y cout

También es posible especificar el ancho de un campo por medio del manipulador setw() , que se encuentra declarado en iomanip.h ; así como los manipuladores window(), clrscr() y gotoxy() declarados en constrea.h, como se muestra en el listado 1.10.

#include <iostream.h> #include <iomanip.h> #include <constrea.h> int main() { char nombre[10] ; int numero = 150 ; float sueldo ; constream ventana0, ventana1, ventana2 ; ventana0.window(1,1,80,25); ventana1.window(10,05,20,05); ventana2.window(10,07,40,07); ventana0.clrscr(); ventana1.clrscr(); cout << "NUMERO: " << setw(3) << numero; cout << " NOMBRE : " ; cin.getline(nombre,10); ventana2.clrscr(); cout << "\nSUELDO : $ " ; cin>> sueldo ; return 0; }

Listado 1.10.- Uso de manipuladores.

2.- Constantes, variables, operadores

En esta unidad se trata el manejo de los objetos donde se almacenan los datos, así como las operaciones que pueden realizarse con los valores almacenados.En este contexto, llamaremos objeto a una región identificable de memoria que puede

Page 16: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

almacenar un valor fijo o variable. No debe confundirse el significado que se dá aquí al vocablo objeto con el que tiene en la Programación Orientada a Objetos.

2.1.- ConstantesPodemos definir a una constante como una localidad de memoria (objeto), la cual tiene un nombre y un tipo de dato asociados, además de un valor que permanece fijo durante el tiempo de ejecución.

En la unidad 1 se estudió la manera de definir constantes por medio de la directiva #define . Aquí definiremos a las constantes utilizando el modificador const, usando del formato mostrado a continuación:

           const <tipo> <identificador> = <valor>

Ejemplo: const int iva = 10 ;

Los valores para las constantes, permitidos por Borland C++, se clasifican en cuatro clases:

I).- Enteros, que pueden escribirse en formato:             I.a).- Decimal, con valores de 0 a 4,294'967,295.                              Ejemplo:  const int smin = 15300 ;       I.b).- Octal, que deben empezar con un 0 (cero).                Ejemplo:  const int lim = 030 ; (decimal 27)        I.b).- Hexadecimal, que empiezan con 0X ó 0x (cero x)

             Ejemplo:  const int mem = 0xFFFF ;

II).- Caracteres, compuestos de uno o más caracteres encerrados entre comillas sencillas (apóstrofes).

     Ejemplo: const char inicial = 'A' ;

Para manejar los caracteres de control se utilizan las secuencias de escape que empiezan con el carácter de diagonal hacia atrás (\) seguida de un carácter. Si el carácter que sigue a la diagonal es alguno de los caracteres mostrados en la tabla 2.1, se obtendrá el efecto explicado en ella; pero si es cualquiera de los demás caracteres del Código ASCII, se visualizará el carácter tal cual.

Secuencia Caracter Efecto\a BEL Sonido de la bocina

Page 17: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

\b BS Retroceso\f FF Alimentación de forma\n LF Nueva línea\r CR Retorno de carro\t HT Tabulación horizontal\v VT Tabulación vertical\\ \ Diagonal hacia atrás\' ' Comilla sencilla\" " Comilla doble\? ? Interrogación\O O Cualquiera O=Número octal de hasta tres dígitos\x H Cualquiera H=Número hexadecimal

Tabla 2.1.- Secuencias de escape.

Borland C++ permite el uso de dos caracteres juntos como valores de caracteres.

Por ejemplo: 'AB' , '\n\t' , '\007\007'

III).- Punto flotante, cualquiera con punto decimal ó con formato exponencial.

Ejemplos:  3.135 , 0. , .0  , 34e3 , -3.4 ,  .075E12

IV).- Enumeración, son identificadores definidos en declaraciones de tipo enum.

Ejemplo:  enum frutas { pera, uva, manzana, fresa } ;

donde: pera, uva, manzana y fresa son constantes de enumeración y tienen los siguientes valores:

                  pera    = 0                  uva     = 1                 manzana = 2                 fresa   = 3

Pueden asignarse valores explícitos como en:

     enum frutas {pera, uva , manzana = 10 , fresa = uva+5} ;

y entonces :

                pera    =  0

Page 18: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

                uva     =  1                manzana = 10                fresa   =  6

2.2.- Variables

Una variable es un espacio de memoria que tiene un nombre y un tipo de dato asociados, además de un valor que puede cambiarse durante el tiempo de ejecución.

En el Lenguaje C++ puede declararse una variable en cualquier lugar del programa, con la única restricción de que la declaración preceda a la primera instrucción donde se utiliza tal variable.

El formato para la declaración de una variable o grupo de variables es el siguiente:

[, var2, ... ] ;

Ejemplos: int algo, otro ;

int suma = 0 , inicial = 1 ;

float desc , acum = 0 ;

char prim = 'A' , seg ;

2.3.- Ambito

El ámbito es una zona de programa donde puede utilizarse un identificador para accesar su objeto . El ámbito va a depender, básicamente, del lugar del programa en que aparece la declaración.

Bajo este enfoque, se tienen las siguientes clases de ámbito:

Ambito de BLOQUE.

En el Lenguaje C++, se generaliza el concepto de ámbito local, a través de la introducción del concepto de bloque.

Un bloque está formado por las instrucciones delimitadas por un par de llaves { }.

Es posible anidar una serie de bloques, y pueden declararse variables dentro de cada bloque. Si, en diferentes bloques, existen variables con el mismo identificador, todas serán consideradas como variables diferentes. Así que puede tenerse:

Page 19: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

..............

..............int x=5; // x del bloque 1{ int x=10; // x del bloque 2 { int x=20; // x del bloque 3 { int x=30; // x del bloque 4 printf("x=%d",x); // x=30 } printf("%d",x); // x=20 } printf("%d",x); // x=10}printf("%d",x); // x=5..........................

Debe tenerse cuidado al utilizar identificadores iguales para diferentes variables. Lo más recomendable es no utilizar los mismos identificadores y no intentar manejar variables en un ámbito diferente al que pertenecen.

Ambito de FUNCION.

En esta clase sólo se encuentran los identificadores que se utilizan como etiquetas cuando se tienen instrucciones goto.El identificador de una etiqueta debe ser único dentro de una función, y no puede utilizarse la instrucción goto para saltar de una función a otra.

Ambito de PROTOTIPO DE FUNCION.

El ámbito de los identificadores utilizados dentro de los paréntesis en la declaración de prototipo de una función, finaliza con la declaración del prototipo.

Ambito de ARCHIVO.

Las variables declaradas fuera de cualquier bloque en un archivo son consideradas como variables globales, y el ámbito de sus identificadores abarca todos los bloques contenidos en el archivo en cuestión; por lo que: una variable con éste ámbito puede ser utilizada en las funciones del archivo que estén definidas después del punto donde se declaró dicha variable.

2.4.- Duración

Page 20: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

La duración define el periodo de tiempo durante el cual los identificadores declarados tienen objetos asignados en memoria. Las variables, por ejemplo, tienen memoria asignada durante el tiempo de ejecución.

Existen tres clases de duración:

2.4.1.- Duración estática. 2.4.2.- Duración local. 2.4.3.- Duración dinámica.

2.4.1.- Duración estática

Los objetos con duración estática están localizados en memoria durante todo el tiempo de ejecución del programa. Las funciones, las variables con ámbito de archivo y las variables con especificadores de clase de almacenamiento static ó extern, tienen duración estática.

Los objetos con duración estática son inicializados a cero , en ausencia de un valor inicial explícito.

2.4.2.- Duración local

Los objetos de duración local, conocidos también como objetos automáticos, son creados en la pila de la memoria RAM ó en los registros del microprocesador. La memoria asignada a éstos objetos es liberada cuando finaliza la ejecución del bloque donde fueron creados. Los objetos de duración local deben utilizarse siempre en un ámbito local ó de función.

Cuando se utiliza el especificador de clase de almacenamiento register, se implica el uso del especificador de clase de almacenamiento auto.

2.4.3.- Duración dinámica

Los objetos de duración dinámica se crean y se destruyen, por invocaciones a funciones específicas, durante el tiempo de ejecución de un programa. El almacenamiento de estos objetos se dá en el área de reserva de la memoria RAM llamado montículo.

Page 21: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

2.5.- Enlace

En la creación de un programa ejecutable, primero se lleva a cabo la compilación de diversas unidades de traslación, las cuales se componen del código fuente junto con los archivos incluidos. Posteriormente, el archivo objeto (.obj) , se enlaza con librerías preexistentes para obtener el archivo ejecutable (.exe).

El enlace es el proceso que permite a cada instancia de un identificador asociarse correctamente con un objeto o función particular. Todos los identificadores tienen uno de los siguientes atributos de enlace, íntimamente relacionados con su ámbito:

. ENLACE EXTERNO, . ENLACE INTERNO, . SIN ENLACE.

Estos atributos se determinan a través del emplazamiento y los formatos de las declaraciones, junto con el uso ( implícito ó explícito ) de los especificadores de clase de almacenamiento static ó extern.

Cada instancia de un identificador con enlace externo representa al mismo objeto ó función a través de todo el conjunto de archivos y librerías que componen el programa.

Cada instancia de un identificador con enlace interno representa al mismo objeto ó función solamente dentro de un archivo.

Los identificadores sin enlace representan entidades únicas.

A continuación se presentan las:

REGLAS PARA LOS ENLACES INTERNO Y EXTERNO:

1. Cualquier identificador de objeto ó archivo que tenga ámbito de archivo tendrá enlace interno si su declaración contiene el especificador static.Para C++, si el mismo identificador aparece con ambos tipos de enlace en el mismo archivo, el identificador tendrá enlace externo.

2. Si la declaración de un identificador de un objeto ó función contiene el especificador extern , el identificador tendrá el enlace correspondiente a una declaración con ámbito de archivo.

3. Si una función se declara sin un especificador de clase de almacenamiento, su enlace está determinado como si se hubiera utilizado el especificador extern.

Page 22: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

4. Si un identificador de objeto con ámbito de archivo se declara sin especificador de clase de almacenamiento, el identificador tendrá enlace externo.

Los siguientes identificadores no tienen atributo de enlace:

a).- Cualquier identificador declarado para ser algo diferente de un objeto ó función ( p.ej. un identificador typedef ).

b).- Los parámetros de las funciones. c).- Los identificadores de ámbito de bloque para objetos declarados sin el el

especificador extern.

Para aclarar lo relacionado con el especificador extern, revisemos los listados 2.1 y 2.2.

// EXTERN1.CPP int edad; char nombre[31]; // DEFINICION de variables globales

Listado 2.1.- EXTERN1.CPP, que define las variables globales.

// EXTERN2.CPP #include <iostream.h> // DECLARACION de variables globales que: extern int edad; // se encuentran en otro archivo extern char nombre[]; void main() { cout << "\nCUAL ES TU NOMBRE ? " ; cin>> nombre ; cout << "\nCUANTOS AÑOS TIENES, " << nombre << " ?" ; cin>> edad ; cout << "\n\n" ; cout << " TE FALTAN " << 100-edad ; cout << " PARA LLEGAR A LOS CIEN, " << nombre << "\n" ; }

Listado 2.1.- EXTERN2.CPP, que declara y utiliza variables globales definidas en otro archivo.

En el archivo EXTERN1.CPP se definen las variables globales edad y nombre. Posteriormente se compila este archivo para obtener el archivo EXTERN1.OBJ.

En el archivo EXTERN2.CPP se declaran las variable edad y nombre utilizando el modificador extern.

Finalmente, se crea un proyecto al que puede llamarse EXTERN.PRJ, en el cual se incluyen los archivos EXTERN1.OBJ y EXTERN2.CPP; se compila el proyecto y se obtiene el archivo ejecutable EXTERN.EXE

2.6.- Operadores

Page 23: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Los objetos que se manejan en C++ van a tener un tipo de dato asociado, el cual determina la cantidad de espacio de almacenamiento que se le asigna a cada uno de los objetos de tal tipo, así como el conjunto de operaciones que podrán realizarse con los valores almacenados. Las operaciones serán representadas a través de identificadores específicos llamados operadores.

En esta sección estudiaremos los operadores, agrupándolos en:

2.6.1.- Operadores aritméticos. 2.6.2.- Operadores Relacionales. 2.6.3.- Operadores lógicos. 2.6.4.- Operadores entre bits. 2.6.5.- Operadores de asignación.

2.6.1.- Operadores ariméticos

Los operadores aritméticos se aplican sobre objetos con valores numéricos, como se muestra en la tabla 2.2 .

Sean: X = 20 , Y = 30 , A = 100.0 , B = 200.0

Operador Operación Ejemplo Resultado+ Adición Z=X+Y Z=50- Sustracción Z=Y-X Z=10* Multiplicación Z=X*Y Z=600/ División Z=Y/X Z=1.5

% Módulo Z=Y%X Z=10++ Incremento X++ X=21- Decremento X- X=19

Tabla 2.2.- Operadores aritméticos.

2.6.2.- Operadores relacionales

Los operadores relacionales se usan para comparar los valores que resultan de reducir expresiones. Los resultados coincidirán con los valores de verdad:

FALSO igual a CERO VERDADERO diferente de CERO Los operadores relacionales en C++ son :

Page 24: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

> Mayor que

= Mayor ó igual que

<= Menor ó igual que="=" Igual que !="Diferente" que ( No igual que )

2.6.3.- Operadores lógicos

Los operadores lógicos se aplican sobre los enunciados que resultan de las operaciones relacionales, y el resultado siempre será un valor de verdad. Los operadores lógicos son:

&& Y ( Conjunción )

|| O ( Disyunción )

! NO ( Negación )

2.6.4.- Operadores entre bits

Con estos operadores se puede realizar la comprobación, colocación ó desplazamiento de los bits actuales de una variable de los tipos int y char.

Los operadores entre bits son:

& Y ( Conjunción )

| O ( Disyunción )

^ O ( Disyunción Exclusiva ó XOR )

~ ( Complemento a uno ó NOT )

>> ( Desplazamiento a la DERECHA ) << ( Desplazamiento a la IZQUIERDA )

A continuación se presentan ejemplos de algunas operaciones entre bits:

Ejemplo 2.6.1:Supongamos que se quiere cambiar el bit de paridad ( el de la extrema izquierda ) de uno a cero. Para esto puede utilizarse la operación de conjunción entre bits.

Page 25: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Si tenemos la declaración:

char indice = 81 ;

y, suponiendo que se quiere cambiar el bit de paridad de 1 a 0 , la instrucción en C++ se escribiría :

indice & 127 ;

La operación realizada a mano es :

11010001 <------ indice operador> & 01111111 <------ 127 en binario 01010001 <------ resultado

En este caso se utilizó el número 127 porque es el único número entero que puede escribirse en un octeto ( byte ) y que tiene los siete primeros bits con valor 1 .

Ejemplo 2.6.2Se requiere que en el número 112 tengan valor 1 los bits que correspondan con los del número 7 que tengan valor 1. En este caso, la operación a utilizar es la disyunción entre bits, quedando las instrucciones en la siguiente forma:

char masc ; masc = 112 | 7 ;

La operación manual tomaría la siguiente forma:

01110000 <------ 112 en binario operador> | 00000111 <------ 7 en binario 01110111 <------ resultado

Ejemplo 2.6.3La disyunción exclusiva sirve para poner a uno los bits del primer operando cuyos correspondientes bits en el segundo operando sean distintos, como se ve a continuación:

x = 125 ^ 120 ; char x ;

Manualmente se tendría:

01111101 <------- 125 en binario operador> 01111000 <------- 120 en binario 00000101 <------- resultado

Los operadores de desplazamiento mueven todos los bits, de una variable entera ó de carácter, hacia la izquierda ó hacia la derecha.A medida que los bits son desplazados hacia un extremo, los lugares desocupados del

Page 26: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

estremo contrario van siendo ocupados por ceros. Los unos que salen por los extremos no se pueden recuperar(no hay rotación) .

2.6.5.- Operadores de asignación

El estudio de los operadores de asignación requiere del conocimiento de los conceptos de valor izquierdo ( lvalue ) y de valor derecho ( rvalue ). 

Un valor izquierdo es una expresión que designa un objeto. Las expresiones utilizadas como valor izquierdo corresponden a objetos cuyos valores pueden cambiar durante la ejecución de un programa. Generalmente, en la expresión correspondiente a un valor izquierdo aparece el identificador de una variable. Cuando el valor izquierdo se refiere a la dirección de una variable, la expresión puede constar de una combinación de varios identificadores.

Un valor derecho es una expresión formada por cualquier combinación de objetos y operadores que pueda ser reducida a un valor.

Los operadores de asignación sirven para asignar un valor derecho a un valor izquierdo, y están formados por la combinación del operador de asignación simple = con otro operador, como se muestra en la tabla 2.3 .

Operador Significado Ejemplo: Equivale a:= Asignación múltiple .

+= Suma asigna X+=Y X=X+Y-= Resta asigna X-=Y X=X-Y*= Multiplicación asigna X*=Y X=X*Y/= Divide asigna X/=Y X=X/Y

%= Residuo asigna X%=Y X=X%Y<<= Dezplaz. izq. asigna X<<Y X=X<<Y>>= Dezplaz. der. asigna X>>Y X=X>>Y&= Conj. e/bits asigna X&=Y X=X&Y^= Disy. exclu. asigna X^=Y X=X^Y|= Disyunción asigna X|=Y X=X|Y

Tabla 2.3.- Operadores de asignación.

 

Page 27: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Los operadores estudiados hasta aquí no son todos los que existen en el C++, sino los que a mi juicio son indispensables para iniciar el estudio del lenguaje. Si, posteriormente, es necesario utilizar algún operador no tratado en esta unidad, en ese momento se estudiarán las características particulares de tal operador.

3.- Instrucciones de control

En esta unidad estudiaremos las instrucciones que sirven para controlar el flujo de ejecución de un programa en C++ . De acuerdo a las características de cada una, las clasificaremos en grupos de estructuras básicas de: 

3.1.- Instrucciones de secuencia.3.2.- Instrucciones de selección. 3.3.- Instrucciones de iteración. 

Las estructuras básicas deben cumplir con la condición básica de la Programación Estructurada de: sólo una entrada, sólo una salida.

3.1.- Secuencia

A este grupo pertenecen las instrucciones que están formadas por una o varias expresiones simples colocadas una a continuación de la otra. La sintaxis para las instrucciones estructurados en secuencia es la siguiente:

                    instruccion_1 ;                    instruccion_2 ;                    .........                    instruccion_N ;

Su diagrama de flujo se presenta en la figura 3.1.

Page 28: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

3.2.-Selección

A este grupo pertenecen aquellas instrucciones que sirven para que la ejecución del programa tome una de varias opciones existentes en una ramificación.

En C++ se tienen las siguientes instruccións de selección:

3.2.1.- La instrucción if - else 3.2.2.- La instrucción switch

3.2.1.- La instrucción if - else

Esta instrucción permite elegir entre dos opciones de ejecución, y su sintaxis es :

if( condición ) [else] [bloque_2]

donde: bloque_1 y bloque_2 pueden estar formados por uno ó

Page 29: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

más instrucciones.

else es opcional, y en caso de no existir, bloque_2 tampoco existirá.

Al ejecutarse esta estructura, primero se evalúa la condición. En caso de que, de esta evaluación, resulte un valor de verdad verdadero, se ejecutarán las instrucciones que forman el bloque_1; en caso contrario (si el valor de verdad es falso), se ejecutarán las instrucciones del bloque_2.

El diagrama de flujo para la estructura if-else se presenta en la figura 3.2.

Esta estructura puede anidarse para elgir entre un grupo de más de dos opciones, tomando la siguiente forma:

if(condición_1) bloque_1; else if(condición_2) bloque_2; else if(condición_3) bloque_3; ............ else bloque_N;

A continuación se muestran algunos ejemplos de aplicación de la instrucción if-else

Page 30: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

#include <iostream.h> void main() { long ncontrol; cout << "NUMERO DE CONTROL: "; cin>> ncontrol; if(ncontrol<=0) cout << "NUMERO INVALIDO........."; else cout << "CORRECTO !!" << "\n\n\n"; }

Listado 3.1.- Uso de if-else

#include <iostream.h> #include <conio.h> void main() { int calif; clrscr(); cout << "CALIFICACION: "; cin>> calif; if(calif > 100) cout << "ERROR: CALIFICACION DEMASIADO ALTA ....."; else if(calif < 0) cout << "ERROR: CALIFICACION DEMASIADO BAJA ....."; else if( (calif>= 70) && (calif <=100)) cout << "CALIFICACION APROBATORIA."; else cout << "CALIFICACION REPROBATORIA."; }

Listado 3.2.- Uso de if-else-if

3.2.2.- La instrucción switch

Esta instrucción es útil cuando se tiene que elegir entre más de dos opciones, como es el caso de manejo de menús. Esta instrucción es preferible que el uso de anidamientos de varios if-else.

Su sintaxis es:

switch(expresión_entera) { case Const_1 : Bloque_1 ; break ; case Const_2 : Bloque_2 ; break ; ............................. ............................. ............................. case Const_N : Bloque_N ; break ; default : Bloque_X ; }

A la entrada en una instrucción switch, primero se evalúa la expresión_entera. En caso de que el resultado coincida con Const_1, se ejecuta el Bloque_1 y break interrumpe la ejecución del instrucción; en caso de que coincida con el valor de

Page 31: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Const_2, se ejecuta el Bloque_2 , se interrumpe la ejecución de la instrucción. Lo mismo pasa en caso de que el resultado coincida con cualquiera de los otros valores constantes. En caso de existir default:, y si el resultado no coincide con ninguno de los valores constantes, se ejecutan las instrucciones contenidas en el Bloque_X.

Esta estructura puede representarse con el diagrama mostrado en la figura 3.3.

Es recomendable el uso de la instrucción switch en el caso del manejo de un menú de opciones como se observa en el listado 3.3.

include <iostream.h> #include <conio.h> void main() { char opcion; clrscr(); gotoxy(30,5); cout << "MENU DE OPCIONES"; gotoxy(30,8); cout << "1.- CREACION"; gotoxy(30,10); cout << "2.- MODIFICACION"; gotoxy(30,12); cout << "3.- ELIMINACION"; gotoxy(30,14); cout << "0.- SALIDA"; gotoxy(30,18); cout << "SU OPCION ? "; opcion="getche();" cout << "\n\n"; switch(opcion) { case

Page 32: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

'1': clrscr(); cout << "RUTINA DE CREACION\n"; break; case '2': clrscr(); cout << "RUTINA DE MODIFICACION\n"; break; case '3': clrscr(); cout << "RUTINA DE ELIMINACION\n"; break; case '0': clrscr(); cout << "SALIDA AL SISTEMA OPERATIVO\n";break; default:clrscr(); cout << "OPCION INVALIDA.....\n"; } }

Listado 3.3.- Uso de switch

3.3.- Iteración

Las estructuras de iteración se utilizan en la ejecución repetida de un bloque de instruccións.

En el Lenguaje C++, se tienen:

3.3.1.- La instrucción while 3.3.2.- La instrucción do - while 3.3.3.- La instrucción for

Para el manejo de las estructuras de iteración.

3.3.1.- La instrucción while

Con esta instrucción se maneja una estructura en la que, de entrada, se evalúa una condición. En caso de que el resultado de tal evaluación sea un valor diferente de cero , se ejecuta un bloque de instrucciones, en el cual debe existir una instrucción que modifique la condición, ya que de lo contrario ejecutará un ciclo infinito ( loop ). Si el resultado de la evaluación es un valor igual a cero , el bloque de instrucciones no se ejecuta y finaliza la ejecución de la instrucción.

La sintaxis de la instrucción while es la siguiente:

while(condición) bloque;

En la figura 3.4 se presenta el diagrama de la instrucción while.

Page 33: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

El listado 3.4 muestra el uso de la instrucción while.

#include <iostream.h> #include <conio.h> #define FALSO 0 void main() { int valor=1; clrscr(); while(valor!=FALSO) { cout << "\nTeclee un valor entero ( 0="salir" ) : "; cin>> valor; } }

Listado 3.4.- Uso de la instrucción while

3.3.2.- La instrucción do - while

Page 34: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

La instrucción do-while tiene un comportamiento similar a while, sólo que en este caso primero se ejecuta el bloque de instrucciones y después se evalúa la condición. Con esto se asegura que el bloque se ejecutará al menos una vez.

Esta es la sintaxis de la instrucción do-while :

do bloque; while(condición);

La instrucción do-while puede representarse con el diagrama mostrado en la figura 3.5.

Como una aplicación de la instrucción do-while , se presenta en el listado 3.5 una variante al problema resuelto en el listado 3.3 .

#include <iostream.h> #include <conio.h> void main()

Page 35: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

{ char opcion; do{ // inicia ciclo 1 clrscr(); gotoxy(30,5); cout << "MENU DE OPCIONES"; gotoxy(30,8); cout << "1.- CREACION"; gotoxy(30,10); cout << "2.- MODIFICACION"; gotoxy(30,12); cout << "3.- ELIMINACION"; gotoxy(30,14); cout << "0.- SALIDA"; gotoxy(30,18); cout << "SU OPCION ? "; window(42,18,42,18); do{ // inicia ciclo 2 clrscr(); opcion="getche();" }while((opcion < '0')||(opcion>'3'));// fin ciclo 2 window(1,1,80,25); switch(opcion) { case '1': clrscr(); cout << "RUTINA DE CREACION\n"; break; case '2': clrscr(); cout << "RUTINA DE MODIFICACION\n"; break; case '3': clrscr(); cout << "RUTINA DE ELIMINACION\n"; break; case '0': clrscr(); cout << "SALIDA AL SISTEMA OPERATIVO\n"; break; } cout << "\n\nPULSE CUALQUIER TECLA PARA CONTINUAR.."; getch(); }while(opcion!="0" ); // fin ciclo 1 }

Listado 3.5.- Aplicación de la instrucción do-while

3.3.3.- La instrucción for

Entre las instrucciones de iteración, for es la más versátil, ya que, entre otras características, permite la declaración de variables dentro de su estructura.

La sintaxis de la instrucción for es la siguiente:

for(inicialización ; condición ; control) bloque;

donde: inicialización es un bloque de instrucciones que puede incluir la declaración de las variables involucradas y la asignación de valores iniciales.

condición es una instrucción que puede evaluarse de tal forma que se obtenga como resultado un valor de verdad ( falso ó verdadero ). Mientras, se cumpla la condición, se ejecu- tará el bloque de instrucciones.

control es un bloque de instrucciones separadas por comas y que controlan la variación de los valores de las variables utilizadas.

Los bloques de inicialización, condición y control no son obligatorios, pero sí lo son los tres punto y coma que los separan; de tal suerte que la forma mínima de una instrucción for quedaría así:

Page 36: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

for(;;) // ciclo infinito ; // no realiza tarea alguna

El diagrama para la instrucción for se muestra en la figura 3.6.

El listado 3.6 muestra el uso de la instrucción for.

#include <iostream.h> #include <conio.h> int main() { clrscr(); for(int x=1 ; x <= 25 ; x++) { for(int y="1" ; y <="80" ; y++) { gotoxy(y,x); if((x="=25)" && (y="=80))" getch(); cout << '.'; } } return 0; }

Listado 3.6.- Utilización de la instrucción for

Page 37: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

4.- Diseño de funciones

Las funciones son el módulo básico para la construcción de programas en C++.

Además de la función main(), con frecuencia es necesario utilizar funciones adicionales que pueden ser accesadas a través del enlace de librerías precompiladas ó a través de su definición en el archivo de código fuente ó en archivos de cabecera.

En esta unidad estudiaremos los procedimientos necesarios para el manejo de las funciones definidas en el código fuente.

En principio, debemos distinguir entre: declarar, definir e invocar una función, ya que la confusión de éstos términos es causa de frecuentes problemas.

Desde los orígenes del Lenguaje C ha existido la distinción entre definir y declarar una función. Cuando se define una función se le está reservando espacio de almacenamiento en memoria; en cambio cuando se declara solo se está avisando que más adelante se encuentra una función con ciertas características, pero no se le reserva espacio en memoria.

4.1.- Definición de funciones

La definición de una función implica reservarle espacio de almacenamiento en memoria, de acuerdo al tipo de dato a retornar.

Es en la definición donde se incluye el cuerpo de la función.

El formato general de la definición de una función es el siguiente:

tipo identificador( argumentos ) { bloque;}

donde:

tipo es el tipo de dato que va a retornar la función, e identificador es el nombre de la función.

La existencia de argumentos dependerá de la tarea que va a realizar la función, pero el par de paréntesis es requisito indispensable.

Page 38: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

En el ejemplo 4.1. se presenta una aplicación de la definición, la declaración y la invocacion de funciones.

4.2.- Declaración de funciones

Cuando se declara una función, se está avisando al compilador que más adelante encontrará la definicion de tal función, y que por el momento, tome nota de las características de ella, como son: el tipo de dato que retorna, su nombre y los tipos de argumentos que va a recibir. Con esto, no habrá ningún problema en invocar a la función en un bloque de programa ubicado antes del lugar donde se encuentra escrita su definición.En el ejemplo 4.1, las líneas:

void saludo(); float calcula(float);

representan la declaración de las funciones ó la declaración de los prototipos de las funciones saludo() y calcula().

En la declaración de la función saludo() se especifica que no va a retornar valor alguno, y que no recibirá argumentos.

En la declaración de la función calcula() se especifica que va a retornar un valor de tipo float, y que va recibir un argumento de tipo float.

Es importante observar que la declaración de una función es parecida a la línea de encabezado de su definición , sólo que en el caso de la declaración se escribe un punto y coma al final.

También cabe hacer notar que en la declaración no se requiere escribir identificadores para los argumentos, como se observa en el ejemplo 4.1, sino que basta con incluir los tipos de datos de los argumentos. Se pueden incluir identificadores de argumentos, sólo que el ámbito de tales identificadores estará restringido a la declaración de la función correspondiente.

Por lo tanto, la declaración:

float calcula(float); podría haberse escrito así :

float calcula(float arg);

En este caso, el identificador arg no tiene uso alguno; por lo que es innecesaria su inclusión.

Si el número de argumentos, o los tipos correspondientes no coinciden entre la

Page 39: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

declaración y la línea de encabezado de la definición de la función el compilador marcará un error.

4.3.- Invocación a funciones

Una invocación ó llamada a una función implica pasarle el control de la ejecución del programa, así como los argumentos ó parámetros que requiere para realizar su tarea. En el listado 4.1 se tienen las líneas:

saludo(); //INVOCACION A LA FUNCION saludo() precio = calcula(costo); //INVOCACION A LA FUNCION calcula()

En la primera, se invoca a la función saludo() y no se le pasa ningún argumento. En la segunda, se invoca a la función calcula(), pasándosele como argumento una copia del valor que tiene la variable costo. El valor retornado por calcula() se asigna a la variable precio.

// ENCABEZADOS #include <iostream.h> #include <conio.h> // DECLARACION DE FUNCIONES void saludo(); float calcula(float); // DEFINICION DE LA FUNCION PRINCIPAL void main() { float costo, precio; clrscr(); cout << "COSTO : $ "; cin>> costo; saludo(); //INVOCACION A LA FUNCION saludo() precio = calcula(costo); //INVOCACION A LA FUNCION calcula() cout << "PRECIO : $ " << precio; } // DEFINICION DE LA FUNCION saludo() void saludo() { clrscr(); cout << "!! BIENVENIDO A LA VENTA ESPECIAL !!"; } // DEFINICION DE LA FUNCION calcula() float calcula(float x) { return( x * 1.6); }

Listado 4.1.- Diseño de funciones utilizando prototipos.

4.4.- El prototipo de una función¿es obligatorio?

Cuando encuentra la primera invocación a una función, el compilador verifica si ya se realizó la declaración ó la definición de la función invocada. En caso de no existir ni declaración ni definición previa, enviará un mensaje de error diciendo que la función invocada debe tener un prototipo. En ese momento se detiene la compilación y no se

Page 40: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

genera el archivo .OBJ correspondiente.

Lo anterior puede sugerir que el prototipo de la función invocada es indispensable. La realidad es que se puede omitir el prototipo si escribimos la definición de la función antes de su primera invocación, como se muestra en el ejemplo 4.2.

// ENCABEZADOS #include <iostream.h> #include <conio.h> // DEFINICION DE LA FUNCION saludo() void saludo() { clrscr(); cout << "!! BIENVENIDO A LA VENTA ESPECIAL !!"; } // DEFINICION DE LA FUNCION calcula() float calcula(float x) { return( x * 1.6); } // DEFINICION DE LA FUNCION PRINCIPAL int main() { float costo, precio; clrscr(); cout << "COSTO : $ "; cin>> costo; saludo(); //INVOCACION A LA FUNCION saludo() precio = calcula(costo); //INVOCACION A LA FUNCION calcula() cout << "PRECIO : $ " << precio; return 0; }

Listado 4.2.- Diseño de funciones sin uso de prototipos.

Aunque el prototipo no es obligatorio, es recomendable utilizarlo, ya que de esta manera se permite al compilador verificar que el número y tipos de argumentos utilizados en la invocación (parámetros actuales) coinciden con los de la definición (parámetros formales).

La declaración de prototipos también sirve para que el usuario de un programa conozca la forma de utilización de la función, sin tener que proporcionarle el código fuente. Por ejemplo, si tenemos un programa que maneja operaciones matemáticas podemos distribuir a los usuarios el código objeto que contenga las definiciones de las funciones y un archivo de cabecera que contenga los prototipos.

Los archivos pudieran tomar la forma mostrada a continuación:

ARITME.OBJ Archivo que contiene el código objeto, donde se definen las funciones aritméticas. //ARITME.HPP: CONTIENE LOS PROTOTIPOS DE LAS FUNCIONES double suma(double,double); double resta(double,double); ........................... ...........................

//APLICA.CPP: PROGRAMA FUENTE ELABORADO POR // EL USUARIO #include <iostream.h> #include "ARITME.HPP" //Incluye el archivo que contiene //los prototipos de las funciones

Page 41: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

//aritméticas void main(void) { double result, a=13500.45, b=16763.87; result = suma(a,b); // Invocación a la función suma() // definida en ARITME.OBJ cout << result; }

Este es el criterio que se sigue al utilizar los archivos de cabecera que contiene el paquete compilador.

Por ejemplo, cuando se escribe la línea:

#include <stdio.h>

se está incluyendo el archivo que contiene los prototipos de las funciones que se utilizan para el manejo de la entrada/salida estándar.

4.5.- Sobrecarga de funciones

En la mayoría de los casos, los identificadores ó nombres de los objetos deben ser únicos, esto es, que dos objetos dentro del mismo ámbito no deben tener el mismo nombre. Una de las excepciones la constituyen las funciones, que pueden compartir el mismo nombre entre varias de ellas, dando la impresión de que a una sola función se le ha "sobrecargado de tareas", razón por la cual se les llama funciones sobrecargadas.

Por ejemplo, supongamos que deseamos una función que sirva para sumar números. Lo ideal sería contar con una sola función que realizara la suma de números de diferentes tipos y que retornara un valor de acuerdo al tipo de resultado. El Lenguaje C++ permite resolver este problema por medio de la sobrecarga de funciones.

A continuación se muestran los prototipos para cada una de las funciones que comparten el nombre suma :

int suma(int,int); // Recibe enteros, devuelve entero

float suma(float,float);

double suma(double,double);

Aquí surge la pregunta: ¿ cómo distingue el compilador entre cada una de las funciones suma() ?.

Debido a que varias funciones sobrecargadas pueden usar el mismo identificador, C+

Page 42: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

+ utiliza el concepto de nombres ampliados para controlar cada función individualmente. El nombre ampliado de una función se construye tomando como base el nombre de la función y los tipos de los argumentos. El tipo de retorno de la función no se utiliza en la formación del nombre ampliado. La composición del nombre ampliado ocurre a nivel del compilador, no al nivel del enlazador. El enlazador resuelve fácilmente las referencias externas para sobrecargar las funciones debido a que éstas tienen nombres ampliados únicos.

Las reglas básicas usadas en Borland C++ para los nombres ampliados son relativamente simples como se observa a continuación :

1. Se utiliza primero el nombre de la clase, precedido por el carácter @. 2. Enseguida viene el nombre de la función, precedido por el carácter @. 3. Si la función no es parte de una clase, el nombre ampliado empieza con el

símbolo @ seguido por el nombre de la función. Se respetan las reglas establecidas para la creación de identificadores.

4. El nombre de la función está seguido por una secuencia $q ; las letras minúsculas que le siguen designan cada tipo de argumento declarado.

La tabla 4.1 muestra una lista de las letras que se utilizan para manejar los tipos predefinidos de C++ en la formación de los nombres ampliados.

Tipo de argumento Letrasvoid vvoid* pvunsigned char ucunsigned char* pucunsigned char& rucsigned char zcsigned char* pzcsigned char& rzcint iint* piint& riunsigned int uiunsigned int* puiunsigned int& ruilong llong* pllong& rl

Page 43: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

unsigned long ulunsigned long* pulunsigned long& rulfloat ffloat* pffloat& rfdouble ddouble* pddouble& rd

Tabla 4.1.- Letras para los tipos de argumentos en la creación de nombres ampliados.

Utilizando la tabla 4.1, se pueden predecir fácilmente los nombres ampliados de las funciones, como se muestra a continuación:

Declaración de funciones Nombre ampliadovoid imprim(void); @imprim$qvvoid imprim(int); @imprim$qivoid imprim(int,int,int); @imprim$qiiiint redim(long,unsigned int*); @redim$qlpuivoid despliega(char*); @despliega$qpzcint suma(int,int); @suma$qiifloat suma(float,float); @suma$qffdouble suma(double,double); @suma$qdd

4.6.- Paso de parámetros

Cuando se invoca a una función, en ocasiones es necesario enviarle algunos elementos indispensables para realizar su tarea. Estos elementos, enviados en la invocación, se llaman parámetros actuales.

Dependiendo de la naturaleza de los parámetros, clasificaremos el paso de ellos en:

4.6.1.- Paso Por Valor. 4.6.2.- Paso Por Referencia. 4.6.3.- Argumentos Predeterminados.

Page 44: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

4.6.1.- Paso por valor

Se realiza un paso de parámetros por valor cuando se envía una copia de los valores de los parámetros actuales.

En este caso, la función invocada no podrá modificar los valores de las variables utilizadas como parámetros actuales, sino que trabajará con las copias que se le envían. Estas copias son recibidas en los parámetros formales, los cuales se comportan como variables locales pertenecientes a la función invocada.

Al igual que las variables locales, una vez que termina la ejecución de la función invocada, las variables pertenecientes a los parámetros formales se destruyen; y por lo tanto, se pierden los valores que envió el módulo invocador.

El listado 4.3 muestra el paso de parámetros por valor.

#include <iostream.h> #define NL cout << "\n" void precio(double); void main(void) { double costo; cout << "COSTO : $ "; cin>> costo; precio(costo); // SE INVOCA A precio() Y SE LE ENVIA UNA // COPIA DEL VALOR DE costo NL; cout << costo; // EL VALOR DE costo SE CONSERVA DESPUES // DE LA INVOCACION A precio() } void precio(double recibido) { recibido="recibido" * 1.5; // SE MODIFICA EL VALOR DE LA // COPIA GURDADA EN recibido NL; cout << recibido; }

Listado 4.3.- Paso de parámetros por valor.

4.6.2.- Paso por referencia

A diferencia del paso por valor, el paso por referencia permite que la función invocada modifique el valor original de las variables usadas como argumentos.

En C++, el paso de parámetros por referencia se realiza creando un alias del identificador de la variable que forma el parámetro actual, como se muestra en el listado 4.4 .

#include <iostream.h> #include <conio.h> void oferta(float &); void main(void) {

Page 45: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

float precio; clrscr(); gotoxy(20,12); cout << "¿ CUAL ES EL PRECIO ?. N$ " ; cin>> precio; oferta(precio); gotoxy(20,14); cout << "¿ DIJO USTED N$ " << precio << " ?"; gotoxy(20,16); cout << " ESO ES CON EL 20 % DE DESCUENTO"; gotoxy(1,25); } void oferta(float & bajo) { bajo *="0.8" ; gotoxy(20,24); cout << "PRECIO REBAJADO: N$ " << bajo; }

Listado 4.4.- Paso de parámetros por referencia.

En el listado 4.4, se puede decir que bajo es otro identificador asociado a la variable precio, por lo que la función oferta() realmente modifica el valor contenido en dicha variable.

Según lo anterior, podemos decir que es preferible utilizar una variable existente en lugar crear una nueva, y eliminar el paso de parámetros por valor.

El paso de parámetros por valor puede sustituirse por un paso de parámetros por referencia donde se utilice el modificador const, como se muestra en el listado 4.5.

#include <iostream.h> #include <conio.h> void seudoferta(const float &); void main(void) { float precio; clrscr(); gotoxy(20,12); cout << "¿ CUAL ES EL PRECIO ?. N$ " ; cin>> precio; seudoferta(precio); gotoxy(20,14); cout << "¿ DIJO USTED N$ " << precio << " ?"; gotoxy(20,16); cout << " ESO DIJE.... NO HAY DESCUENTO"; gotoxy(1,25); } void seudoferta(const float & bajo) { float rebajado; rebajado="bajo" * 0.8 ; gotoxy(20,24); cout << "PRECIO REBAJADO: N$ " << rebajado; }

Listado 4.5.- Paso por valor utilizando referencias.

4.6.3.- Argumentos predeterminados

Cuando se declara el prototipo de una función, es posible asignar valores iniciales a los argumentos. A éstos se les llama argumentos predeterminados, y tienen como

Page 46: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

objetivo simplificar el manejo de los valores de los parámetros actuales utilizados.

Supóngase el problema de calcular del precio de un artículo. En la solución utilizaremos una función que recibe dos parámetros: el precio del artículo y el descuento correspondiente.

El valor del precio cambia para cada artículo, pero el valor del descuento puede considerarse como un pocentaje fijo del precio para la mayoría de los artículos (por ejemplo en época de ofertas).

En el listado 4.6 se presenta un programa solución para este caso.

////////////////////////////////////////////////////// // ARGPRED.CPP : EJEMPLO DEL MANEJO DE ARGUMENTOS // // CON VALORES PREDETERMINADOS // ////////////////////////////////////////////////////// #include <iostream.h> #include <conio.h> // DECLARACION DEL PROTOTIPO DE LA FUNCION desc(). // AL ARGUMENTO DE TIPO ENTERO porcentaje SE LE ASIGNA // UN VALOR INICIAL DE 20 Y AL iva UN VALOR INICIAL DE 10. double desc(double , int porcentaje = 20, int iva = 10); // DEFINICION DE LA FUNCION main() void main(void) { double precio, rebajado_1, rebajado_2, rebajado_3 ; clrscr(); gotoxy(20,10); cout << "PRECIO: N$ "; cin>> precio; rebajado_1 = desc(precio); // SE ACEPTA EL porcentaje Y EL

// iva PREDETERMINADOS. gotoxy(40,10); cout << "PRECIO REBAJADO 1="N$" " << rebajado_1; gotoxy(20,12); cout << "PRECIO: N$ "; cin>> precio; rebajado_2 = desc(precio,40); // EL porcentaje CAMBIA A 40,

// SE CONSERVA EL VALOR DEL iva. gotoxy(40,12); cout << "PRECIO REBAJADO 2="N$" " << rebajado_2; gotoxy(20,14); cout << "PRECIO: N$ "; cin>> precio; rebajado_3 = desc(precio,20,0); // SE CAMBIA EL VALOR // DE iva. AUNQUE SE CONSERVA EL VALOR // DE porcentaje, DEBE ESCRIBIRSE

// DE NUEVO. gotoxy(40,14); cout << "PRECIO REBAJADO 3="N$" " << rebajado_3; } // DEFINICION DE LA FUNCION desc() double desc(double p, int d, int i) { return(p * (1.00 (d-i)/100.00)); }

4.7.- Funciones recursivas

Page 47: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Se dice que una función es recursiva cuando puede invocarse a sí misma. En cada invocación se crean las variables locales, por lo que es indispensable incluir una condición de salida, ya que de no hacerlo se agotará la memoria disponible en la pila.

Como un primer acercamiento al manejo de funciones recursivas en C++, se presenta el listado 4.7.

#include <iostream.h> #include <conio.h> #define NL cout << "\n" #define PULSE cout << "PULSE CUALQUIER TECLA PARA CONTINUAR." void recursiva(void); void main(void) { clrscr(); recursiva(); NL; PULSE; getch(); NL; } void recursiva(void) { int x; cout << "TECLEE UN NUMERO ENTERO... ( 0="SALIR)" "; cin>> x ; if(x) recursiva(); }

Listado 4.7.- Diseño de una función recursiva.

En el listado 4.7 podemos observar que, para que la función recursiva se invoque a si misma, es necesario que el valor almacenado en x sea diferente de 0.

El cálculo del factorial de un número es un problema clásico en la aplicación de las funciones recursivas. En el listado 4.8 se presenta un programa en C++ que calcula el factorial de un número dado.

#include <iostream.h> #include <conio.h> long factorial(unsigned short int); void main() { unsigned short int num; long result; clrscr(); do { gotoxy(20,11); cout << "El FACTORIAL del número: " ; clreol(); cin>> num ; } while((num <0) || (num> 19 )); result = factorial(num); gotoxy(20,13); cout << " es : " << result; } long factorial(unsigned short int n) { if(n="=0)" return 1; else return n*(factorial(n-1)) ; }

Listado 4.8.- Cálculo del factorial utilizando funciones recursivas.

En el listado 4.8, si el número dado por el usuario es 4, el proceso realizado por el programa se podría representar de la manera mostrada en la tabla 4.2.

Page 48: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Numero deinvocación Valor de n Resultado

1 4 4*(factorial(3))2 3 3*(factorial(2))3 2 2*(factorial(1))4 1 1*(factorial(0))5 0 1

Tabla 4.2.- Resultados de invocar a la función factorial() pasándole como parámetro el número 4 .

En cada invocación, se crea en la pila una variable cuyo identificador es n y su valor cambiará como se muestra en la tabla 4.2. Como en las invocaciones 1,2,3 y 4 el valor de n es diferente de 0, la función vuelve a invocarse a sí misma, quedando sin resolver el resultado. Cuando el valor de n es igual a 0 (invocación 5), la función retorna el valor 1 la la invocación 4, por lo que el resultado en ésta se calcula así :

1 * (factorial(0)) = 1 * 1 = 1

La invocación 4 retorna a la invocación 3 el valor 1 y el resultado en la invocación 4 es :

2 * (factorial(1)) = 2 * 1 = 2

A su vez, la invocación 3 retorna a la invocación 2 el valor 2. El resultado en la invocación 2 es :

3 * (factorial(2)) = 3 * 2 = 6

Posteriormente, la invocación 2 retorna el valor 6 a la invocación 1. El resultado en la invocación 1 es :

4 * (factorial(3)) = 4 * 6 = 24

Finalmente, la invocación 1 retorna el valor 24 a la función invocadora main(), la cual asigna a la variable result el valor recibido ( 24 ).

5.- Arreglos.

Page 49: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Los arreglos consisten de un conjunto de elementos del mismo tipo almacenados en localidades contíguas de memoria. Los elementos de un arreglo comparten el mismo nombre, pudiéndo distinguirse un elemento de otro a través de un subíndice.

En esta unidad, describiremos la declaración de los arreglos, el manejo de los elementos de un arreglo dado, así como lo relacionado con los arreglos que se utilizan para formar las cadenas de caracteres.

5.1.- Declaración de arreglos

Los elementos de los arreglos son variables de algún tipo dado, que al compartir el mismo nombre pueden ser tratadas como una sola entidad.

La sintaxis para la declaración de un arreglo es la siguiente:

tipo identificador [ <expresión_constante> ] ;

donde:

tipo es el tipo de los elementos que componen el arreglo

identificador es el nombre del arreglo

expresión_constante es una expresión que al reducirse debe dar como resultado un valor entero positivo.

En el ejemplo 5.1 se muestra la declaración de arreglos.

DECLARACION RESULTADO char nombre[31]; Declara un arreglo unidimensional llamado nombre compuesto de 31 elementos de tipo carácter. int valor[20]; Declara un arreglo unidimensional llamado valor, compuesto por 20 elementos de tipo entero con signo. unsigned long abc[x] Declara un arreglo unidimensional llamado abc, compuesto por x elementos de tipo entero largo sin signo. En este caso x debe ser una constante entera. double matriz[5][7]; Declara un arreglo bidimensional llamado matriz, compuesto por 35 elementos de tipo entero. int trid[3][5][8]; Declara un arreglo tridimensional llamado trid, compuesto por 120 elementos de tipo entero.

Page 50: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Ejemplo 5.1.- Declaraciones de arreglos.

Como se observa en el ejemplo 5.1, la declaración de un arreglo multidimensional se distingue en que se agrega una pareja de corchetes para cada dimensión, por lo que la sintaxis, en este caso, toma la siguiente forma:

tipo identificador [ cte1 ][ cte2 ][ cte3 ] ... ;

donde: cte1, cte2, etc. representan los subíndices para cada dimensión.

El número y tamaño de las dimensiones solo estará restringido por la disponibilidad de memoria RAM, por lo que se puede tener una declaración como la siguiente:

double multidim [5][5][5][5][5][5] ;

El espacio de memoria ocupado por el arreglo multidim es el mismo que el ocupado por:

double unidim [15625

5.2.- Manejo de arreglos

En la sección anterior se ha tratado lo relacionado con la declaración de arreglos con elementos de diferentes tipos y con una o varias dimensiones.En esta sección se tratará lo relativo al acceso a los elementos individuales de un arreglo, ya sea para asignarles valores específicos o para utilizar los valores almacenados.

5.2.1.- Asignación de valores a los elementos de un arreglo

Al declarar un arreglo dentro de una función, los valores almacenados en cada uno de los elementos son desconocidos (se dice que el arreglo "tiene basura"), lo cual causa que el programa correspondiente arroje resultados inesperados. Para evitar los valores desconocidos, se recomienda asignar valores iniciales a cada uno de los elementos de los arreglos, como se muestra a continuación:

Por ejemplo, supongamos la siguiente declaración:

int vector[5];

Page 51: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

En este caso, se declara un arreglo de 5 variables de tipo entero agrupadas con el nombre vector, las cuales pueden representarse con la figura 5.1.

Figura 5.1.- Representación de un arreglo

Como puede observarse en la figura 5.1, el primer subíndice tiene valor cero y el último tiene valor cuatro.

Lo anterior se debe a que, en C++, el primer subíndice siempre vale cero y el último tiene un valor menor en uno que el valor de la dimensión del arreglo.

Una vez declarado el arreglo, se pueden asignar valores a cada uno de sus elementos, como se muestra enseguida:

vector[0] = 100 ; vector[1] = 101 ; vector[2] = 102 ; vector[3] = 103 ; vector[4] = 104 ;

y el arreglo vector luciría como en la figura 5.2.

Figura 5.2.- El arreglo vector después de asignarle valores.

Page 52: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

En este caso particular, pudimos haber asignado los valores por medio de un estatuto for de la siguiente forma:

for( int x = 0 ; x < 5 ; x++) vector[x] = x + 100 ;

Esta forma es la más conveniente en caso de que la cantidad de elementos sea grande y que los valores a asignar sean iguales o las diferencia entre elementos consecutivos sea constante.

Todo lo escrito en este ejemplo es válido para arreglos con elementos de cualquier tipo.

Por ejemplo, si queremos desplegar en pantalla los caracteres del código ASCII lo haríamos por medio del siguiente grupo de instrucciones:

unsigned char caracter[256]; for( int x=0 ; x < 256 ; x++ ) printf("%c", caracter[x]) ;

Cuando se manejan arreglos de varias dimensiones, debe recordarse que el subíndice de cada una de ellas inicia con un valor 0, como se observa en el listado 5.1.

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

void main(void) { int matriz[3][4]; clrscr(); for(int x=0 ; x < 3 x++) { for(int y=0 y< 4 ; y++) { matriz[x][y]=x+y+1 ; gotoxy(y+1,x+1); cout matriz[x][y]; } } }

Listado 5.1.- Arreglo con dos dimensiones.

El resultado de ejecutar el programa del listado 5.1 es el siguiente:

1234 2345 3456

El arreglo matriz puede representarse con la figura 5.3 .

Page 53: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Figura 5.3.- Representación del arreglo matriz .

Obsérvese que el primer subíndice varía de 0 a 2 y el segundo varía de 0 a 3.

En los párrafos anteriores, se ha mostrado la forma de asignar valores a cada uno de los elementos de un arreglo una vez que se ha escrito su declaración. Aquí veremos la manera de asignarle valores iniciales a los elementos, en la misma instrucción que contiene la declaración del arreglo.

Por ejemplo, para declarar un arreglo de 10 enteros y al mismo tiempo asignarle a cada uno de sus elementos los valores 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, puede escribirse :

int digitos[] = { 0,1,2,3,4,5,6,7,8,9 };

En este caso, aunque no se escribe el tamaño de la dimensión del arreglo digitos, tiene el mismo efecto que escribir:

int digitos[10]; digitos[0] = 0 ; digitos[1] = 1 ;

Page 54: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

digitos[2] = 2 ; digitos[3] = 3 ; digitos[4] = 4 ; digitos[5] = 5 ; digitos[6] = 6 ; digitos[7] = 7 ; digitos[8] = 8 ; digitos[9] = 9 ;

Como puede verse, la primera forma es mucho más compacta, aunque , como en muchas de las instrucciones de C++, la brevedad del código sacrifica la claridad.

5.3.- Cadenas de caracteres

En C++ no existe un tipo predefinido para el manejo de cadenas de caracteres como en otros lenguajes, sino que tienen que declararse como arreglos de caracteres.

Lo que distingue a una cadena de caracteres, con respecto a un arreglo de caracteres cualquiera, es que la cadena de caracteres tiene como último carácter al carácter nulo \0.

Por ejemplo, si se declara el arreglo:

char cadena[8];

podemos asignar los siguientes valores a cada uno de sus elementos:

cadena[0] = 'A' ; cadena[1] = 'R' ; cadena[2] = 'R' ; cadena[3] = 'E' ; cadena[4] = 'G' ; cadena[5] = 'L' ; cadena[6] = 'O' ; cadena[7] = '\0';

Al contener el carácter nulo, el arreglo cadena será reconocido por las funciones y objetos diseñados para manipular cadenas de caracteres. Para manejar un arreglo de cadenas de caracteres se debe declarar como un arreglo bidimensional de elementos de tipo char, como puede observarse en el listado 5.2.

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

void main()

Page 55: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

{ unsigned short int calif[10]; char nombre[10][21]; // Se declara un arreglo bidimensional // para 10 nombres de 20 caracteres por // nombre mas un caracter para el nulo. clrscr(); for( int x=0 ; x < 10 ; x++) { gotoxy(10,x+1); cout << "NOMBRE [" << x << "] = " ; cin >> nombre[x]; gotoxy(45,x+1); cout << "CALIFICACION [" << x << "] = " ; cin >> calif[x]; } }

Listado 5.2.- Manejo de un arreglo de cadenas de caracteres.

En el listado 5.2, se inicia con el subíndice 0 para no desperdiciar el primer elemento del arreglo. Además, se debe recordar que el subíndice del último elemento del arreglo es igual al tamaño de la dimensión menos 1.

5.4.- Asignación de Valores Iniciales a Cadenas

Cuando se tiene un arreglo de cadenas de caracteres, se puede utilizar la forma compacta mostrada en la sección anterior, sólo que , en lugar de constantes numéricas manejaríamos constantes de cadena.

Por Ejemplo:

char nombres[][5] = { "HUGO", "PACO", "LUIS" } ;

es equivalente a: char nombres[3][5];

nombres[0] = "HUGO" ; nombres[1] = "PACO" ; nombres[2] = "LUIS" ;

Esto también puede manejarse a nivel de caracteres, así :

char nombres[][5] = { 'H','U','G','O','\0', 'P','A','C','O','\0', 'L','U','I','S','\0' };

o así:

Page 56: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

char nombres[3][5];

nombres[0][0] = 'H' ; nombres[0][1] = 'U' ; nombres[0][2] = 'G' ; nombres[0][3] = 'O' ; nombres[0][4] = '\0' ; nombres[1][0] = 'P' ; nombres[1][1] = 'A' ; nombres[1][2] = 'C' ; nombres[1][3] = 'O' ; nombres[1][4] = '\0' ;

nombres[2][0] = 'L' ; nombres[2][1] = 'U' ; nombres[2][2] = 'I' ; nombres[2][3] = 'S' ; nombres[2][4] = '\0' ;

En los listados 5.3 y 5.4 se muestran las dos primeras formas, observándose que se obtiene el mismo resultado.

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

void main() { char nombres[][5] = { "HUGO","PACO","LUIS" }; int calif[] = { 100, 90, 100 }; clrscr(); for( int x=0 ; x < 3;x++) { gotoxy(35,x+10); cout nombres[x]; gotoxy(40,x+10); cout << calif[x]; } }

Listado 5.3.- Manejo de arreglos de cadenas.

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

void main() { char nombres[][5] = { 'H','U','G','O','\0', 'P','A','C','O','\0', 'L','U','I','S','\0' }; int calif[] = { 100, 90, 100 }; clrscr(); for( int x=0 ; x< 3;x++) { gotoxy(35,x+10);

Page 57: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

cout nombres[x]; gotoxy(40,x+10); cout << calif[x]; } }

Listado 5.4.- Manejo de arreglos bidimensionales de caracteres.

En los listados 5.3 y 5.4, se muestra que no es necesario escribir el valor de la primera dimensión de los arreglos cuando, en su declaración, se asignan valores constantes a los elementos.

La ventaja que tiene la forma mostrada en 5.3 y 5.4 es que, al no especificar una de las dimensiones, la cantidad de cadenas a manejar puede variarse con sólo agregarlas a la lista o eliminarlas de ella.

5.5.- Funciones para el manejo de cadenas

Como se estableció al principio de esta unidad, el lenguaje C++ no cuenta con algún tipo de dato específico para el manejo de cadenas de caracteres, pero sí cuenta con un grupo de funciones que se han acumulado durante la evolución del Lenguaje C.

Para leer una cadena de caracteres desde el teclado existe la función gets(), y para desplegar una cadena en pantalla se usa la función puts(). Los prototipos de ambas funciones se encuentran declarados en el archivo STDIO.H.

Por ejemplo, el listado 5.5 muestra un programa que sirve para leer y desplegar cadenas de caracteres utilizando las funciones gets() y puts().

#include <stdio.h> // Para gets() y puts() #include <conio.h> // Para clrscr() y gotoxy() #include <string.h> // Para strupr() y strlen()

void main() { char nombre[31]; // Declara un arreglo de 31 caracteres char saludo1[] = "¡¡ HOLA,"; //Constante de caracteres char saludo2[] = " !!"; clrscr(); gotoxy(20,10); puts("¿ Cuál es tu nombre ? "); //Despliega cadena de car. gotoxy(45,10); gets(nombre); // Lee cadena de caracteres strupr(nombre); // Convierte a mayúsculas gotoxy(20,12); puts(saludo1); gotoxy(30,12); puts(nombre);

Page 58: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

gotoxy(30+strlen(nombre),12); // Longitud de la cadena puts(saludo2); }

Listado 5.5.- Lectura y desplegado de cadenas de caracteres.

Además de las funciones gets() y puts(), existe otro grupo de funciones para el manejo de cadenas de caracteres, como strlen() y strupr() utilizadas en el programa del listado 5.5. Los prototipos de estas funciones se encuentran declarados en el archivo STRING.H En la tabla 5.1 se describen brevemente algunas de las funciones para el manejo de cadenas de caracteres en el C++ de Borland, cuyos prototipos se encuentran declarados en el archivo STRING.H .

FUNCION DESCRIPCION

stpcpy Copia una cadena de caracteres en otra.Se detiene cuando encuentra el terminador nulo.

strcat Añade una cadena de caracteres a otra.strchr Busca, en una cadena, un caracter dado.strcmp Compara dos cadenas.strcmpi Macro que compara dos cadenas sin distinguir entre mayúsculas y minúsculas.strcpy Copia una cadena.

strcspn Busca segmentos que no contienen un subconjunto de un conjunto especificado de caracteres.

strdup Copia una cadena a una nueva localidad._strerror Genera un mensaje de error definido por el programador.strerror Retorna el apuntador al mensaje asociado con el valor del error.stricmp Compara dos cadenas sin diferenciar entre mayúsculas y minúsculasstrlen Determina la longitud de una cadena.strlwr Convierte las mayúsculas de una cadena en minúsculas.strncat Añade el contenido de una cadena al final de otra.

strncmp Compara parte de una cadena con parte de otra.

strncmpi Compara parte de una cadena con parte de otra, sin distinguir entre mayúsculas y minúsculas.

strncpy Copia un un número de bytes dados, desde una cadena hacia otra.

strnicmp Compara parte de una cadena con parte de otra, sin distinguir entre mayúsculas y minúsculas.

strnset Hace que un grupo de elementos de una cadena tengan un valor dado.

strpbrk Busca la primera aparición, en una cadena, de cualquier caracter de un conjunto dado.

strrchr Busca la última aparición de un caracter en una cadena.

Page 59: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

strrev Invierte el orden de los caracteres de una cadena.strset Hace que los elementos de una cadena tengan un valor dado.

strspn Busca en una cadena el primer segmento que es un subconjunto de un conjunto de caracteres dado.

strstr Busca en una cadena la aparición de una subcadena dada._strtime Convierte la hora actual a una cadena.strtod Convierte una cadena a un valor double ó long double.strtol Convierte una cadena a un valor long.

strtoul Convierte una cadena a un valor unsigned long.strupr Convierte las minúsculas de una cadena a mayúsculas.

Tabla 5.1.- Funciones para el manejo de cadenas de caracteres.

5.6.- Control de acceso a los elementos de un arreglo

En C++, el acceso a los elementos de un arreglo tiene que ser controlado por el programador, ya que el lenguaje no restringe la posibilidad de accesar a posiciones de memoria que están más abajo de la última posición reservada para el arreglo. Lo mismo sucede cuando se manejan cadenas, donde el programador tiene la responsabilidad de controlar el acceso a los caracteres de la cadena, tomando como límite el terminador nulo. En el listado 5.6 se presenta un ejemplo de acceso a los 5 bytes colocados abajo del terminador nulo de una cadena dada por el usuario.

#include <stdio.h> // Para gets() y puts() #include <conio.h> // Para clrscr() y gotoxy() #include <string.h> // Para strlen() void main() { char nombre[31]; clrscr(); gotoxy(10,1); puts("¿ Cuál es tu nombre ? "); gotoxy(35,1); gets(nombre); clrscr(); gotoxy (10,1); puts("ELEMENTO CARACTER DIRECCION"); for( int x=0 ; (x <strlen(nombre)+5) && (x<23); x++) { gotoxy(10,x+2); printf("nombre[%2d] %c= %4d %9u", x,nombre[x],nombre[x],&nombre[x]); } }

Page 60: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Listado 5.6.- Colocación de los elementos de una cadena en memoria RAM.

6.- Variables automáticas, variables dinámicas y apuntadores

En esta unidad trabajaremos con los conceptos necesarios para el manejo de situaciones en que se requiere utilizar direcciones de memoria, ya sea que se trate de las direcciones correspondientes a las variables manejadas en la pila (variables automáticas) o a las variables manejadas en el montículo (variables dinámicas). Para el manejo de esas direcciones de memoria se requiere el uso de apuntadores.

También revisaremos la estrecha relación existente entre arreglos y apuntadores; y en la parte final retomaremos el tema del paso de parámetros para analizar la forma en que se pasa un parámetro cuando éste consiste de un arreglo o cuando consiste de una función.

6.1.- Conceptos básicos

Para efectos de nuestro estudio, llamaremos variables automáticas a las creadas en la pila (stack) y variables dinámicas a las creadas en el montículo (heap).

Un apuntador es una variable cuyo contenido es un valor entero sin signo( de 16 o 32 bits ), el cual representa una dirección de memoria. La dirección de memoria puede corresponder a una variable creada en la pila o en el montículo.

Los apuntadores son una de las herramientas más poderosas con que cuenta el Lenguaje C++ . Desafortunadamente, muchos programadores han creado el mito de que el estudio de los apuntadores es muy complicado, lo cual ha desarrollado una fobia entre quienes se inician en el estudio del lenguaje.

En las unidades anteriores se han utilizado variables automáticas. Para el manejo de las variables dinámicas es indispensable el uso de apuntadores; mientras que para las variables automáticas, los apuntadores son una alternativa.

En las figuras 6.1, 6.2 y 6.3 se muestra el manejo de la pila y el montículo en los seis modelos de memoria disponibles en el C++ de Borland.

Page 61: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Figura 6.1.- Modelo TINY

Figura 6.2.- Modelos SMALL y MEDIUM

Figura 6.3.- Modelos COMPACT, LARGE y HUGE

Para cada programa, dependiendo del espacio requerido para el código y para los datos, se puede utilizar un modelo de memoria específico. El modelo predeterminado en el C++ de Borland es el modelo SMALL.

A continuación se presenta una breve descripción de los modelos de memoria manejados por el C++ de Borland :

Page 62: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Con el modelo de memoria TINY se utiliza un solo segmento (64 KB) para el código, los datos y la pila. La utilización del modelo TINY es aconsejable únicamente cuando se quiere crear un archivo .COM

En el modelo SMALL, el código se almacena en un segmento y los datos en otro. Este modelo de memoria se recomienda para programas .EXE de tamaño medio.

Cuando se usa el modelo MEDIUM, los datos están limitados a un segmento pero el código puede utilizar varios (hasta 16 MB). El uso de este modelo se recomienda cuando se tiene un programa grande con pocos datos.

El modelo COMPACT es lo contrario de MEDIUM, ya que limita el código a un segmento mientras que los datos pueden ocupar varios (hasta 16 MB).Este modelo es recomendable cuando se tienen programas hasta de tamaño medio con muchas variables ó variables muy grandes.

En los modelos LARGE y HUGE, el código y los datos pueden ocupar varios segmentos (hasta 16 MB). La diferencia entre estos modelos es que el HUGE puede manejar variables automáticas que, en total, excedan los 64 KB.

La tabla 6.1 muestra los modificadores predeterminados para los apuntadores a funciones y apuntadores a datos en los diferentes modelos de memoria.

Modelode

Memoria

Apuntadoresa

Funciones

Apuntadoresa

DatosTINY near near

SMALL near nearMEDIUM far near

COMPACT near farLARGE far farHUGE far far

Tabla 6.1.- Modificadores predeterminados para apuntadores. NOTA: Cuando se compila un módulo, el código resultante no debe ser mayor que 64 KB, ya que debe caber en un segmento de código. Esto debe cumplirse aún cuando se esté utilizando un modelo de memoria que utilice varios segmentos para el código (MEDIUM, LARGE, HUGE).

De manera similar, en el caso del modelo HUGE no debe haber datos automáticos de más de 64 KB en cada módulo.

6.2.- Declaración de apuntadores

Page 63: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Los apuntadores son variables automáticas cuyos valores representan direcciones de memoria correspondientes a otras variables.

La sintáxis para la declaración de un apuntador es la siguiente:

tipo * identificador ;

Ejemplo: int *apint ; // Declaración del apuntador apint // Se dice que : "apint va a apuntar a // variables de tipo int" donde: apint es el nombre del apuntador * es el operador de indirección

Obsérvese que el operador de indirección utiliza el mismo símbolo que el operador de multiplicación.

En el ejemplo anterior, puede decirse que:

*apint se refiere al objeto apuntado por apint . apint es un apuntador a objetos de tipo int

Recuerde que: para referirse al objeto apuntado, se antepone el operador de indirección al nombre del apuntador.

Al declarar al apuntador, se le reserva espacio en la pila. Esto no implica que se esté reservando espacio para el objeto apuntado.

En la figura 6.4 se representa la asignación de espacio en la pila para el apuntador apint.

Figura 6.4.- Apuntador en la pila.

6.3.- Variables automáticas y apuntadores

Page 64: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Las variables automáticas se crean en tiempo de compilación y se destruyen al terminar la ejecución del módulo donde fueron declaradas.Aunque no es estrictamente necesario, se pueden manejar las variables automáticas por medio de apuntadores, como se muestra en el listado 6.1.

#include <iostream.h> #include <conio.h> void main() { int automatica ; // Se declara la variable automatica. int *apunt ; // Se declara el apuntador apunt, que apun-

// tará a objetos de tipo int. automatica = 100 ; // Se asigna el valor 100 a la variable // automatica. apunt = &automatica ; // Se asigna a apunt la dirección de // automatica ó apunt apunta a // automatica. clrscr(); cout << "VALOR=" << automatica << " \n"; // 100 *apunt="200" ; // Se asigna el valor 200 al objeto apunta- // do por apunt. cout << "VALOR=" << automatica << " \n"; // 200 getch(); }

Listado 6.1.- Ejemplo de variables automáticas y apuntadores.

Las instrucciones del listado 6.1 se traducen en la siguiente secuencia, donde los apuntadores se representan con una flecha (para simular que "apuntan hacia" ó "señalan" un objeto) y los objetos apuntados se representan por un cuadro (para simular un recipiente).

INSTRUCCION REPRESENTACION GRAFICA int automatica ; automatica |----------------| | ? | |----------------| int *apunt ; ----> ? automatica = 100 ; automatica |----------------| | 100 | |----------------|

apunt = &automatica ; automatica, *apunt |----------------| apunt ---->| 100 | |----------------|

Page 65: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

*apunt = 200 ; automatica, *apunt |----------------| apunt ---->| 200 | |----------------|

El estado final de la zona de memoria correspondiente al objeto apuntado y al apuntador se representa en la figura 6.5.

Figura 6.5.- Visualización del objeto apuntado y del apuntador en la memoria RAM.

Nótese que tanto el apuntador como el objeto apuntado se almacenan en la pila.

Las direcciones de memoria FFF0 , FFF2 , FFF4 , son hipotéticas.

Un apuntador es una variable que solo puede contener un valor a la vez, por lo que solo puede apuntar a un objeto al mismo tiempo.

Por otro lado, una variable cualquiera puede ser apuntada por varios apuntadores, ya que su dirección de memoria puede ser almacenada en distintas variables a la vez.

Al declarar un apuntador, se está especificando el tipo de variable al que va a apuntar.

Por ejemplo, no podrá declararse un apuntador a objetos de tipo int y después intentar utilizarlo para apuntar a objetos de tipo float.

Cuando se desee manejar un apuntador a cualquier tipo de objeto, se puede declarar de tipo void, como en:

void *multiusos ;

En el listado 6.2 se muestra un ejemplo de aplicación de un apuntador de tipo void.

#include <iostream.h> #include <conio.h> #define NL cout << "\n" void main() { int varent="0" ; float varflot="0.0" ; void *apmulti="&varent;" // apmulti APUNTA A varent *(int

Page 66: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

*)apmulti="2" ; // ASIGNA 2 AL OBJETO NL; // APUNTADO POR apmulti cout << varent ; apmulti="&varflot" ; // apmulti APUNTA A varflot *(float *)apmulti="1.1" ; // ASIGNA 1.1 AL OBJETO APUNTADO // POR apvoid NL; cout << varflot ; NL; getch(); }

Listado 6.2.- Utilización de apuntadores de tipo void.

Del listado 6.2, analicemos la instrucción:

*(int *)apmulti = 2 ;

en donde:

apmulti es un apuntador de tipo void.

(int *)apmulti está forzando a que apmulti apunte a objetos de tipo int.

*(int *)apmulti se refiere a un objeto de tipo entero apuntado por apmulti.

En los ejemplos manejados hasta aquí se ha supuesto el modelo de memoria SMALL, por lo que los apuntadores son near por predeterminación.

El hecho de que sean near significa que solo disponen de dos bytes para almacenar una dirección de memoria. Al disponer de dos bytes, el número entero sin signo más grande que pueden almacenar es 65535, el cual corresponde a la dirección más alta de un segmento de memoria.

Cuando se está utilizando el modelo de memoria SMALL y se quiere accesar una dirección de memoria de otro segmento, se debe utilizar el modificador far al declarar el apuntador correspondiente. Un apuntador far dispone de cuatro bytes; en los dos primeros bytes almacena la dirección del segmento y en los otros dos la dirección del desplazamiento. De esta forma se puede accesar una dirección de memoria que se encuentre en un segmento diferente al segmento de datos en uso.

En el listado 6.3 se muestra el manejo de un apuntador far para rellenar la pantalla con el caracter tecleado. La ejecución del programa termina cuando se pulsa la tecla .

#include <iostream.h> #include <conio.h> void main() { int far *aplej ; // Declara un apuntador far a enteros char c ; int ren, col ; clrscr(); cout << "Teclee caracteres ( < Enter >="Salida" ) : " ; aplej="(int" far *) 0xB8000000 ; while(( c="getche())" !="\r" ) for( ren="0" ; ren <

Page 67: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

25 ; ren ++ ) for( col="0" ; col < 80 ; col++ ) *(aplej + ren*80 + col )="c" | 0x0700 ; clrscr(); }

Listado 6.3.- Utilización de un apuntador far a enteros.

A continuación, se explican algunas lineas del listado 6.3.

En:

int far *aplej ;

se declara un apuntador lejano a objetos de tipo entero. Observe que la sintaxis que rige la declaración es:

tipo far *identif ;

En la linea:

aplej = (int far *) 0xB8000000 ;

la parte

(int far *) representa un forzamiento de tipo para que la constante hexadecimal sea manejada por un apuntador lejano.

0xB8000000 representa la direccón de memoria reservada para uso exclusivo en los modos de video CGA y EGA.

En este caso, B800 es la dirección del segmento, y 0000 es la dirección del desplazamiento.

Por último, en la linea:

*(aplej + ren*80 + col ) = c | 0x0700 ;

la parte: aplej + ren*80 + col calcula la dirección de memoria correspondiente a cada caracter.

*(aplej + ren*col +80 ) representa al objeto apuntado (un caracter).

mientras que:

c | 0x0700

Page 68: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

es una operación OR a nivel de bits para asegurar que el caracter c tendrá los atributos de caracter normal.

6.4.- Apuntadores y constantes

Un apuntador o un objeto apuntado pueden declararse con el modificador const, y en tal caso no se les podrá asignar un nuevo valor.Por ejemplo, en el listado 6.3 la constante B8000000 se asigna al apuntador aplej. El apuntador aplej fué declarado como una variable, por lo que sería válido asignarle cualquier nuevo valor.Si deseamos que el valor B8000000 permanezca sin cambios en aplej, deberemos escribir la siguiente declaración:

int far *const aplej = (int far *) 0xB8000000L;

con lo cual no podrá asignarse un nuevo valor al apuntador aplej.El valor de una constante no puede cambiar, pero sí puede utilizarse para realizar operaciones, como se observa en la siguiente fracción de línea del listado 6.3 :

aplej + ren*80 + col

En este caso, se utiliza el valor de aplej para calcular la dirección de memoria donde se va a almacenar cada carácter.

6.5.- Variables dinámicas y apuntadores

Las variables dinámicas se manejan en el montículo, y deben su nombre al hecho de que pueden ser creadas y destruidas durante el tiempo de ejecución de un módulo.

Para el manejo de variables dinámicas se hace indispensable la utilización de apuntadores, así como de funciones especiales para la asignación y liberación de la memoria correspondiente a dichas variables.

Tanto en C como en C++ existen funciones tales como malloc() y free() para la asignación y liberación de memoria del montículo.

Además de estas funciones, el C++ cuenta con dos operadores interconstruidos:

new que realiza una labor parecida a la de la función malloc(), asignando un bloque de memoria.

delete que libera un bloque de memoria asignada en tiempo de ejecución, de manera semejante a como lo hace free().

Page 69: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

En el listado 6.4 se muestra un ejemplo de aplicación de los operadores new y delete.

#include <iostream.h>

void main() { int *apent; // Declara un apuntador a entero

apent = new int ; // Reserva un bloque de memoria dinámica // de 2 bytes para manejarlo por medio // de apent. *apent = 10 ; // Asigna el valor 10 al objeto apuntado // por apent. cout << *apent ; // Despliega el contenido del objeto // apuntado por apent. delete apent ; // Libera el espacio de memoria manejado // por apent. }

Listado 6.4.- Aplicación de new y delete.

En el programa del listado 6.4, se supone que la reservación será exitosa porque existe espacio suficiente en el montículo.

Pero ¿quién asegura que el espacio requerido por new está disponible?. Para controlar esta situación y evitar un mensaje de error por parte del sistema en tiempo de ejecución, en 6.5 se propone una nueva versión del programa.

#include <iostream.h> #include <stdlib.h> // Para exit().

void main() { int *apent; // Declara un apuntador a entero

if((apent = new int)==NULL)// Intenta reservar un bloque de { // memoria dinámica de 2 bytes ra // manejarlo por medio de apent.

cout << "NO hay espacio suficiente\n"; exit(1); // Finaliza la ejecución del programa. } *apent="10" ; // Asigna el valor 10 al objeto apuntado // por apent. cout << *apent ; // Despliega el contenido del objeto // apuntado por apent. delete apent ; // Libera el espacio de memoria manejado // por apent. }

Listado 6.5.- Reservación de memoria para una variable dinámica.

Los operadores new y delete pertenecen al Lenguaje C++ , de tal manera que no se requiere incluir ningún archivo de cabecera para utilizarlos.

Para crear un arreglo de 25 elementos de tipo double, en el montículo, puede escribirse:

double* dap ;dap = new double[25];

Page 70: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

ó su forma equivalente:

double* dap = new double[25];

En este ejemplo, se está declarando a dap como un apuntador a variables dinámicas de tipo doble; al tiempo que se le asigna el valor retornado por new. El valor retornado por new es la dirección del inicio de un bloque de memoria del tamaño requerido para almacenar 25 elementos de tipo double.

En caso de que el montículo no disponga del espacio requerido, new retorna el valor NULL ( nulo ).

Aunque, cuando se requiere memoria a través de new se debe indicar el tamaño exacto del bloque requerido, el tamaño del bloque se puede dar en tiempo de ejecución como se muestra en el listado 6.6.

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

void main() { unsigned int n; clrscr(); gotoxy(20,1); cout << "NUMERO DE ELEMENTOS DEL ARREGLO: "; cin>> n; float* apf; if((apf=new float[n])==NULL) { cout << "NO HAY ESPACIO SUFICIENTE EN EL MONTICULO\N"; exit(1); } for(int x="0" ; x < n ; x++) { gotoxy(20,x+3); *(apf+x)="x+100.25;" cout << *(apf+x) << "\n"; } delete apf; getch(); }

Listado 6.6.- Manejo de un arreglo en el montículo.

6.6.- Apuntadores y arreglos

Como se mencionó en la unidad 5, el tema de arreglos está íntimamente ligado al de apuntadores; tanto que es posible intercambiarlos en la solución de un problema.

El nombre de un arreglo corresponde al de un apuntador que almacena un valor constante. Este valor constante es la dirección de memoria del primer elemento del arreglo.

Por ejemplo :

Page 71: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

int calif[]={100,90,95,80,90}; // Declaración e // inicialización // de un arreglo // de 5 enteros

se puede representar con la figura 6.6.

Figura 6.6.- El arreglo calif[ ] en la pila.

En realidad, el lenguaje manejará al arreglo a través de un apuntador llamado calif, el cual tiene almacenado el valor 65494, que a su vez corresponde a la dirección de inicio del elemento calif[0].

La representación del apuntador calif, en la zona de variables globales de la memoria RAM, es la siguiente :

En el listado 6.7 se presenta el manejo del arreglo calif[ ], a través de la notación de arreglos, y en el listado 6.8 el manejo con la notación de apuntadores.

#include <iostream.h> void main() { int calif[] = { 100,90,95,80,90}; for(int i=0 ; i <5 ; i++) //Notación de arreglos. cout << "\n" << calif[i] ; }

Listado 6.7.- Manejo de calif[] , notación de arreglos.

#include <iostream.h> void main() {

Page 72: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

int calif[] = { 100,90,95,80,90}; for(int i=0 ; i <5 ; i++) // Notación de apuntadores cout << "\n" << *(calif+i) ; }

Listado 6.8.- Manejo de calif[] , notación de apuntadores.

Como puede observarse, la única diferencia entre los listados 6.7 y 6.8 es que el primero utiliza calif[i] y el segundo *(calif+i).

Debido a que la ejecución de los programas de ambos listados producen resultados iguales, se deduce que:

calif[i] == *(calif+i)

Para entender esto que a simple vista no es obvio, revisaremos algunos conceptos: 1.- El nombre del arreglo corresponde al de un apuntador que apunta al primer elemento del arreglo, por lo que:

calif apunta a calif[0]

Visto gráficamente :

2.- Para hacer referencia a un elemento específico del arreglo, se toma como base la dirección del primer elemento y, con el subíndice del elemento específico, se calcula su dirección. Por ejemplo, para referirse al segundo elemento del arreglo puede procederse así :

calif[1] // Notación de arreglos

ó

*(calif+1) // Notación de apuntadores, donde la expresión calif+1sirve para calcular la dirección del elemento que está una posición más allá del elemento apuntado por calif.

Para referirse a calif[2] ( tercer elemento ), puede escribirse:

*(calif+2)

Lo que significa: "El objeto que se encuentra dos posiciones después del objeto apuntado por calif". En este caso, una posición es el espacio requerido por cada uno de los elementos, de tal manera que, si calif apunta a la dirección 65494, entonces calif+2 es la expresión que calcula la dirección 65498. La figura 6.7 muestra los

Page 73: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

elementos del arreglo calif[] con sus nombres en notación de arreglos y en notación de apuntadores.

Figura 6.7.- El arreglo calif[] en la pila. Notación en arreglos y en apuntadores.

De lo anterior, se infiere la regla: calif[i] == *(calif+i)por lo que: &calif[i] == calif+i

Esto es que, la dirección del i-ésimo elemento de un arreglo se calcula sumándole el subíndice a la dirección del primer elemento.

Como otro ejemplo, supongamos la siguiente declaración correspondiente a un arreglo de 10 elementos de tipo float. float* sueldo[10];

Si la dirección del primer elemento es 65494, entonces:

&sueldo[5] es igual a : sueldo+5 = 65494 + ( 5 * 4 ) = 65514 ^ | Tamaño de float ---------|

que corresponde a la dirección del elemento que se encuentra 5 posiciones más adelante de aquél apuntado por sueldo.

Como regla, podemos establecer que el subíndice se refiere a la posición del elemento en el arreglo. Es por esto que al calcular la dirección por medio del subíndice, éste debe multiplicarse por el número de bytes que representan el tamaño de cada elemento ( dado por el tipo utilizado en la declaración del arreglo ).

6.6.1.- Apuntadores y cadenas

Page 74: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Como se vió en la unidad 5, una cadena es un arreglo de caracteres cuyo último elemento es el caracter nulo.

Utilizando la nomenclatura de arreglos, podemos declarar:

char nombre[ ] = "PACO" ;

Esto mismo puede hacerse por medio de apuntadores, como se muestra en el listado 6.9.

#include <iostream.h> #include <conio.h> #include <stdio.h> void main() { char *nombre = "PACO" ; clrscr();

gotoxy(30,12); cout<< "!! HOLA, " ; puts(nombre); gotoxy(43,12); cout << " !!"; getch(); }

Listado 6.9.- Manejo de un arreglo de caracteres (cadena) por medio de apuntadores.

6.7.- Arreglos de apuntadores

Los apuntadores pueden manejarse en un arreglo, de tal forma que:

char nombres[ ][5] = { "HUGO", "PACO", "LUIS" } ;

es la declaración de un arreglo de cadenas, con asignación de valores iniciales. Su equivalente en notación de apuntadores es:

char *nombres[ ] = { "HUGO", "PACO", "LUIS" } ;

en el que se declara un arreglo de apuntadores. El listado 6.10 muestra el programa completo para el manejo de este ejemplo.

#include <iostream.h> #include <conio.h> #include <string.h> void main()

Page 75: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

{ char *nombres[ ] = { "HUGO", "PACO", "LUIS" } ; char invitado[11]; int bandera; clrscr(); gotoxy(30,10); cout << "CUAL ES SU NOMBRE ? " ; gotoxy(50,10); cin>> invitado ; gotoxy(30,12); for( int x = 0 ; x <3 ; x++ ) if(strcmp(invitado, nombres[x])="=" 0) bandera="0;" if(bandera="=" 0) cout << "!! PASE, ESTIMADO " << invitado << " !!"; else cout << "!! FUERA DE AQUI, " << invitado << " !!"; getch(); }

Listado 6.10.- Manejo de un arreglo de apuntadores.

6.8.- Paso de arreglos como parámetros

Un arreglo puede pasarse como parámetro a una función. Si tuviera que pasarse por valor un arreglo muy grande, sería un desperdicio de memoria. En el Lenguaje C++ el paso de arreglos se hace por referencia, ya que el nombre del arreglo corresponde a un apuntador al primer elemento del arreglo.

Al pasar un parámetro correspondiente a un arreglo, se pasa la dirección del primer elemento, por lo que la función invocada puede modificar cualquier elemento del arreglo.

El listado 6.11 contiene un programa que maneja una función llamada nputs() , la cual recibe como parámetro un arreglo de caracteres.

#include <iostream.h> #include <conio.h> #include <stdio.h> #include <string.h> void nputs(char *); void main() { char cadena[81]; clrscr(); gotoxy(10,10); cout << "ESCRIBA UNA CADENA: "; gets(cadena); gotoxy(10,12); nputs(cadena); getch(); } void nputs(char cad[ ]) { int x="0;" while(cad[x]) { cout << cad[x] ; x++; } }

Listado 6.11.- Manejo de nputs() con arreglos. En el listado 6.12 se muestra el manejo de la función nputs(), por medio de apuntadores.

Page 76: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

#include <iostream.h> #include <conio.h> #include <stdio.h> #include <string.h> void nputs(char *); void main() { char cadena[81]; clrscr(); gotoxy(10,10); cout << "ESCRIBA UNA CADENA: "; gets(cadena); gotoxy(10,12); nputs(cadena); getch(); } void nputs(char *cad) { while(*cad) cout << *cad++ ; }

Listado 6.12.- Manejo de nputs() con apuntadores.

.9.- Paso de funciones como parámetros

Toda función tiene asociada una dirección de inicio de código, la cual puede pasarse como parámetro en la invocación a otra función, como se muestra en el listado 6.13.

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

int cmpcad(char*, char*); void compara(char*, char*, int(*)(char*, char*)); void main() { char cadx[80], cady[80];

clrscr(); gotoxy(10,5); cout << "ESCRIBA UNA CADENA : " ; cin>> cadx; gotoxy(10,7); cout << "ESCRIBA OTRA CADENA : " ; cin>> cady; gotoxy(10,9); compara(cadx, cady, cmpcad); gotoxy(1,24); } void compara(char *cad1, char *cad2, int (*cmpcad)(char*, char*)) { if(!(*cmpcad)(cad1,cad2)) cout << "LAS CADENAS SON IGUALES"; else cout << "LAS CADENAS SON DISTINTAS"; } int cmpcad(char *x, char *y) { return(strcmp(x,y)); }

Page 77: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Listado 6.13.- Paso de apuntadores como parámetros a funciones.

En el listado 6.13, la expresión :

int(*cmpcad)(char*, char*)

establece que cmpcad es un apuntador a una función, la cual devuelve un valor de tipo entero .

6.10.- Apuntadores a apuntadores

Como se vió al principio de la unidad, un apuntador también es una variable. Su dirección puede ser almacenada por otra variable apuntador, por lo que puede hablarse de un apuntador a un apuntador.

Esto puede extrapolarse para dos o más variables, como se observa en el listado 6.14.

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

void main() { int x, *a, **b, ***c ; // 1 clrscr(); a = &x ; // 2 *a = 100 ; // 3 b = &a ; // 4 **b += *a ; // 5 c = &b ; // 6 ***c += **b + *a ; // 7 cout << " *a=" << *a << " \n" ; cout << " **b=" << **b << " \n" ; cout << "***c=" << ***c << " \n" ; getch(); }

Listado 6.14.- Ejemplo de apuntadores a apuntadores. A continuación se analizan las líneas marcadas en el listado 6.14.

int x, *a, **b, ***c; // 1

Se declaran:

x como una variable de tipo entero. a como un apuntador a objetos de tipo entero. b como un apuntador a un apuntador, el cual a su vez apuntará a objetos de tipo entero. Se dice que b es "el apuntador del apuntador". c como un apuntador a un apuntador que apunta a otro apuntador, el cual a su vez apunta a objetos de tipo entero. Se dice que c es "el apuntador del apuntador del apuntador".

La pila luciría así:

Page 78: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

a = &x ; // 2

Se asigna, al apuntador a, la dirección de x. La pila luciría así:

*a = 100 ; // 3

Al objeto apuntado por a se le asigna el valor 100. La pila luciría así:

b = &a ; // 4

Al apuntador b se le asigna la dirección del apuntador a. La pila luciría así:

**b += *a ; // 5

Al objeto apuntado por el apuntador apuntado por b se le suma el valor del objeto apuntado por a. La pila luciría así:

Page 79: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

c = &b ; // 6

Al apuntador c se le asigna la dirección del apuntador b. La pila luciría así:

***c += **b + *a ; // 7

Se asigna al objeto apuntado por el apuntador apuntado por el apuntador c, el valor del objeto apuntado por el apuntador apuntado por el apuntador b más el valor del objeto apuntado por el apuntador a. La pila luciría así:

_ Gif animado

_ Applet animado

El listado 6.15 muestra otro ejemplo de manejo de apuntadores a apuntadores. En él, la línea de cabecera de la definición de main() contiene argumentos entre los paréntesis. Esto significa que, al invocar al programa ECO.EXE desde la línea de comandos, se le pueden pasar argumentos. Estos argumentos serán tomados como cadenas de caracteres que serán manejadas por **argumentos. La variable entera contador contiene el número correspondiente a la cantidad de cadenas que se le pasan a main() en la invocación.

// ECO.CPP : Maneja argumentos en la invocación a main(), // de manera similar a como trabaja la orden

Page 80: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

// ECHO del sistema operativo

#include <iostream.h>

void main(int contador, char **argumentos) { cout << "\n"; for( int x="1" ; contador--> 0 ; x++) {

int y = 0 ; do{ cout << argumentos[x][y++]; } while(argumentos[x][y]); cout

<< " "; } cout << "\n"; }

Listado 6.15.- Paso de argumentos a main(). Por ejemplo, si escribimos en la línea de comandos:

A:\> ECO HOLA AMIGOS !!!

en la pantalla aparecerá:

HOLA AMIGOS !!!

En este caso:

argumentos[0] = ECO 1 argumentos[1] = HOLA 2 argumentos[2] = AMIGOS 3 argumentos[3] = !!! 4 <---- Número de argumentos contador="4"

La línea de cabecera de la función main() pudo escribirse de esta otra forma:

void main(int contador, char *argumentos[]) ya que existe la siguiente equivalencia :

Esto puede generalizarse, de manera que:

*cadena == cadena[] **cadena == *cadena[] ***cadena == **cadena[]

El uso de corchetes vacíos sólo es válido cuando se realiza una asignación ó cuando se escriben los argumentos en la línea de cabecera de la definición de una función. Sólo el par de corchetes de la extrema derecha puede escribirse vacío, los demás deberán contener un valor constante.

La línea de cabecera en la definición de la función main() puede tomar también la siguiente forma:

void main(int contador, char **argumentos, char **ambiente)

Page 81: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

donde:

**ambiente se refiere a un conjunto de cadenas de caracteres correspondientes al ambiente del sistema operativo, como las asignadas a prompt, path , etc.

7.- Tipos definidos por el programador

Hasta aquí, se ha trabajado con los tipos de datos fundamentales (predefinidos por el lenguaje), y las variables han sido declaradas para almacenar un solo valor de un tipo especificado. Sólo en el caso de arreglos se han declarado conjuntos de valores, pero todos ellos del mismo tipo.En esta unidad, estudiaremos las formas existentes para definir nuevos identificadores para los tipos fundamentales, así como la manera en que se declaran las variables compuestas de elementos de diferentes tipos.

7.1.- El especificador typedef

La palabra reservada typedef representa un especificador de clase de almacenamiento, y se utiliza para definir nuevos especificadores o sinónimos para los tipos existentes.

Por ejemplo :

typedef int entero ;

define a entero como un sinónimo para el tipo int .

Una vez definido un sinónimo, se puede utilizar para definir nuevos especificadores, de manera que un tipo puede tener varios, como se muestra a continuación.

typedef float flotante ; // Define a flotante como sinónimo de // float

typedef flotante FLOAT ; // Define a FLOAT como sinónimo de // flotante

typedef FLOAT FLOTANTE ; // Define a FLOTANTE como sinónimo de // FLOAT

typedef float Float ; // Define a Float como sinónimo de float

En este caso, flotante, FLOAT, FLOTANTE y Float son nuevos identificadores o sinónimos de float .

Page 82: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Otro ejemplo es :

typedef long double gigante ; // Define a gigante como sinónimo de // long double

Cabe hacer notar que typedef no crea nuevos tipos, sino que define nuevos nombres para los tipos existentes.

7.2.- Estructuras

En ocasiones es necesario contar con tipos de datos que estén compuestos por conjuntos de elementos de diferentes tipos entre sí. En C++, esto se logra por medio de estructuras, las cuales son tipos derivados definidos por el usuario que representan colecciones de elementos.

7.2.1.- Definicion de tipos de estructuras

La palabra struct se utiliza para definir un tipo compuesto.

Por ejemplo, si se requiere definir un nuevo tipo llamado catalogo cuyos elementos sean:

clave número entero , precio número de punto flotante

se puede escribir: |------ Etiqueta | struct catalogo { int clave ; float precio ; } ;

En este caso, catalogo es un nuevo tipo que puede utilizarse para declarar una estructura como:

catalogo refac ;

7.2.2.- Acceso a los miembros de una estructura

Page 83: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Una vez declarada una estructura, pueden asignárseles valores iniciales a sus miembros, como en:

refac.clave = 1001 ;

refac.precio = 624.50 ;

Obsérvese que el operador punto sirve para representar la dependencia de cada uno de los miembros con su estructura; esto es: refac.clave se refiere al miembro clave de la estructura refac.

El operador punto es necesario porque, al declararse varias estructuras del mismo tipo, habrá que distinguir los correspondientes miembros de unas y otras.

Por ejemplo, si escribimos:

catalogo refac, muebles ;

En este caso se declaran dos estructuras de tipo catalogo, cuyos identificadores son refac y muebles.

Entonces podemos realizar las siguientes asignaciones:

refac.clave = 2001 ; refac.precio = 360.45

muebles.clave = 4001 ; muebles.precio = 2500.50 ;

En este caso, los miembros clave y precio de las estructuras refac y muebles son inconfundibles.

El listado 7.1 muestra el código fuente para este ejemplo.

#include <iostream.h>

void main() { struct catalogo { int clave ; float precio ; } ; catalogo refac, muebles;

refac.clave = 2001 ; refac.precio = 360.45 ;

muebles.clave = 4001 ; muebles.precio = 2500.50;

Page 84: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

cout << refac.clave << " " << refac.precio << "\n"; cout << muebles.clave << " " << muebles.precio; }

Listado 7.1.- Definición y uso de un tipo struct.

En el listado 7.1 se observa que primero se define el tipo catalogo y después se utiliza para declarar las estructuras refac y muebles.

El tipo catalogo está disponible para declarar otras estructuras, además de refac y muebles; pero, si existe la seguridad de que eso no será necesario, se puede cambiar el código fuente a la forma presentada en el listado 7.2.

#include <iostream.h> void main() { struct { int clave ; float precio ; } refac, muebles ;

refac.clave = 2001 ; refac.precio = 350.45 ;

muebles.clave = 4001 ; muebles.precio = 2500.50;

cout << refac.clave << " " << refac.precio << "\n"; cout << muebles.clave << " " << muebles.precio; }

Listado 7.2.- Utilización de un tipo struct desechable.

Como puede observarse en el listado 7.2, puede definirse un tipo struct sin etiqueta, escribiendo la primera línea así :

struct {

y la declaración de las estructuras a utilizar deberá escribirse antes del punto y coma de la última línea, como en:

} refac, muebles ;

Puede decirse que se ha definido un tipo "desechable", ya que sólo lo utilizamos para declarar a las estructuras refac y muebles, además de que no le asignamos una etiqueta.

7.2.3.- Arreglos de Estrucuras

Page 85: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Las estructuras, al igual que las demás variables, pueden agruparse en arreglos como se muestra en el listado 7.3.

#include <iostream.h> #include <string.h>

// DEFINE UN TIPO DE ESTRUCTURA LLAMADO dicc struct dicc { char *ingl ; char *espa ; } ;

// DECLARA UN ARREGLO DE ESTRUCTURAS LLAMADO tabla Y ASIGNA // VALORES INICIALES A LOS ELEMENTOS DEL ARREGLO. dicc tabla[] = { "absolute","absoluto", "append","anexar", "begin","iniciar,principiar,comenzar", "close","cerrar", "do","hacer", "file","archivo", "while","mientras" };

// DEFINE A NUMESTR, QUE CONTIENE EL NUMERO DE ESTRUCTURAS // QUE COMPONEN EL ARREGLO tabla #define NUMESTR (sizeof(tabla)/sizeof(dicc)) int busqbin(char* , dicc * , int) ;

void main() { char pal[80]; int nret ;

do{ cout << "\n\nPara finalizar, escriba FIN en : \n"; cout << "\n Palabra en inglés : "; cin>> pal; nret = busqbin(pal,tabla,NUMESTR);

if(nret == -1) { if(strcmp(strupr(pal),"FIN")==0) cout << "\n\n!! HASTA LUEGO !!\n\n"; else cout << "\n\nPALABRA NO REGISTRADA\n\n"; } else cout << "\n" << pal << "=" << tabla[nret].espa ; }while(strcmp(strupr(pal)," FIN")!="0);" } // FUNCION QUE, DADA UNA PALABRA EN INGLES, REALIZA UNA // BUSQUEDA BINARIA EN EL ARREGLO tabla, RETORNANDO EL // NUMERO DE ESTRUCTURA DONDE SE ENCUENTRA LA palabra int busqbin(char *pal, dicc *t, int N) { int bajo="0," alto="N-1," medio, cond; while(bajo <="alto)" { medio="(bajo" + alto) / 2 ; if((cond="strcmp(pal," t[medio].ingl)) < 0) alto="medio" 1 ; else if(cond> 0) bajo = medio + 1 ; else return(medio) ; }

Page 86: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

return(-1) ; }

Listado 7.3.- Manejo de un arreglo de estructuras

7.2.4.- Apuntadores a estructuras

Al declarar una estructura se le asocia un bloque de memoria en la pila ó en el montículo. La dirección inicial del bloque puede asignarse a un apuntador, por lo que la estructura puede manejarse a través de éste.

Un apuntador a una estructura se declara de la misma forma en que se declara cualquier apuntador.

Por ejemplo, si usamos el tipo dicc definido anteriormente, podemos escribir la línea:

dicc *apd ;

que declara a apd como un apuntador a objetos de tipo dicc.

En este momento, se puede declarar una variable dinámica utilizando el apuntador apd, como se muestra a continuación:

apd = new dicc;

El acceso a los elementos de la estructura apuntada por apd se logra a través del "operador flecha" -> , que se forma con los símbolos:

- ( menos ) y > ( mayor que )

En el listado 7.4 se muestra el manejo de una lista simplemente ligada cuyos nodos están compuestos por estructuras de tipo alumno.

#include <iostream.h> #include <conio.h> #include <stdio.h> #include <stlib.h> #include <string.h> #define IZQ 20 #define NOMEM "\nMEMORIA INSUFICIENTE....\n" #define VACIA "\nLISTA VACIA....\n" #define PULSE "\nPULSE CUALQUIER TECLA PARA CONTINUAR....\n" #define ELIM "\nNODO ELIMINADO....\n" #define NOEX "\nNOMBRE INEXISTENTE....\n" void inserta(); void elimina();

Page 87: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

void despliega(); // Define a alumno como un tipo de estructura struct alumno { char nombre[31]; int calif; struct alumno *sig; // Liga a cada nodo }; // con el siguiente. alumno *primero, *actual, *previo; // Declara apuntadores // a estructuras de char cad[31]; // tipo alumno. void main() { char opcion; primero = actual = previo = NULL ; // Controla apuntadores for(;;) // Ciclo indefinido { clrscr(); gotoxy(IZQ,1);cout << "LISTA SIMPLEMENTE LIGADA"; gotoxy(IZQ,4);cout << "1.- INSERCION DE UN NODO"; gotoxy(IZQ,6);cout << "2.- ELIMINACION DE UN NODO"; gotoxy(IZQ,8);cout << "3.- DESPLEGADO DE LA LISTA"; gotoxy(IZQ,10);cout << "0.- F I N"; gotoxy(IZQ,14); cout << "Escriba el número de su opción : "; do{ gotoxy(55,14); clreol(); opcion="getche();" } while((opcion < '0') || (opcion> '3')); clrscr();

switch(opcion) { case '1' : inserta(); break; case '2' : elimina(); break; case '3' : despliega(); break; case '0' : exit(0); } } } void inserta() { alumno *nuevo; // Declara apuntador a estructura nuevo = new alumno;// Crea nodo if(!nuevo) // Si nuevo = NULL ( No hay memoria) { cout << NOMEM << PULSE; getch(); return; } gotoxy(IZQ,2); cout << "INSERTANDO....";// Si hay memoria gotoxy(1,4); cout << "NOMBRE : "; gets(nuevo->nombre); // Lee campo nombre para nodo nuevo gotoxy(wherex()+35,wherey()-1); cout << "\nCALIFICACION : "; cin>> nuevo->calif; // Lee campo calif para nodo nuevo if(!primero) // Lista vacía { primero = nuevo; // primero apunta a donde apunta nuev primero->sig = NULL ; // Liga de primero apunta a NULL } else // Lista no vacia {

Page 88: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

nuevo->sig = primero; // Liga de nuevo apunta a donde // apunta primero primero = nuevo ;//primero apunta a donde apunta nuevo } }

void elimina() { if(!primero) // Lista vacía { cout << VACIA << PULSE; getch(); return; } cout << "NOMBRE A ELIMINAR : "; gets(cad); actual="primero;" // actual apunta a donde apunta primero while(actual)//Mientras actual no apunte a NULL(fin lista) { // Si campo nombre de nodo apuntado por actual="cad" if(strcmp(strupr(cad),strupr(actual->nombre))==0) { if(actual==primero) // Si actual apunta a donde // apunta primero primero = actual->sig; // primero apunta a // donde apuntaliga de actual previo->sig = actual->sig; // Liga de previo apunta // a donde apunta liga // de actual delete actual; // Elimina nodo apuntado por actual cout << ELIM << PULSE; getch(); return; } else // nombre de nodo apuntado por actual !="cad" { previo="actual;" // previo apunta a donde apunta // actual actual="actual-">sig; // actual apunta a donde // apunta liga de actual } } cout << NOEX << PULSE; getch(); } void despliega() { actual="primero;" // actual apunta a donde apunta primero if(!actual) // Si actual apunta a NULL ( lista vacía ) { cout << VACIA << PULSE; getch(); return; } while(actual) // Mientras actual no apunte a NULL { cout << "\n" << actual->nombre;//Despliega nombre y cout << " " << actual->calif;//calif de actual actual = actual->sig; // Actual apunta a donde apunta // liga de actual } cout << "\n\n" << PULSE ; getch(); }

Listado 7.4.- Manejo de una lista simplemente ligada

7.3.- Uniones

Los tipos union comparten muchas de las características sintácticas y funcionales de los tipos struct, sin embargo existen algunas diferencias; la principal es que la union permite que solamente uno de sus miembros esté activo a la vez, mientras que en la estructura todos los miembros están activos al mismo tiempo. Otra diferencia es que el tamaño de la union es el tamaño del miembro más grande, mientras que en la estructura su tamaño es la suma de los tamaños de sus miembros.

Page 89: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Las uniones son recomendables cuando se van a manejar variables que pueden compartir espacio en la memoria, debido a que sus valores van a requerirse en momentos diferentes.

Por ejemplo, sean tres variables :

una de tipo int, otra de tipo float, y otra de tipo double.

Si no van a manejarse simultáneamente, puede definirse un nuevo tipo union que las abarque, así:

union nums { int x ; // 2 bytes float y ; // 4 bytes double z ; // 8 bytes } ;

En este momento se cuenta con un nuevo tipo llamado nums, así que podemos utilizarlo para efectuar la siguiente declaración:

nums varnums ; // Se declara la variable varnums // que ocupa 8 bytes de memoria.

Se pueden accesar los miembros de varnums a través del operador punto, por ejemplo :

varnums.x = 10 ; // Se asigna el valor 10 al miembro // x de la union varnums.

En el listado 7.5 se muestra un programa completo para el manejo de este ejemplo.

#include <iostream.h> void main() { union nums{ int x ; float y ; double z ; }; nums varnums ; varnums.y = 200000.125 ; varnums.z = 3000000.3333 ; varnums.x = 10 ; // Despliega el valor 10 y dos valores inesperados.

Page 90: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

cout << varnums.x << " " << varnums.y ; cout << " " << varnums.z << "\n" ; varnums.x="10" ; varnums.z="3000000.3333" ; varnums.y="200000.125" ; // Despliega un valor inesperado, el valor 200000.125 y // un valor inesperado. cout << varnums.x << " " << varnums.y ; cout << " " << varnums.z << "\n" ; varnums.x="10" ; varnums.y="200000.125" ; varnums.z="3000000.3333" ; // Despliega dos valores inesperados y 3000000.3333 cout << varnums.x << " " << varnums.y ; cout << " " << varnums.z << "\n" ; }

Listado 7.5.- Acceso a los miembros de una union

Los miembros de una union pueden ser de cualquier tipo, incluyendo estructuras, como se muestra en el listado 7.6.

#include <iostream.h> #include <dos.h> // Para int86() void main() { REGS regs; unsigned int tam ; int86(0x12, &regs, &regs) ; tam = regs.x.ax ; cout << "\nEl tamaño de la memoria convencional es de " ; cout << tam << " KiloBytes\n" ; }

Listado 7.6.- Uso de estructuras dentro de uniones. La union REGS está definida en el archivo dos.h de la siguiente forma :

struct WORDREGS { unsigned int ax, bx, cx, dx, si, di, flags ; }; struct BYTEREGS { unsigned char al, ah, bl, bh, cl, ch, dl, dh ; }; union REGS { WORDREGS x ; BYTEREGS h ; };

Puede declararse una union sin etiqueta, y en tal caso se le llama union anónima. En el listado 7.7 se muestra un ejemplo.

#include

Page 91: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

void main() { union { // Nótese la ausencia de etiqueta int a ; char b ; }; // No se ha definido un tipo union, sino una // union anónima. a=1 ; // Se accesan los miembros sin utilizar b='X'; // el operador punto, ya que no existe // un identificador para la union. // Despliega un valor inesperado y el valor X cout << a << " " << b << "\n"; b="X" ; a="1" ; // Despliega el valor 1 y un valor inesperado cout << a << " " << b << "\n"; }

Listado 7.7.- Manejo de uniones anónimas.

8.- Programación orientada a objetos

La Programación Orientada a Objetos es un método de programación que intenta imitar la forma en que modelamos el mundo real y tiene como meta principal mejorar la eficiencia y productividad de los grupos de programadores. Muchos expertos afirman que la Programación Orientada a Objetos ayuda a reducir la complejidad, especialmente en programas grandes ( de 10,000 líneas o más ), además de que permite a los programadores reutilizar el código existente en lugar de reescribir módulos que ya trabajan correctamente.

8.1.- Conceptos básicos

Para enfrentarse a las complejidades del mundo real, el ser humano ha desarrollado la capacidad de generalizar, clasificar y generar abstracciones.

Así, tenemos un vocabulario donde muchos de los sustantivos representan clases de objetos.

Los objetos de cada clase comparten ciertos atributos o rasgos de comportamiento.

Por ejemplo, al tratarse algún tema relacionado con las aves no es necesario referirnos a alguna característica de una ave en particular sino a los atributos que comparten todas ellas.

Los lenguajes de Programación Orientada a Objetos pueden dividirse en dos grupos:

Page 92: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

El primero formado por los llamados lenguajes "puros" (llamados así debido a que sólo permiten programar con el paradigma de la Programación Orientada a Objetos) dentro del cual se encuentran SmallTalk y Actor, entre otros.

El segundo grupo lo forman los lenguajes que permiten, además de la Programación Orientada a Objetos, la programación procedimental, razón por la que se les llama "híbridos". A este grupo pertenece el lenguaje C++.

Para el manejo de los lenguajes de Programación Orientada a Objetos es necesario estudiar los conceptos de :

ENCAPSULAMIENTOHERENCIAPOLIMORFISMO

En las siguientes seciones se estudia detalladamente cada una de ellas.

8.2.- Encapsulamiento

El encapsulamiento consiste en poner juntos los datos y las funciones dentro de un objeto de tipo clase.

El modelo procedimental puede representarse como en la figura 8.1.

Figura 8.1.- Modelo procedimental.

donde se observa que los datos y el código se manejan como partes separadas. El programa ( código ) es alimentado con los datos para que produzca resultados.

Por otra parte, el modelo orientado a objetos puede representarse como en la figura 8.2.

Figura 8.2.- Modelo de la Programación Orientada a Objetos.

Page 93: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

donde DATOS y CODIGO se han unido para formar un OBJETO, el cual va a producir ciertos RESULTADOS, de acuerdo al MENSAJE que se le envíe. Esto es, un objeto de ciertas características va a comportarse de acuerdo a su clase al recibir un mensaje específico. Como se logra el ENCAPSULAMIENTO en el lenguaje C++ ?

Podemos pensar que, así como se requieren tipos para definir las variables en un enfoque procedimental, se requieren "tipos" o "moldes" para crear los objetos.

En el lenguaje C++ esos moldes se crean por medio de las palabras reservadas : class, struct y union .

La Geometría representa un campo idóneo para ejemplificar el manejo de objetos. Las definiciones para cada una de las figuras se hacen a través de enunciados que incluyen la expresión: " .. es el lugar geométrico de todos los puntos que ...". Como puede verse, el concepto de punto es la base para la definición de cualquier figura geométrica.

La representación de las figuras geométricas, en la pantalla de un monitor, se hace por medio de los pixeles, que son los elementos básicos ( puntos iluminados ) utilizados para representar los gráficos en la pantalla.

Con base a estos conceptos, en la siguiente sección vamos a crear una clase base llamada punto.

Los objetos de tipo punto podrán situarse en cualquier lugar de la pantalla utilizando un par de coordenadas ( x,y ).

8.2.1.- Definición de clases

La definición de la clase punto en C++ puede tomar la siguiente forma :

class punto { int x,y ; // MIEMBROS DATO } ;

Aquí no se está haciendo uso del encapsulamiento, puesto que no se ha declarado ninguna función miembro dentro de la clase punto.

8.2.3.- Construtores

Page 94: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Los constructores y los destructores son dos tipos especiales de funciones miembro.

Un constructor especifica la manera en que será creado e inicializado un nuevo objeto de cierta clase.

Los constructores en C++ pueden ser definidos por el usuario ó generados por el lenguaje.

El compilador de C++ invoca automáticamente al constructor apropiado cada vez que se defina un nuevo objeto.Esto puede ocurrir en una declaración de datos, cuando se copia un objeto o a través de la asignación dinámica de memoria a un nuevo objeto por medio del operador new.

Para añadir un constructor a la clase punto escribimos :

class punto { int x,y ; int dax() { return x ; } int day() { return y ; } punto(int nx, int ny) ; // DECLARACION DEL CONSTRUCTOR };

punto::punto(int nx, int ny) // DEFINICION DEL CONSTRUCTOR{ x = nx ; y = ny ;}

8.2.4.- Destructores

Los destructores destruyen a los objetos creados, liberando la memoria asignada. Pueden ser invocados explícitamente por medio del operador delete.

Siguiendo con el ejemplo de la clase punto :

class punto { int x,y ; int dax() { return x ; } int day() { return y ; } punto(int nx, int nx) ; ~punto() ; // DECLARACION DEL DESTRUCTOR };

punto::punto(int nx, int nx){ x = nx ; y = ny ;

Page 95: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

}

punto::~punto() // DEFINICION DEL DESTRUCTOR{ delete x ; delete y ;}

8.2.5.- Ambito de clase

El término ámbito se refiere al área dentro de un programa donde un identificador dado es accesible.Para controlar la manera en que son accesados los miembros de las clases, C++ ha introducido el concepto de ámbito de clase . Todos los miembros de una clase están en el ámbito de esa clase, de manera que cualquier miembro de una clase puede referenciar a cualquier otro miembro de la misma clase.

Las funciones miembro de una clase tienen acceso irrestricto a los miembros dato de esa misma clase. El acceso a los datos y funciones miembro de una clase exterior al ámbito de la clase es controlado por el programador a través de los especificadores de acceso.

8.2.5.1.- Especificadores de acceso

Para el control de acceso a los miembros de una clase, se cuenta con tres palabras clave : private: , protected: y public: .

La palabra clave apropiada ( incluyendo los dos puntos ) debe colocarse antes de las declaraciones que van a afectarse.

private: Los miembros que siguen pueden ser accesados solamente por las funciones miembro declaradas dentro de la misma clase.

protected: Los miembros que siguen pueden ser accesados por funciones miembro declaradas dentro de la misma clase, y por funciones miembro de clases derivadas de esta clase.

public: Los miembros que siguen pueden ser accesados desde cualquier lugar dentro del mismo ámbito de la definición de la clase.

Por predeterminación, los miembros de las clases de tipo class son private , los de las clases de tipo struct son public.

Page 96: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Por ejemplo, para hacer que las funciones miembro de la clase punto puedan ser accesadas desde cualquier lugar, escribimos :

class punto { int x, y ; // privadas POR PREDETERMINACION

public: // LO QUE SIGUE ES público int dax() { return x; } int day() { return y; } punto ( int nx, int ny ) ; ~punto(); }punto::punto(int nx, int ny){ x = nx ; y = ny ;}

punto::~punto(){ delete x ; delete y ;}

8.2.5.2.- Acceso privilegiado

Una clase puede darle a otra clase o función acceso privilegiado a sus áreas privadas. Tal acceso debe darse explícitamente, declarando la otra clase o función por medio del modificador friend (amigo). Los amigos son tratados como si fueran miembros de la clase y tienen acceso irrestricto a las áreas privadas de los objetos.

Por ejemplo :

class cosa { private: int dato ; public: friend void carga( cosa t , int x ) ; };

void carga ( cosa t , int x ){ t.dato = x ;}

La definición de la clase cosa contiene el prototipo para la función carga y la diseña como un amigo. La función carga no es un miembro de la clase cosa, sino que es una

Page 97: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

función independiente a la que se le ha dado acceso especial a los miembros privados de los objetos de tipo cosa.

Debido a que es un amigo de la clase cosa, carga puede accesar directamente los datos miembro privados de su parámetro t.

Una clase completa puede hacerse un amigo de otra, tal como en:

class perico ;

class paloma { public: friend class perico ; };

class perico { //CUALQUIER COSA };

En el listado 8.1 se muestra un ejemplo de utilización de la clase punto.

#include <iostream.h> class punto { int x,y ; public: punto(int nx, int ny) { x = nx ; y = ny ; } int dax() { return x ; } int day() { return y ; } }; void main() { int datox, datoy ; cout << "COORDENADA EN X : " ; cin>> datox ; cout << "COORDENADA EN y : " ; cin>> datoy ; punto objeto(datox , datoy) ;//INVOCA AL CONSTRUCTOR punto cout << "X=" << objeto.dax() ; cout << '\n' ; cout << " Y=" << objeto.day() ; cout << '\n' ; } </pre></B></I><h4>Listado 8.1.- Utilización de la clase punto.</h4><TABLE BORDER=0 WIDTH=" 99%">

Page 98: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

8.3.- Herencia

En la Programación Orientada a Objetos, la herencia es un concepto semejante al de la herencia genética que se dá en la naturaleza.

En el reino animal, por ejemplo, podemos hablar de una clase base llamada aves, cuyos miembros poseen:

- plumas - alas - pico

Sin embargo, no todas las aves son iguales, por lo que se tiene que recurrir a las clases derivadas para distinguir unas aves de otras, como se muestra en la figura 8.3.

Figura 8.3.- Jerarquía de clases a partir de la clase aves.

Cada una de las clases derivadas conserva las características de la clase base aves, pero posee algunas otras que la distingue de las demás clases.

Hasta aquí, hemos hablado solamente de clases de aves no de las aves en sí. No nos hemos referido a ninguna ave en particular, por lo que pudieramos decir que hemos estado hablando de los "tipos de aves".

Una vez que se tienen los tipos, podemos llevar a cabo la construcción de aves. Cuando tales aves hayan cumplido su misión, podremos destruirlas.

Esto último puede parecer demasiado cruel en el mundo animal, pero en el caso de la Programación Orientada a Objetos es lo más práctico, pues resultaría un gasto innecesario de memoria el conservar los objetos que ya no van a utilizarse .

Las clases, al ser consideradas como tipos, pueden utilizarse para construir bloques de

Page 99: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

programa. Si un tipo no cumple con las especificaciones de un nuevo programa, se puede utilizar para crear otro nuevo. Este nuevo tipo se modifica para usarse en la elaboración de nuevos objetos, y a su vez queda disponible para elaborar otros tipos, según se necesite.

Es en esta creación de nuevos tipos donde se llega a la utilización de los modelos jerárquicos de clasificación.

En C++, la herencia se maneja por medio de la derivación de clases.

8.3.1.- Derivación de clases

La derivación de clases en C++ se rige por la siguiente sintaxis :

class derivada : modificador_de_acceso base{ ..... .....}

modificador_de_acceso es opcional y puede ser public o private.

8.3.2.- Modificadores de acceso

Los modificadores de acceso se utilizan para cambiar la accesibilidad de los miembros heredados de acuerdo a lo que se observa en la tabla 8.1 :

EN CLASE BASE MODIF. DE ACCESO EN CLASE DERIVADAprivate private inaccesible

protected private privatepublic private privateprivate public inaccesible

protected public protectedpublic public public

Tabla 8.1.- Modificadores de acceso para las clases derivadas.

Cuando se utiliza la palabra class, el modificador de acceso está predefinido como private ; cuando se utiliza la palabra struct, está predefinido como public.

Page 100: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Al crear nuevas clases que dependen de clases existentes, debe asegurarse de que entiende la relación existente entre las clases base y derivada. Para esto es vital el entendimiento de los niveles de acceso conferidos por los especificadores private , protected y public .

El C++ permite pasar los derechos de acceso de padres a hijos sin exponer los datos a quienes no son miembros ni amigos de la familia.

El nivel de acceso de un miembro de una clase base, no es necesariamente el mismo cuando se maneja en el ámbito de la clase base que cuando se maneja en el ámbito de la clase derivada. El programador tiene el control sobre los niveles de acceso en todos los ámbitos.

Como puede observarse en la tabla 8.1, el especificador private convierte los miembros public y protected de la clase base a miembros private en la clase derivada. Los miembros con especificador private en la clase base, mantienen ese especificador en la clase derivada.

Aunque en las clases definidas por medio de la palabra class el nivel de acceso esta predeterminado como private, conviene explicitar los niveles de cada uno de los miembros de cada clase, para evitar confusiones.

Por lo tanto, puede escribirse :

class ejemplo { private: int num ; float costo ; public: void valida( char * ) ; int precio( float ) ; };

Una clase derivada hereda todos los miembros de su clase base, pero puede utilizar solamente los miembros protegidos y públicos de su clase base.

Los miembros privados de la clase base no están disponibles directamente a través de los miembros de la clase derivada.

Al usar la derivación public, los miembros protegidos de la clase base permanecen protegidos en la clase derivada; así que no estarán disponibles desde el exterior, excepto para otras clases derivadas públicamente y para los miembros y clases amigos.

8.3.3.- Manejo de clases en módulos

Page 101: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Las clases pueden empacarse en módulos para su utilización posterior en el desarrollo de programas.Una clase es inherentemente modular con sus miembros dato, funciones miembro y control de acceso. En el desarrollo de un programa, a veces tiene sentido poner las declaraciones, para cada clase o grupo relacionado de clases, por separado en un archivo de cabecera; y en otro las definiciones para las funciones miembro que no estén en línea.

En el listado 8.2 se presenta el archivo de cabecera PIXEL.H , donde, a partir de la clase base punto, se deriva otra clase llamada pixel que nos servirá para visualizar u ocultar puntos en cualquier lugar de la pantalla.

/////////////////////////////////////////////////////////// // PIXEL.H : CONTIENE LAS CLASES : // // // // punto QUE MANEJA LA LECTURA Y COLOCACION // // DE UN PUNTO. // // // // pixel MANEJA LA VISIBILIDAD DE UN PUNTO EN // // LA PANTALLA. // /////////////////////////////////////////////////////////// enum booleano { falso, verdadero } ; class punto { protected: //PERMITE QUE LAS CLASES DERIVADAS HEREDEN //DATOS privados. int x , y ; public: //LAS SIGS.FUNCIONES PUEDEN ACCESARSE DESDE //EL EXTERIOR. punto(int nx, int ny) ; int dax(); int day(); }; // CLASE DERIVADA DE punto. LA DERIVACION public SIGNIFICA // QUE x , y ESTARAN PROTEGIDAS DENTRO DE pixel. class pixel : public punto { protected: //LAS CLASES DERIVADAS DE pixel NECESITAN //ACCESO A ESTOS MIEMBROS DATO. booleano visible ; public: pixel(int nx, int ny); // CONSTRUCTOR void mostrar(); void ocultar();

Page 102: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

boleano es_visible(); void mover_hacia(int nuevax, int nuevay); };

Listado 8.2.- Archivo PIXEL.H

Nótese la utilización de protected en lugar de private para ciertos elementos, con lo que se hace posible la utilización del archivo PIXEL.H en aplicaciones que manejen clases derivadas de punto y pixel.

Las definiciones para las funciones miembro de las clases punto y pixel se almacenan en el archivo PUNTO2.CPP del listado 8.3.

///////////////////////////////////////////////////////////// // PUNTO2.CPP : CONTIENE LA DEFINICION PARA LAS FUNCIONES // // MIEMBRO DE LAS CLASES punto y pixel // ///////////////////////////////////////////////////////////// #include "pixel.h" #include <graphics.h> // DEFINICION DE LAS FUNCIONES MIEMBRO PARA LA CLASE punto punto::punto(int nx , int ny) { x = nx ; y = ny ; }; int punto::dax(void) { return x ; }; int punto::day(void) { return y ; }; // DEFINICION DE LAS FUNCIONES MIEMBRO PARA LA CLASE pixel pixel::pixel(int nx, int ny) : punto(nx,ny) { visible = falso ; }; void pixel::mostrar(void) { visible = verdadero ; putpixel(x,y, getcolor()); // USA EL COLOR PREDEFINIDO. // SUPONE QUE EN main() SE // INICIALIZAN LOS GRAFICOS. }; void pixel::ocultar(void)

Page 103: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

{ visible = falso ; putpixel(x,y,getbkcolor()); // USA EL COLOR DEL FONDO. };

booleano pixel::es_visible(void) { return visible ; }; void pixel::mover_hacia(int nuevax, int nuevay) { ocultar(); // HACE INVISIBLE AL PUNTO ACTUAL x = nuevax ; // CAMBIA LAS COORDENADAS x,y . y = nuevay ; mostrar(); // MUESTRA EL PUNTO EN LA NUEVA LOCALIDAD. };

Listado 8.3.- Definición de las clases punto y pixel.

Observe que, para definir el constructor de la clase derivada, se hace referencia al constructor de la clase base como en :

pixel::pixel(int nx,intny) : punto(nx,ny)Esto significa que, para construir un objeto de la clase pixel, primero se invoca al constructor de la clase base punto con los argumentos nx y ny creando e inicializando los miembros dato x,y. Posteriormente se invoca al constructor pixel, creando e inicializando el miembro dato visible.

Note que la referencia al constructor de la clase base aparece en la definición, no en la declaración, del constructor de la clase derivada.

Si no se ha definido un constructor para una clase x, el C++ generará un constructor predefinido de la forma : x::x();esto es, un constructor sin argumentos.

El listado 8.4 contiene un programa que muestra la utilización de la clase pixel . //////////////////////////////////////////////////////////// // PIXEL.CPP : APLICACION ELEMENTAL DE LA CLASE pixel // // // //////////////////////////////////////////////////////////// #include <graphics.h> #include <conio.h> #include "pixel.h" int main() { // INICIALIZACION DEL SISTEMA DE GRAFICOS int manej = DETECT, modo ; initgraph ( &manej, &modo, "c:\\borlandc\\bgi");

Page 104: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

// ENCIENDE, CAMBIA Y APAGA PIXELES pixel unpixel(100,50) ; // DEFINE unpixel CON VALORES // INICIALES x=100, y=50 unpixel.mostrar(); // unpixel SE ENCIENDE getch() ; // ESPERA UNA PULSACION DE TECLA unpixel.mover_hacia(300,150); // MUEVE unpixel A 300,150 getch(); unpixel.ocultar(); // OCULTA A unpixel getch(); closegraph(); return 0 ; }

Listado 8.4.- Utilización de la clase pixel.

Como podrá observarse, no se ha incluido el archivo PUNTO2.CPP, por lo que este programa deberá compilarse utilizando la opción PROJECT del Ambiente Integrado de Desarrollo ( IDE ) del C++ de Borland, creando un archivo de proyecto PIXEL.PRJ que incluya los archivos GRAPHICS.H, PUNTO2.CPP y PIXEL.CPP .

Uno de los atractivos de las clases es que pueden utilizarse para construir otras clases que respondan a las necesidades de una nueva aplicación.

En el ejemplo que se muestra en el listado 8.5 se hace uso de la clase pixel para derivar una nueva clase llamada circulo, la cual contendrá funciones miembro para mostrar, ocultar, mover, expander y contraer circulos. ///////////////////////////////////////////////////////////// // CIRCULO.CPP : MANEJO DE LA CLASE circulo DERIVADA DE LA // // CLASE pixel // ///////////////////////////////////////////////////////////// #include <graphics.h> #include <conio.h> #include "pixel.h" class circulo : pixel // DERIVADA PRIVADAMENTE DE pixel { int radio ; public: circulo(int nx, int ny, int nradio); void mostrar(void); void ocultar(void); void expander(int cantexp); void contraer(int cantcont); void mover_hacia(int nvax, int nvay); }; circulo::circulo(int nx,int ny,int nradio) : pixel(nx,ny) { radio = nradio;

Page 105: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

}; void circulo::mostrar(void) { visible = verdadero; circle(x,y,radio); // DIBUJA EL CIRCULO }; void circulo::ocultar(void) { unsigned int colortemp ; // PARA GUARDA EL COLOR ACTUAL colortemp = getcolor(); // GUARDA EL COLORR ACTUAL setcolor(getbkcolor()); // CAMBIA AL COLOR DEL FONDO visible = falso ; circle(x,y,radio); // DIBUJA EL CIRCULO CON EL COLOR // DEL FONDO PARA BORRARLO setcolor(colortemp); // RESTABLECE EL COLOR ORIGINAL };

void circulo::expander(int cantexp) { ocultar(); // BORRA EL CIRCULO ACTUAL radio += cantexp; // EXPANDE EL RADIO if( radio <0 ) // EVITA RADIOS NEGATIVOS radio="0" ; mostrar(); // DIBUJA EL NUEVO CIRCULO }; void circulo::contraer(int cantcontr) { expander(-cantcontr); }; void circulo::mover_hacia(int nvax, int nvay) { ocultar(); // BORRA EL CIRCULO ACTUAL x="nvax" ; // ASIGNA NUEVAS COORDENADAS y="nvay" ; mostrar(); // DIBUJA EL CIRCULO EN LA NUEVA POSICION }; void main() { int manej="DETECT" , modo ; initgraph(&manej, &modo, "c:\\borlandc\\bgi"); circulo uncirculo(100,200,50); uncirculo.mostrar(); getch(); uncirculo.mover_hacia(200,250); getch(); uncirculo.expander(50); getch(); uncirculo.contraer(75); getch(); closegraph(); }

Listado 8.5.- Utilización de la clase círculo

8.3.4.- Herencia múltiple

Todos los lenguajes de Programación Orientada a Objetos manejan la herencia en la forma que hemos visto hasta aquí, donde cada clase derivada tiene sólo una clase base. Sin embargo, algunos de ellos permiten manejar lo que se llama herencia múltiple, en la cual se permite que una clase derivada pueda tener varias clases base.

El mecanismo de herencia múltiple fué una de las principales características añadidas a la versión 2.0 de C++.

Como un ejemplo del manejo de la herencia múltiple, consideremos el problema de tener que desplegar texto dentro de un círculo.

Al principio podemos estar tentados a añadir una cadena de caracteres como

Page 106: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

miembro dato a la clase circulo y después agregarle código a circulo::mostrar para que despliegue el circulo con texto adentro. Pero el texto y el círculo son dos cosas diferentes. Cuando se piensa en texto, se piensa en tamaños y tipos de caracteres y en otros atributos que nada tienen que ver con los círculos.

Sin embargo, podemos derivar una nueva clase directamente de la clase circulo y de otra clase que permita darle capacidad de manejar texto.

En la figura 8.4 se ilustra este enfoque, el cual se detalla en el programa MULTI.CPP del listado 8.6.

Figura 8.4.- Ejemplo de Herencia Múltiple.

//////////////////////////////////////////////// // MULTI.CPP : ILUSTRA LA HERENCIA MULTIPLE // //////////////////////////////////////////////// #include <graphics.h> #include <conio.h> #include <string.h> #include "pixel.h" class circulo : public pixel { protected:

Page 107: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

int radio; public: circulo(int nx, int ny, int nradio) ; void mostrar(void); }; class mensaje : public punto // DESPLIEGA UN MENSAJE { char *mens ; // MENSAJE A DESPLEGAR int fuente ; // FUENTE A UTILIZAR int campo ; // TAMAÑO DEL CAMPO PARA EL TEXTO public: mensaje( int mensx, int mensy, int mensf, int mensc, char *texto ); void mostrar(void); // MUESTRA EL MENSAJE }; class multi : circulo, mensaje // HEREDA DE AMBAS { // CLASES. public: multi(int mcircx, int mcircy, int mcircr, int fuente, char *mens); void mostrar(void); // MUESTRA CIRCULO CON MENSAJE };

// DEFINICION DE LAS FUNCIONES MIEMBRO PARA LA CLASE circulo circulo::circulo(int nx,int ny, int nradio) : pixel(nx,ny) { radio = nradio ; }; void circulo::mostrar(void) { visible = verdadero ; circle(x,y,radio) ; // DIBUJA EL CIRCULO }; // DEFINICION DE LAS FUNCIONES MIEMBRO PARA LA CLASE mensaje mensaje::mensaje(int mensx, int mensy, int mensf, int mensc, char *texto ) : punto(mensx,mensy) { fuente = mensf ; campo = mensc ; mens = texto ; // APUNTA A MENSAJE }; void mensaje::mostrar(void) { int tam = campo / ( 8 * strlen(mens)) ; // 8 PIXELES POR // CARACTER settextjustify(CENTER_TEXT, CENTER_TEXT);// CENTRA TEXTO settextstyle(fuente, HORIZ_DIR, tam) ; //AGRANDA SI tam >1 outtextxy(x,y, mens) ; // DESPLIEGA MENSAJE

Page 108: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

}; // DEFINICION DE LAS FUNCIONES MIEMBRO PARA LA CLASE multi multi::multi(int mcircx, int mcircy, int mcircr, int fuente, char *mens) : circulo(mcircx, mcircy, mcircr) , mensaje(mcircx, mcircy, fuente, 2*mcircr, mens) { }; void multi::mostrar(void) { circulo::mostrar(); mensaje::mostrar(); };

// DEFINICION DE LA FUNCION PRINCIPAL void main() { int manej = DETECT, modo ; initgraph(&manej, &modo, "c:\\borlandc\\bgi"); multi uno(250,100,25,SANS_SERIF_FONT,"Tu"); uno.mostrar(); multi dos(250,150,100,TRIPLEX_FONT,"Tierra"); dos.mostrar(); multi tres(250,250,225,GOTHIC_FONT,"Universo"); tres.mostrar(); getch(); closegraph(); }

Listado 8.6.- Ejemplo de herencia múltiple.

8.4.- Poliformismo

Las funciones miembro son las que determinan el comportamiento de un objeto. Esto conlleva la idea de que, si queremos que un objeto se comporte de manera diferente, primero habrá que modificar la clase o "molde", agregándole la función miembro que determina el comportamiento deseado, para, posteriormente, crear un objeto de la clase modificada.

En los ejemplos mostrados hasta aquí, todos los objetos tienen la habilidad necesaria para mostrarse en la pantalla. Sin embargo, cada tipo de objeto difiere de los demás en la forma de mostrarse.

Por ejemplo, un pixel puede mostrarse con sólo indicarle las coordenadas x,y correspondientes a su posición de la pantalla. En cambio, un círculo requiere de la

Page 109: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

medida del radio además de las coordenadas x,y de su centro.

Esto nos lleva a pensar que para otros objetos tales como: lineas, rectangulos, elipses, etc. , pueden derivarse nuevas clases tomando como base la clase pixel y en las cuales se conservan los miembros dato x,y además de las funciones miembro dax() y day() definidos en la clase punto ; pero deben definirse nuevas funciones miembro mostrar(), así que, cada vez que se defina una nueva clase derivada de pixel, debe compilarse un nuevo proyecto ( archivo .PRJ ), donde se incluyan los archivos PUNTO2.CPP junto con el archivo correspondiente a la nueva figura ( por ejemplo LINEA.CPP ) para obtener el correspondiente archivo ejecutable ( por ejemplo LINEA.EXE ).

Ya sea que hubieramos escrito las funciones mostrar() de todas las clases de figuras en un solo archivo fuente, o que las hubieramos manejado en varios archivos de un proyecto; habríamos tenido que volver a compilar cada vez que una nueva clase hubiera sido añadida.

Suponiendo que ya se tienen las funciones mostrar() para cada uno de los objetos involucrados, ahora surge la pregunta:

¿ A cuál de las funciónes mostrar() se está haciendo referencia ? Para responder a esto, con los mecanismos del C++ estudiados hasta antes de esta unidad, se pueden utilizar las siguientes formas:

1.- La que realiza la distinción por medio del nombre ampliado. Por ejemplo, mostrar(int,char) no es lo mismo que mostrar(int,float).

2.- La que utiliza el operador de resolución de ámbito, por medio del cual circulo::mostrar() se distingue de pixel::mostrar() y de ::mostrar().

3.- La que distingue de acuerdo a la clase de objeto que se maneja. Por ejemplo: uncirculo.mostrar() invoca a circulo::mostrar(), mientras que unpixel.mostrar() invoca a pixel::mostrar().

Todas esas resoluciones de función se han hecho en tiempo de compilación, por medio del mecanismo conocido como enlace temprano o estático.

La alternativa en C++ se dá a través del polimorfismo, que consiste en la capacidad que posee el código para comportarse de diversas maneras. Cada forma de comportamiento dependerá de una situación específica, determinada en tiempo de ejecución, por medio del enlace tardío.

El enlace tardío o dinámico trabaja por medio de funciones miembro especiales llamadas funciones virtuales.

8.4.1.- Funciones virtuales

Page 110: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

El concepto clave en las funciones virtuales es que las invocaciones a ellas se resuelven en tiempo de ejecución (de ahí el término enlace tardío). En nuestro ejemplo, esto significa que la invocación a la función mostrar() adecuada va a realizarse hasta que se conozca el tipo de objeto involucrado. Consideremos la función miembro circulo::mover_hacia() definida en CIRCULO.CPP :

void circulo::mover_hacia(int nvax, int nvay){ ocultar(); x = nvax; y = nvay; mostrar(); }

Observamos esta definición es parecida a la de pixel::mover_hacia() que se encuentra en PUNTO2.CPP :

void pixel::mover_hacia(int nvax, int nvay){ ocultar(); x = nvax; y = nvay; mostrar(); }

El valor de retorno, el nombre de la función, el número y tipo de argumentos, y hasta el cuerpo de la función, son idénticos.

Si el C++ encuentra dos llamadas a función que tengan el mismo nombre de función pero diferentes nombres ampliados, el compilador resuelve las ambigüedades.

Pero nuestras dos funciones mover_hacia() , a simple vista, no ofrecen al compilador ninguna pista distintiva. ¿ Cómo va a saber el compilador a cuál función llamar ?. Cuando se trata de funciones miembro ordinarias, el compilador determina la función objetivo a partir de la clase del objeto involucrado en la llamada.

Entonces, ¿ porqué no dejar que circulo herede la función mover_hacia() desde pixel, tal como hereda las funciones miembro dax() y day(), desde la clase punto vía la clase pixel ?.

La razón para no hacerlo es que un pixel se mueve de manera diferente a como se mueve un círculo y las funciones ocultar() y mostrar() invocadas en circulo::mover_hacia() no son las mismas que se invocan en pixel::mover_hacia(). Heredar mover_hacia() desde pixel puede llevar a invocar a las funciones ocultar() y mostrar() erróneas cuando se intente mover un círculo.

La solución a este problema es declarar a las funciones ocultar() y mostrar() como

Page 111: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

funciones virtuales. Esto retardará el enlace para que se hagan las llamadas a las versiones correctas de esas funciones cuando se invoque a la función mover_hacia() ya sea para mover un pixel o para mover un círculo (o cualquier otra figura ).

8.4.1.1.- Declaración de funciones virtuales

Para declarar una función como virtual, simplemente hay que anteponer la palabra virtual en la primera declaración de la función miembro.

Por ejemplo :

virtual void mostrar();virtual void ocultar();

Es importante recordar que solamente las funciones miembro pueden ser declaradas como virtual , y que no deben redeclararse en las clases derivadas.

Si redeclaramos a la función miembro mostrar() con los mismos parámetros y el mismo tipo de retorno, en una clase derivada, la nueva función mostrar() automáticamente se vuelve virtual, ya sea que se utilice o no el calificador virtual. Se dice que la nueva función virtual mostrar() sobreescribe a la función mostrar() de la clase base.

Tenemos la libertad de redeclarar a la función mostrar() con parámetros diferentes ( cambiando o no el tipo de retorno ), pero entonces el mecanismo virtual será inoperante para esta versión de mostrar().

Al principio, deberá evitarse la sobrecarga temeraria, ya que hay situaciones en que una función no virtual puede ocultar a una función virtual en su clase base.

A manera de ejemplo, vamos a crear un módulo ( Por medio de los archivos FIGURAS.H y FIGURAS.CPP mostrados más adelante ) que define algunas clases de figuras y crea un mecanismo generalizado para arrastrarlas a través de la pantalla.

El principal objetivo, en el diseño del módulo, es permitir a los usuarios del mismo extender las clases definidas en él.

Al añadir una nueva función miembro a una jerarquía de clases existente, surge la siguiente reflexión : ¿ En qué lugar de la jerarquía debe ser colocada la nueva función miembro ?

La función arrastrar() debe ser un miembro de la clase pixel, para que todos los objetos de clases derivadas de pixel puedan arrastrase a través de la pantalla.

Page 112: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

Una vez decidido el lugar donde va a colocarse la función arrastrar(), vamos a rediseñar su definición.

Vamos a re-escribir la función arrastar() para que las funciones que invoca, tales como: dax(), day(), mostrar(), mover_hacia() y ocultar() , referencien las versiones correspondientes al tipo de objeto que va a ser arrastrado.

En la nueva definición de la clase pixel, del archivo FIGURAS.H mostrado en el listado 8.7, se hacen virtuales a las funciones miembro mostrar() , ocultar() y arrastrar().

//////////////////////////////////////////////////////// // FIGURAS.H : CONTIENE LA DEFINICION DE LAS CLASES // // punto, pixel y circulo // // EN UN SISTEMA A COMERCIALIZAR, ESTA SERIA LA // // UNICA PARTE QUE DEBE DISTRIBUIRSE EN FORMA DE // // CODIGO FUENTE. // //////////////////////////////////////////////////////// enum booleano { falso, verdadero } ; class punto { protected: int x, y ; public: punto(int nx, int ny) { x = nx ; y = ny ; } int dax() { return x ; } int day() { return y ; } };

class pixel : public punto { protected: booleano visible ; public: pixel(int nx, int ny); virtual void mostrar(); virtual void ocultar(); virtual void arrastrar(int narrastre); booleano es_visible() { return visible ; } void mover_hacia(int nvax, int nvay); };

class circulo : public pixel { protected: int radio; public: circulo(int nx, int ny, int nradio); void mostrar(); void ocultar(); void expander(int cantexp); void contraer(int cantcont); }; // PROTOTIPO PARA LA FUNCION NO MIEMBRO leedelta()

Page 113: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

// DEFINIDA EN EL LISTADO 8.8 booleano leedelta(int& deltax, int& deltay);

Listado 8.7.- Archivo de cabecera para las clases punto,pixel y circulo.

El archivo FIGURAS.CPP, mostrado en el listado 8.8, contiene las definiciones para las funciones miembro declaradas en el listado 8.7.

//////////////////////////////////////////////////////////// // FIGURAS.CPP : CONTIENE LAS DEFINICIONES PARA LAS // // FUNCIONES DE LAS CLASES DECLARADAS EN FIGURAS.H // // // // EN UN CONTEXTO COMERCIAL, ESTE ARCHIVO PUEDE DISTRI- // // BUIRSE EN FORMA DE OBJETO ( .OBJ ). // //////////////////////////////////////////////////////////// #include <graphics.h> #include <conio.h> #include "figuras.h"

// DEFINICION DE LAS FUNCIONES MIEMBRO PARA LA CLASE pixel

pixel::pixel(int nx, int ny) : punto(nx,ny) { visible = falso ; } void pixel::mostrar() { visible = verdadero; putpixel(x,y,setcolor()); }

void pixel::ocultar() { visible = falso; putpixel(x,y, getbkcolor()); }

void pixel::mover_hacia(int nvax, int nvay) { ocultar(); x = nvax ; y = nvay ; mostrar(); }

void pixel::arrastrar(int narrastre) { int deltax, deltay ; int figx, figy ; mostrar() ; // DESPLIEGA LA FIGURA A SER ARRASTRADA figx = dax(); figy = day();

// ESTE ES EL CICLO DE ARRASTRE while(leedelta(deltax,deltay)) {

Page 114: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

figx += (deltax * narrastre) ; figy += (deltay * narrastre) ; mover_hacia(figx, figy) ; }; } // LA SIGUIENTE ES UNA FUNCION DE PROPOSITOS GENERALES QUE // UTILIZA LAS TECLAS DE FLECHAS PARA MOVER EL CURSOR.

booleano leedelta(int& deltax, int& deltay) { char car; booleano salir; deltax = 0 ; deltay = 0 ; do{ car = getch() ; // LEE LA TECLA if(car == 13) // RETORNO DE CARRO return falso ; if(car == 0 ) // CODIGO EXTENDIDO { salir = verdadero ; // ASUME QUE ES UTILIZABLE car = getch() ; // TOMA EL RESTO DEL CODIGO switch(car) { case 72 : deltay = -1 ; break ; // ARRIBA case 80 : deltay = 1 ; break ; // ABAJO case 75 : deltax = -1 ; break ; // IZQ. case 77 : deltax = 1 ; break ; // DER. default : salir = falso ; // OTRA }; } } while( !salir ) ; return verdadero; }

// DEFINICION DE LAS FUNCIONES MIEMBRO PARA LA CLASE circulo circulo::circulo(int nx, int ny, int nradio) : pixel(nx, ny) { radio = nradio; } void circulo::mostrar() { visible = verdadero; circle(x,y,radio); }

void circulo::ocultar() { unsigned int colortemp; colortemp = getcolor(); setcolor(getbkcolor()); visible = falso; circle(x,y, radio); setcolor(colortemp); } void circulo::expander(int cantexp)

Page 115: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

{ ocultar(); radio += cantexp; if(radio <0) radio="0;" mostrar(); } void circulo::contraer(int cantcont) { expander(-cantcont); }

Listado 8.8.- Definiciones de las funciones para las clases punto, pixel y circulo.

Ahora vamos a probar las nuevas clases punto, pixel y circulo utilizándolas para derivar una clase llamada arco que se encuentra definida en el archivo POLIMORF.CPP del listado 8.9.

//////////////////////////////////////////////////////////// // POLIMORF.CPP : CONTIENE LA DEFINICION DE LA CLASE arco // // QUE SE DERIVA DE circulo. // // Para compilar, forme un proyecto con FIGURAS.OBJ y // // POLIMOFIRSMO // ////////////////////////////////////////////////////////////

#include <graphics.h> #include <conio.h> #include "figuras.h"

class arco : public circulo { int anginic, angfin ;

public: arco(int nx, int ny, int nradio, int nanginic, int nangfin) : circulo(nx, ny, nradio) { anginic = nanginic; angfin = nangfin; } void mostrar() ; // ESTAS FUNCIONES SON VIRTUALES EN // pixel void ocultar() ; }; // DEFINICION DE LAS FUNCIONES MIEMBRO PARA LA CLASE arco

void arco::mostrar() { visible = verdadero ; arc( x, y, anginic, angfin, radio ) ; }

void arco::ocultar() { int colortemp; colortemp = getcolor(); setcolor(getbkcolor()); visible = falso ; arc(x, y, anginic, angfin, radio); setcolor(colortemp);

Page 116: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

}

// DEFINICION DE LA FUNCION PRINCIPAL void main() { int manej =DETECT, modo ; initgraph( &manej, &modo, "c:\\borlandc\\bgi"); circulo uncirculo(151,82,50); arco unarco(151,82,25,0,190); unarco.arrastrar(10) ; // LA FUNCION arrastar() SE APLICA // A UNA FIGURA QUE NO CONOCE. unarco.ocultar(); uncirculo.arrastrar(10); closegraph(); getch(); }

Listado 8.9.- Definición y uso de la clase arco.

8.5.- Objetos dinámicos

En los ejemplos mostrados anteriormente se utilizaron objetos cuyo espacio de almacenamiento se manejó en la pila.

En esta sección trataremos con objetos dinámicos, cuyo espacio de almacenamiento en el montículo se asigna y se libera en tiempo de ejecución por medio de los operadores new y delete. Por ejemplo, si existe una clase llamada punto, podemos escribir:

punto* ap ; ap = new punto(10,20);

En la primera línea se está declarando un apuntador llamado ap, que servirá para apuntar a objetos de clase punto .

En la segunda línea se gestiona un bloque de memoria lo suficientemente grande para almacenar un objeto de la clase punto, se invoca al constructor punto pasándole los argumentos 10,20 . La dirección de inicio del bloque gestionado se asigna al apuntador ap.

Las dos líneas anteriores pueden sustituirse por:

punto* ap = new punto(10,20);

En el listado 8.10 se presenta un ejemplo del manejo de dos objetos dinámicos, donde, además, se utiliza el polimorfismo.

Page 117: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

En 8.10 puede observarse que se declara el apuntador ap para que apunte a objetos de clase numero y, sin embargo, ese mismo apuntador se utiliza para apuntar a objetos de clase division.

Esto se debe a que, en C++, un apuntador declarado para apuntar a objetos de una clase base puede utilizarse para apuntar a objetos de una clase derivada.

Esta regla no es válida en el sentido inverso; esto es, un apuntador declarado para apuntar a objetos de una clase derivada no puede utilizarse para apuntar a objetos de la clase base, por lo que:

division* ap; ap = new numero(10);

produce un error.

/////////////////////////////////////////////////////// // OBJDIN.CPP : Muestra el uso de objetos dinámicos. // /////////////////////////////////////////////////////// #include <iostream.h> class numero { protected: int valor; public: numero(int numdado) { valor = numdado;} virtual int davalor(){return valor;} }; class division : public numero { protected: int divisor; public: division(int d, int n) : numero(d) {divisor=n;} virtual int davalor() {return valor/divisor;} }; void main() { numero* ap; ap = new numero(15); cout << ap->davalor() << '\n'; //Despliega 15 delete ap; ap="new" division(15,2); cout << ap->davalor() << '\n'; // Despliega 7 }

Listado 8.10.- Ejemplo de utilización de objetos dinámicos.

9.- Archivos

Page 118: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

En C++, el manejo de archivos se realiza a través de los flujos de datos ( streams ).

La elegancia y simplicidad de los flujos de datos está disponible también para los archivos creados por el usuario, tanto en modo texto como en modo binario. Hay varias clases en la libreria que tratan con archivos, pero las clases más usadas son ifstream para lectura y ofstream para escritura de archivos.

Las clases para los flujos de datos en archivos tienen funciones para abrir y cerrar archivos, leer y escribir líneas de texto, etc.

1.- Archivos de texto

Un archivo de texto es una secuencia de caracteres ASCII divididos en líneas, cuyo último carácter es el de nueva línea ( \n ).

Debido a la sencillez en su manejo, la mayoría de los programas utilizan archivos de texto.

En el listado 9.1 se muestra un programa que abre un archivo de texto en el modo de lectura, checa la posibilidad de error en la apertura, y despliega el contenido del archivo.

////////////////////////////////////////////////// // LARCHTEX.CPP : Lee un archivo de texto // ////////////////////////////////////////////////// #include <iostream.h> #include <fstream.h> void main() { // Abre un archivo que supuestamente existe ifstream entrada("LARCHTEX.CPP"); // Verifica la existencia del archivo if(!entrada)

{ cout << "\nNo puede abrirse el archivo"; return; } //

Despliega el contenido del archivo char maux[100]; while(entrada) { entrada.getline(maux,sizeof(maux)); cout << "\n" << maux; } }

Listado 9.1.- Lectura de un archivo de texto existente.

Page 119: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

En el listado 9.2 se muestra la manera en que un archivo de texto existente es copiado a un archivo existente o de nueva creación.

///////////////////////////////////////////////////// // GARCHTEX.CPP : Graba un archivo de texto. // ///////////////////////////////////////////////////// #include <iostream.h> #include <fstream.h> void main() { // Abre un archivo en el modo de lectura ifstream entrada("GARCHTEX.CPP"); // Verifica la existencia del archivo a leer if(!entrada)

{ cout << "\nNo puede abrir el archivo GARCHTEX.CPP\n"; return;

} // Abre otro archivo en modo de escritura ofstream salida("GARCHTEX.BAK"); // Verifica la posibilidad error al abrir el archivo // de escritura if(!salida) { cout << "\No puede abrir el archivo GARCHTEX.BAK\n"; return; } // Copia el archivo GARCHTEX.CPP en GARCHTEX.BAK int cnt="0;" char maux[80]; while(entrada && salida) { cnt++; entrada.getline(maux,sizeof(maux)); salida << maux << "\n"; } cout << "\n" << cnt << " líneas copiadas\n"; }

Listado 9.2.- Escritura en un archivo en modo texto.

9.2.- Archivos binarios

Los archivos binarios son diferentes a los archivos de texto principalmente porque los datos no estan organizado en líneas con terminadores de nueva línea, y porque pueden contener cualquier valor de ocho bits. Sin embargo, los archivos binarios se abren en la misma forma que los archivos de texto, excepto por la bandera ios::binary que se pasa a la función de apertura, como se muestra en los listados 9.3 y 9.4.

///////////////////////////////////////////////////// // GARCHBIN.CPP : GRABA UN ARCHIVO EN MODO BINARIO // /////////////////////////////////////////////////////

#include <iostream.h> #include <fstream.h>

struct empleado { int numero; char nombre[31]; float sueldo; };

Page 120: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

void main() { ofstream salida("EMPRESA.DAT", ios::binary); if(!salida) {

cout << "\nNo puede abrirse el archivo EMPRESA.DAT\n"; return; } empleado unempleado; do{ cout << "Número ( 0="Fin" ) : "; cin>> unempleado.numero; cin.ignore();

if(!unempleado.numero) return; cout << "Nombre : "; cin.getline(unempleado.nombre,31); cout

<< "Sueldo : "; cin>> unempleado.sueldo; cin.ignore(); salida.write((char*) &unempleado,sizeof(unempleado));

}while(unempleado.numero != 0); }

Listado 9.3.- Escritura de un archivo en modo binario.

///////////////////////////////////////////////////// // LARCHBIN.CPP : Lee un archivo en modo binario ///////////////////////////////////////////////////////

#include <iostream.h> #include <fstream.h> #include <iomanip.h>

struct empleado { int numero; char nombre[31]; float sueldo; };

void main() { ifstream entrada("EMPRESA.DAT", ios::binary); if(!entrada) {

cout << "\nNo puede abrirse el archivo EMPRESA.DAT\n"; return; } empleado unemp; cout << setiosflags(ios::showpoint | ios::fixed) << setprecision(2) << '\n'; cout << setiosflags(ios::left) << setw(6) << "NUM." << setw(35) << "N O M B R E" << setw(10) << "SUELDO" << "\n="==========================" << setfill('.'); while(entrada) { if(entrada.read((char*) &unemp,sizeof(unemp))) { cout << setiosflags(ios::left) << setw(6) << unemp.numero << setw(35) << unemp.nombre << setiosflags(ios::right) << " $ " << setw(10) << unemp.sueldo << '\n'; } } }

Listado 9.4.- Lectura de un archivo en modo binario.

También es posible abrir un archivo de texto en modo binario, como se muestra en el listado 9.5.

////////////////////////////////////////////////////////////

Page 121: triunfadorsucre2010.files.wordpress.com · Web viewLos parámetros de las funciones. c).- Los identificadores de ámbito de b loque para objetos declarados sin el el especificador

// LARCHBI2.CPP : Lee un archivo de texto en modo binario // //////////////////////////////////////////////////////////// #include <iostream.h> #include <fstream.h> void main() { // Abre un archivo en modo binario ifstream entrada("LARCHBI2.CPP", ios::binary); // Verifica la existencia del archivo if(!entrada)

{ cout << "\nNo puede abrirse el archivo"; return; } // Despliega

el contenido del archivo char c; while(entrada) { entrada.get(c); cout << c ; } }

Listado 9.5.- Lectura de un archivo de texto en modo binario.