Está en la página 1de 60

Departamento de F sica, Facultad de Ciencias, Universidad de Chile. n Las Palmeras 3425, Nuoa.

. Casilla 653, Correo 1, Santiago fono: 562 678 7276 fax: 562 271 2973 e-mail: secretaria@sica.ciencias.uchile.cl

Apuntes de un curso de

F ISICA MATEMATICA

Jos Rogan C. e V ctor Muoz G. n

Indice
1 Elementos del sistema operativo unix. 1.1 Introduccin. . . . . . . . . . . . . . . . . . . . . o 1.2 Ingresando al sistema. . . . . . . . . . . . . . . . 1.2.1 Terminales. . . . . . . . . . . . . . . . . . 1.2.2 Login. . . . . . . . . . . . . . . . . . . . . 1.2.3 Passwords. . . . . . . . . . . . . . . . . . . 1.2.4 Cerrando la sesin. . . . . . . . . . . . . . o 1.3 Archivos y directorios. . . . . . . . . . . . . . . . 1.4 Ordenes bsicas. . . . . . . . . . . . . . . . . . . a 1.4.1 Archivos y directorios. . . . . . . . . . . . 1.4.2 Ordenes relacionadas con directorios. . . . 1.4.3 Visitando archivos. . . . . . . . . . . . . . 1.4.4 Copiando, moviendo y borrando archivos. 1.4.5 Espacio de disco. . . . . . . . . . . . . . . 1.4.6 Links. . . . . . . . . . . . . . . . . . . . . 1.4.7 Proteccin de archivos. . . . . . . . . . . . o 1.4.8 Filtros. . . . . . . . . . . . . . . . . . . . . 1.4.9 Otros usuarios y mquinas . . . . . . . . . a 1.4.10 Fecha . . . . . . . . . . . . . . . . . . . . 1.4.11 Transferencia a diskettes. . . . . . . . . . . 1.4.12 Diferencias entre los sistemas. . . . . . . . 1.5 Shells. . . . . . . . . . . . . . . . . . . . . . . . . 1.5.1 Variables de entorno. . . . . . . . . . . . 1.5.2 Redireccin. . . . . . . . . . . . . . . . . . o 1.5.3 Ejecucin de comandos. . . . . . . . . . . o 1.5.4 Aliases. . . . . . . . . . . . . . . . . . . . 1.5.5 Las shells csh y tcsh. . . . . . . . . . . . . 1.5.6 Las shell sh y bash. . . . . . . . . . . . . . 1.6 Ayuda y documentacin. . . . . . . . . . . . . . . o 1.7 Procesos. . . . . . . . . . . . . . . . . . . . . . . . 1.8 Editores. . . . . . . . . . . . . . . . . . . . . . . . 1.8.1 El editor vi. . . . . . . . . . . . . . . . . . 1.8.2 Editores modo emacs. . . . . . . . . . . . 1.9 El sistema X Windows. . . . . . . . . . . . . . . . 1.10 Uso del ratn. . . . . . . . . . . . . . . . . . . . . o iii 3 3 4 4 5 5 6 6 7 8 9 10 10 10 11 11 14 17 18 18 18 19 20 21 21 22 22 24 25 26 27 28 30 36 37

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

iv 1.11 Internet. . . . . . . . . . . . 1.11.1 Acceso a la red. . . . 1.11.2 El correo electrnico. o 1.11.3 Ftp anonymous. . . . 1.11.4 WWW. . . . . . . . 1.12 Impresin. . . . . . . . . . . o 1.13 Compresin. . . . . . . . . . o 1.14 Compilacin y debugging. . o 1.14.1 Compiladores. . . . . 1.15 make & Makele. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

INDICE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 38 40 41 41 42 42 43 43 44 49 49 49 50 51 52 54 54 54 55 56 57 57 59 60 60 63 63 63 64 64 64 65 69 70 72 74 75 76 78 78 78 79 80

2 Una breve introduccin a C++. o 2.1 Estructura bsica de un programa en C++. . . . . . . . a 2.1.1 El programa ms simple. . . . . . . . . . . . . . . a 2.1.2 Denicin de funciones. . . . . . . . . . . . . . . o 2.1.3 Nombres de variables. . . . . . . . . . . . . . . . 2.1.4 Tipos de variables. . . . . . . . . . . . . . . . . . 2.1.5 Ingreso de datos desde el teclado. . . . . . . . . . 2.1.6 Operadores aritmticos. . . . . . . . . . . . . . . e 2.1.7 Operadores relacionales. . . . . . . . . . . . . . . 2.1.8 Asignaciones. . . . . . . . . . . . . . . . . . . . . 2.1.9 Conversin de tipos. . . . . . . . . . . . . . . . . o 2.2 Control de ujo. . . . . . . . . . . . . . . . . . . . . . . . 2.2.1 if, if... else, if... else if. . . . . . . . . 2.2.2 Expresin condicional. . . . . . . . . . . . . . . . o 2.2.3 switch. . . . . . . . . . . . . . . . . . . . . . . . 2.2.4 for. . . . . . . . . . . . . . . . . . . . . . . . . . 2.2.5 while. . . . . . . . . . . . . . . . . . . . . . . . . 2.2.6 do... while. . . . . . . . . . . . . . . . . . . . 2.2.7 goto. . . . . . . . . . . . . . . . . . . . . . . . . 2.3 Funciones. . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3.1 Funciones tipo void. . . . . . . . . . . . . . . . . 2.3.2 return. . . . . . . . . . . . . . . . . . . . . . . . 2.3.3 Funciones con parmetros. . . . . . . . . . . . . . a 2.3.4 Parmetros por defecto. . . . . . . . . . . . . . . a 2.3.5 Ejemplos de funciones: ra cuadrada y factorial. z 2.3.6 Alcance, visibilidad, tiempo de vida. . . . . . . . 2.3.7 Recursin. . . . . . . . . . . . . . . . . . . . . . . o 2.3.8 Funciones internas. . . . . . . . . . . . . . . . . . 2.4 Punteros. . . . . . . . . . . . . . . . . . . . . . . . . . . 2.5 Matrices. . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.5.1 Declaracin e inicializacin. . . . . . . . . . . . . o o 2.5.2 Matrices como parmetros de funciones. . . . . . a 2.5.3 Asignacin dinmica. . . . . . . . . . . . . . . . . o a 2.5.4 Matrices multidimensionales. . . . . . . . . . . . .

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

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

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

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

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

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

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

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

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

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

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

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

INDICE 2.5.5 Matrices de caracteres: cadenas (strings). Manejo de archivos . . . . . . . . . . . . . . . . 2.6.1 Archivos de salida . . . . . . . . . . . . . 2.6.2 Archivos de entrada . . . . . . . . . . . . 2.6.3 Archivos de entrada y salida . . . . . . . 2.7 main como funcin . . . . . . . . . . . . . . . . o 2.8 Clases. . . . . . . . . . . . . . . . . . . . . . . . 2.8.1 Denicin. . . . . . . . . . . . . . . . . . o 2.8.2 Miembros. . . . . . . . . . . . . . . . . . 2.8.3 Miembros pblicos y privados. . . . . . . u 2.8.4 Operador de seleccin (.). . . . . . . . . o 2.8.5 Implementacin de funciones miembros. o 2.8.6 Constructor. . . . . . . . . . . . . . . . . 2.8.7 Destructor. . . . . . . . . . . . . . . . . 2.8.8 Matrices de clases. . . . . . . . . . . . . 2.9 Sobrecarga. . . . . . . . . . . . . . . . . . . . . 2.9.1 Sobrecarga de funciones. . . . . . . . . . 2.9.2 Sobrecarga de operadores. . . . . . . . . 2.9.3 Coercin. . . . . . . . . . . . . . . . . . o 2.10 Herencia. . . . . . . . . . . . . . . . . . . . . . . 2.6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

v 82 84 84 87 88 89 91 92 92 92 93 94 94 95 96 96 97 97 97 98 101 101 101 102 102 102 103 103 104 105 105 105 106 107 108 108 109 110 111 112 112 112 113 113

5 El sistema de preparacin de documentos TEX . o 5.1 Introduccin. . . . . . . . . . . . . . . . . . . . . o 5.2 Archivos. . . . . . . . . . . . . . . . . . . . . . . . 5.3 Input bsico. . . . . . . . . . . . . . . . . . . . . a 5.3.1 Estructura de un archivo. . . . . . . . . . 5.3.2 Caracteres. . . . . . . . . . . . . . . . . . 5.3.3 Comandos. . . . . . . . . . . . . . . . . . 5.3.4 Algunos conceptos de estilo. . . . . . . . . 5.3.5 Notas a pie de pgina. . . . . . . . . . . . a 5.3.6 Frmulas matemticas. . . . . . . . . . . . o a 5.3.7 Comentarios. . . . . . . . . . . . . . . . . 5.3.8 Estilo del documento. . . . . . . . . . . . . 5.3.9 Argumentos de comandos. . . . . . . . . . 5.3.10 T tulo. . . . . . . . . . . . . . . . . . . . . 5.3.11 Secciones. . . . . . . . . . . . . . . . . . . 5.3.12 Listas. . . . . . . . . . . . . . . . . . . . . 5.3.13 Tipos de letras. . . . . . . . . . . . . . . . 5.3.14 Acentos y s mbolos. . . . . . . . . . . . . . 5.3.15 Escritura de textos en castellano. . . . . . 5.4 Frmulas matemticas. . . . . . . . . . . . . . . . o a 5.4.1 Sub y supra ndices. . . . . . . . . . . . . . 5.4.2 Fracciones. . . . . . . . . . . . . . . . . . . 5.4.3 Ra ces. . . . . . . . . . . . . . . . . . . . . 5.4.4 Puntos suspensivos. . . . . . . . . . . . . .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

vi 5.4.5 Letras griegas. . . . . . . . . . . . . . 5.4.6 Letras caligrcas. . . . . . . . . . . a 5.4.7 S mbolos matemticos. . . . . . . . . a 5.4.8 Funciones tipo logaritmo. . . . . . . 5.4.9 Matrices. . . . . . . . . . . . . . . . . 5.4.10 Acentos. . . . . . . . . . . . . . . . . 5.4.11 Texto en modo matemtico. . . . . . a 5.4.12 Espaciado en modo matemtico. . . . a 5.4.13 Fonts. . . . . . . . . . . . . . . . . . Tablas. . . . . . . . . . . . . . . . . . . . . . Referencias cruzadas. . . . . . . . . . . . . . Texto centrado o alineado a un costado. . . Algunas herramientas importantes . . . . . . 5.8.1 babel . . . . . . . . . . . . . . . . . A 5.8.2 AMS-L TEX . . . . . . . . . . . . . . 5.8.3 fontenc . . . . . . . . . . . . . . . . 5.8.4 enumerate . . . . . . . . . . . . . . . 5.8.5 Color. . . . . . . . . . . . . . . . . . Modicando el estilo de la pgina. . . . . . . a 5.9.1 Estilos de pgina. . . . . . . . . . . . a 5.9.2 Corte de pginas y l a neas. . . . . . . Figuras. . . . . . . . . . . . . . . . . . . . . 5.10.1 graphicx.sty . . . . . . . . . . . . . 5.10.2 Ambiente figure. . . . . . . . . . . . Cartas. . . . . . . . . . . . . . . . . . . . . . A L TEX y el formato pdf. . . . . . . . . . . . . A Modicando L TEX. . . . . . . . . . . . . . . 5.13.1 Denicin de nuevos comandos. . . . o 5.13.2 Creacin de nuevos paquetes y clases o Errores y advertencias. . . . . . . . . . . . . 5.14.1 Errores. . . . . . . . . . . . . . . . . 5.14.2 Advertencias. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

INDICE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 114 114 116 117 119 119 120 120 121 121 122 122 123 124 126 127 127 128 128 129 131 132 133 134 137 138 138 143 150 150 153 147 147 147 149 150 151 151 152 154 154 156 157

5.5 5.6 5.7 5.8

5.9

5.10

5.11 5.12 5.13

5.14

10 EDO: Mtodos bsicos. e a 10.1 Movimiento de un proyectil. . . . . . . . . . . . . . . . . . . 10.1.1 Ecuaciones bsicas. . . . . . . . . . . . . . . . . . . . a 10.1.2 Derivada avanzada. . . . . . . . . . . . . . . . . . . . 10.1.3 Mtodo de Euler. . . . . . . . . . . . . . . . . . . . . e 10.1.4 Mtodos de Euler-Cromer y de Punto Medio. . . . . e 10.1.5 Errores locales, errores globales y eleccin del paso de o 10.1.6 Programa de la pelota de baseball. . . . . . . . . . . . 10.2 Pndulo simple. . . . . . . . . . . . . . . . . . . . . . . . . . e 10.2.1 Ecuaciones bsicas. . . . . . . . . . . . . . . . . . . . a 10.2.2 Frmulas para la derivada centrada. . . . . . . . . . . o 10.2.3 Mtodos del salto de la rana y de Verlet. . . . . . . e

. . . . . . . . . . . . . . . . . . . . . . . . . tiempo. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

INDICE 10.2.4 Programa de pndulo e 10.3 Listado de los programas. . 10.3.1 balle.cc . . . . . . 10.3.2 pendulo.cc . . . . . simple. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

vii 159 163 163 164

11 EDO II: Mtodos Avanzados. e 11.1 Orbitas de cometas. . . . . . . . . . . . . . . . . . 11.1.1 Ecuaciones bsicas. . . . . . . . . . . . . . a 11.1.2 Programa orbita. . . . . . . . . . . . . . 11.2 Mtodos de Runge-Kutta. . . . . . . . . . . . . . e 11.2.1 Runge-Kutta de segundo orden. . . . . . . 11.2.2 Frmulas generales de Runge-Kutta. . . . o 11.2.3 Runge-Kutta de cuarto orden. . . . . . . . 11.2.4 Pasando funciones a funciones. . . . . . . 11.3 Mtodos adaptativos . . . . . . . . . . . . . . . . e 11.3.1 Programas con paso de tiempo adaptativo. 11.3.2 Funcin adaptativa de Runge-Kutta. . . . o 11.4 Listados del programa. . . . . . . . . . . . . . . . 11.4.1 orbita.cc . . . . . . . . . . . . . . . . . .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

167 . 167 . 167 . 169 . 172 . 172 . 175 . 176 . 177 . 178 . 178 . 179 . 182 . 182

INDICE

Primer Curso

INTRODUCCION A LA F ISICA MATEMATICA

48

INDICE

Cap tulo 2 Una breve introduccin a C++. o


versin nal 3.5-19 de agosto de 2002 o

En este cap tulo se intentar dar los elementos bsicos del lenguaje de programacin a a o C++. No se pretende ms que satisfacer las m a nimas necesidades del curso, sirviendo como un ayuda de memoria de los tpicos abordados, para futura referencia. Se debe consignar o que no se consideran todas las posibilidades del lenguaje y las explicaciones estn reducidas a al m nimo.

2.1
2.1.1

Estructura bsica de un programa en C++. a


El programa ms simple. a

El primer ejemplo de todo manual es el que permite escribir Hola en la pantalla. // // Los comentarios comienzan con // // #include <iostream.h> main() { cout << "Hola." << endl; } Las tres primeras l neas corresponden a comentarios, todo lo que est a la derecha de a los caracteres // son comentarios y no sern considerados en la compilacin. En la l a o nea siguiente se incluye un archivo de cabecera, o header, con la instruccin de preprocesador o #include. El nombre del archivo se puede escribir como <nombre.h> o bien "nombre.h". En el primer caso el archivo nombre.h ser buscado en el path por defecto para los include, a t picamente /usr/include o /usr/include/g++-3/ en el caso de headers propios de C++; en el segundo caso la bsqueda se hace en el directorio local. Tambin podr u e amos incluir un path completo cuando se ocupan las comillas. En nuestro ejemplo se incluye el archivo iostream.h, en el cual se hacen las deniciones adecuadas para el manejo de la entrada y salida en C++. Este archivo es necesario para enviar luego un mensaje a pantalla. La funcin main es donde comienza a ejecutarse el programa; siempre debe haber una o funcin main en nuestro programa. Los parntesis vac () indican que el main no tiene o e os 49

50

CAP ITULO 2. UNA BREVE INTRODUCCION A C++.

argumentos de entrada (ms adelante se ver que puede tenerlos). Lo que est encerrado a a a entre llaves {} corresponde al cuerpo de la funcin main. Cada una de las l o neas termina con el carcter ;. El identicador predenido cout representa la salida a pantalla. El operador << a permite que lo que est a su derecha se le d salida por el dispositivo que est a su izquierda, a e a en este caso cout. Si se quiere enviar ms de un objeto al dispositivo que est al inicio de a a la l nea agregamos otro operador << y en este caso lo que est a la derecha del operador se a agregar a lo que est a la izquierda y todo junto ser enviado al dispositivo. En nuestro a a a caso se ha enviado endl, un objeto predenido en el archivo iostream.h que corresponde a un cambio de l nea, el cual ser agregado al nal del mensaje. a Si escribimos nuestro primer programa en el editor xemacs con el nombre de primero.cc las instruciones para editarlo, compilarlo y correrlo sern: a jrogan@pucon:~/tmp$ xemacs primero.cc jrogan@pucon:~/tmp$ g++ -o primero primero.cc jrogan@pucon:~/tmp$ ./primero Hola. jrogan@pucon:~/tmp$ Luego de la compilacin, un archivo ejecutable llamado primero es creado en el directoo rio actual. Si el directorio actual no est en el PATH, nuestro programa debe ser ejecutado a anteponiendo ./. Si est en el PATH, para ejecutarlo basta escribir primero. (Para agrea gar el directorio local al PATH basta editar el archivo ~/.bashrc agregarle una l nea como PATH="${PATH}:." y ejecutar en la l nea de comando source ~/.bashrc para que los cambios tengan efecto.)

2.1.2

Denicin de funciones. o

Las funciones en C++ son muy importantes, pues permiten aislar parte del cdigo en una o entidad separada. Esto es un primer paso a la modularizacin de nuestro programa, es decir, o a la posibilidad de escribirlo en partes que puedan ser editadas de modo lo ms indepena diente posible. Ello facilita enormemente la creacin de cdigo complicado, pues simplica o o su modicacin y la localizacin de errores. Nos encontraremos frecuentemente con este o o concepto. Aprovecharemos de introducir las funciones modicando el primer programa de manera que se delegue la impresin del mensaje anterior a una funcin independiente: o o // // Segunda version incluye funcion adicional // #include <iostream.h> void PrintHola() { cout << "Hola." << endl; }

2.1. ESTRUCTURA BASICA DE UN PROGRAMA EN C++. main() { PrintHola(); }

51

La funcin debe estar denida antes de que sea ocupada, por eso va primero en el cdigo o o fuente. Como ya se dijo antes la ejecucin del programa comienza en la funcin main a pesar o o de que no est primera en el cdigo fuente. Los parntesis vac indican que la funcin a o e os o PrintHola no tiene argumentos y la palabra delante del nombre de la funcin indica el tipo o de dato que devuelve. En nuestro caso la palabra void indica que no devuelve nada a la funcin main. o Una alternativa al cdigo anterior es la siguiente: o #include <iostream.h> void PrintHola(); main() { PrintHola(); } void PrintHola() { cout << "Hola." << endl; } En esta versin se ha separado la declaracin de la funcin de su implementacin. En o o o o la declaracin se establece el nombre de la funcin, los argumentos que recibe, y el tipo de o o variable que entrega como resultado. En la implementacin se da expl o citamente el cdigo o que corresponde a la funcin. Hab o amos dicho que una funcin debe estar denida antes que o sea ocupada. En verdad, basta conque la funcin est declarada. La implementacin puede o e o ir despus (como en el ejemplo anterior), o incluso en un archivo distinto, como veremos ms e a adelante. La separacin de declaracin e implementacin es otro paso hacia la modularizacin o o o o de nuestro programa.

2.1.3

Nombres de variables.

Nuestros datos en los programas sern almacenados en objetos llamados variables. Para a referirnos a ellas usamos un nombre que debe estar de acuerdo a las siguientes reglas: Deben comenzar con una letra (maysculas y minsculas son distintas). u u Pueden contener nmeros. u Pueden contener el s mbolo _ (underscore). Longitud arbitraria.

52

CAP ITULO 2. UNA BREVE INTRODUCCION A C++. No pueden corresponder a una de las palabras reservadas de C++1 : asm auto break case catch char class const continue default delete do double else enum extern float for friend goto if inline int long new operator private protected public register return short signed sizeof static struct switch template this throw try typedef union unsigned virtual void volatile while

2.1.4

Tipos de variables.

Todas las variables a usar deben ser declaradas de acuerdo a su tipo. Por ejemplo, si usamos una variable i que sea un nmero entero, debemos, antes de usarla, declararla, y slo entonces u o podemos asignarle un valor: int i; i=10; Esta necesidad de declarar cada variable a usar se relaciona con la caracter stica de C++ de ser fuertemente tipeado2 . Algunos de los errores ms habituales en programacin se deben a o al intento de asignar a variables valores que no corresponden a sus tipos originales. Si bien esto puede no ser muy grave en ciertos contextos, a medida que los programas se vuelven ms complejos puede convertirse en un verdadero problema. El compilador de C++ es capaz a de detectar los usos indebidos de las variables pues conoce sus tipos, y de este modo nuestro cdigo se vuelve ms seguro. o a Es posible reunir las acciones de declaracin e inicializacin en una misma l o o nea: int i=10; o declarar ms de una variable del mismo tipo simultneamente, e inicializar algunas en la a a misma l nea: int r1, r2, r3 = 10; A veces se requiere que una variable no var una vez que se le asigna un valor. Por ejemplo, e podr amos necesitar denir el valor de = 3.14159..., y naturalmente no nos gustar que, a por un descuido, a esa variable se le asignara otro valor en alguna parte del programa. Para asegurarnos de que ello no ocurra, basta agregar el modicador const a la variable: const float pi = 3.14159;
A esta tabla hay que agregar algunas palabras adicionales, presentes en versiones ms recientes de C++, a como namespace y using 2 Una traduccin libre del trmino ingls strongly typed. o e e
1

2.1. ESTRUCTURA BASICA DE UN PROGRAMA EN C++.

53

Para nmeros reales se puede usar la notacin exponencial. Por ejemplo, 1.5e-3 repreu o senta el nmero 1.5 103 . u Una variable puede ser declarada slo una vez, pero naturalmente se le pueden asignar o valores en un nmero arbitrario de ocasiones. u Los tipos de variables disponibles son3 : int short int short long int long unsigned int unsigned short unsigned long char float Enteros entre 215 = 32768 y 215 1 = 32767 o entre 231 = 2147483648 y 231 1 = 2147483647 Enteros entre 215 y 215 1. Enteros entre 231 y 231 1. Enteros entre 0 y 216 1 o entre 0 y 232 1. Enteros entre 0 y 216 1. Enteros entre 0 y 232 1. Caracteres. Reales en los intervalos [1.7 1038 , 0.29 1038 ], [0.29 1038 , 1.7 1038 ] (Precisin de unos 7 d o gitos decimales.) Reales en los mismos intervalos que float, pero con precisin de 16 decimales, o o en los intervalos [0.9 10308 , 0.86 10308 ] y [0.86 10308 , 0.9 10308 ], con precisin de 15 decimales. o

double

Las variables tipo char alojan caracteres, debiendo inicializarse en la forma: char c = a; Adems de las letras maysculas y minsculas, y s a u u mbolos como &, (, :, etc., hay una serie de caracteres especiales (escape codes) que es posible asignar a una variable char. Ellos son: newline horizontal tab vertical tab backspace carriage return form feed alert (bell) backslash single quote double quote \n \t \v \b \r \f \a \\ \ \"

Por ejemplo, la l nea: cout << "Primera columna\t Segunda columna\n


Los valores de los rangos indicados son simplemente representativos y dependen de la mquina utilizada. a Adems, estos valores no corresponden exactamente a las versiones ms recientes de C++. a a
3

54

CAP ITULO 2. UNA BREVE INTRODUCCION A C++. Segunda linea" << endl;

corresponde al output Primera columna Segunda linea Segunda columna

2.1.5

Ingreso de datos desde el teclado.

El header iostream.h dene un objeto especial llamado cin que est asociado al teclado o a stdin. Con el operador >> asignamos la entrada en el dispositivo de la izquierda a la variable de la derecha; una segunda entrada requiere de otro operador >> y de otra variable. En el siguiente ejemplo veremos una declaracin simultnea de dos variables del mismo tipo i y o a j, un mensaje a pantalla con las instrucciones a seguir, el ingreso de dos variables desde el teclado y luego su escritura en la pantalla. #include <iostream.h> main() { int i, j ; cout << "Ingrese dos numeros enteros: " ; cin >> i >> j ; cout << "Los dos numeros ingresados fueron: " << i <<" "<< j << endl ; }

2.1.6

Operadores aritmticos. e

Existen operadores binarios (i.e., que actan sobre dos variables, una a cada lado del operau dor) para la suma, la resta, la multiplicacin y la divisin: o o + * /

2.1.7

Operadores relacionales.

Los s mbolos para los operadores relacionales de igualdad, desigualdad, menor, menor o igual, mayor y mayor o igual son: == != < <= > >=

Para las relaciones lgicas AND, OR y NOT: o && || !

2.1. ESTRUCTURA BASICA DE UN PROGRAMA EN C++.

55

2.1.8

Asignaciones.

a) Asignacin simple. Podemos asignar a una variable un valor expl o cito, o el valor de otra variable: i = 1; j = k; Una prctica habitual en programacin es iterar porciones del cdigo. La iteracin a o o o puede estar determinada por una variable cuyo valor aumenta (disminuye) cada vez, hasta alcanzar cierto valor mximo (m a nimo), momento en el cual la iteracin se detiene. o Para que una variable x aumente su valor en 2, por ejemplo, basta escribir: x = x + 2; Si x fuera una variable matemtica normal, esta expresin no tendr sentido. Esta a o a expresin es posible porque el compilador interpreta a x de modo distinto a cada lado o del signo igual: a la derecha del signo igual se usa el valor contenido en la variable x (por ejemplo, 10); a la izquierda del signo igual se usa la direccin de memoria en la o cual est alojada la variable x. De este modo, la asignacin anterior tiene el efecto de a o colocar en la direccin de memoria que contiene a x, el valor que tiene x ms 2. En o a general, todas las variables tienen un rvalue y un lvalue: el primero es el valor usado a la derecha (right) del signo igual en una asignacin, y el segundo es el valor usado a la o izquierda (left) es decir, su direccin de memoria. o b) Asignacin compuesta. o La expresin o x = x + 2 se puede reemplazar por += -= *= /= x += 2. Existen los operadores

c) Operadores de incremento y decremento. La expresin o x = x + 1 se puede reescribir x += 1 o bien x++. Anlogamente, existe el operador --. Ambos operadores unarios, ++ y -- pueden a ocuparse como prejos o sujos sobre una variable y su accin diere en ambos casos. o Como prejo la operacin de incremento o decremento se aplica antes de que el valor o de la variable sea usado en la evaluacin de la expresin. Como sujo el valor de la o o variable es usado en la evaluacin de la expresin antes que la operacin de incremento o o o o decremento. Por ejemplo, supongamos que inicialmente x = 3. Entonces la instruccin o y=x++ hace que y = 3, x = 4; por su parte, y=++x hace que y = 4, x = 4. Con estas consideraciones, deber amos poder convencernos de que la salida del siguiente programa es 3 2 2-1 1 1 : // Ejemplo de operadores unarios ++ y --. #include <iostream.h> void main() {

56 int y ; int w = cout << w = x-cout << }

CAP ITULO 2. UNA BREVE INTRODUCCION A C++. int x = (y = 1) ; ++x + y++; w <<" " << x << " " << y << "-" ; - --y; w << " " << x << " " << y << endl ;

Los operadores para asignacin compuesta, y los de incremento y decremento, no son o slo abreviaciones. En realidad hay que preferirlas porque implican optimizaciones en el o ejecutable resultante.

2.1.9

Conversin de tipos. o

Una consecuencia de que C++ sea fuertemente tipeado es que no se pueden hacer operaciones binarias con objetos de tipos distintos. En la siguiente expresin, o int i = 3; float x = 43.8; cout << "Suma = " << x + i << endl; el computador debe sumar dos variables de tipos distintos, y en principio la operacin es o imposible. La estrategia para resolver este problema es convertir ambas variables a un tipo comn antes de efectuar la suma (en ingls, decimos que hacemos un cast de un tipo a otro. u e Existen dos modos de proceder: a) Conversin expl o cita. Si i es un int, por ejemplo, entonces float(i) la convierte en float. As el programa , anterior se puede reescribir: int i = 3; float x = 43.8; cout << "Suma = " << x + float(i) << endl; Ahora la suma es claramente entre dos variables float, y se puede realizar. Sin embargo, esto es bastante tedioso, por cuanto el programador debe realizar el trabajo de conversin personalmente cada vez que en su cdigo se desee sumar un real con un o o nmero entero. u b) Conversin impl o cita. En este caso, el compilador realiza las conversiones de modo automtico, preriendo a siempre la conversin desde un tipo de variable de menor precisin a uno de mayor o o precisin (de int a double, de short a int, etc.). As a pesar de lo que dijimos, el o , cdigo anterior habr funcionado en su forma original. Evidentemente esto es muy o a cmodo, porque no necesitamos hacer una conversin expl o o cita cada vez que sumamos un entero con un real. Sin embargo, debemos estar conscientes de que esta comodidad slo es posible porque ocurren varias cosas: primero, el compilador detecta el intento de o

2.2. CONTROL DE FLUJO.

57

operar sobre dos variables que no son del mismo tipo; segundo, el compilador detecta, en sus reglas internas, la posibilidad de cambiar uno de los tipos (int en este caso) al otro (float); tercero, el compilador realiza la conversin, y nalmente la operacin se o o puede llevar a cabo. Entender este proceso nos permitir aprovechar las posibilidades a de la convesin impl o cita de tipos cuando nuestro cdigo involucre tipos de variables o ms complicados, y entender varios mensajes de error del compilador. a Es interesante notar cmo las conversiones impl o citas de tipos pueden tener consecuencias insospechadas. Consideremos las tres expresiones: i) x = (1/2) * (x + a/x) ; ii) x = (0.5) * (x + a/x) ; iii) x = (x + a/x)/2 ; Si inicialmente x=0.5 y a=0.5, por ejemplo, i) entrega el valor x=0, mientras ii) y iii) entregan el valor x=1.5. Lo que ocurre es que 1 y 2 son enteros, de modo que 1/2 = 0. De acuerdo a lo que dijimos, uno esperar que en i), como conviven nmeros reales a u con enteros, los nmeros enteros fueran convertidos a reales y, por tanto, la expresin u o tuviera el resultado esperado, 1.5. El problema es la prioridad de las operaciones. No todas las operaciones tienen igual prioridad (las multiplicaciones y divisiones se realizan antes que las sumas y restas, por ejemplo), y esto permite al compilador decidir cul a operacin efectuar primero. Cuando se encuentra con operaciones de igual prioridad o (dos multiplicaciones, por ejemplo), se procede a efectuarlas de izquierda a derecha. Pues bien, en i), la primera operacin es 1/2, una divisin entre enteros, i.e. cero. En o o ii) no hay problema, porque todas son operaciones entre reales. Y en iii) la primera operacin es el parntesis, que es una operacin entre reales. Al dividir por 2 ste es o e o e convertido a real antes de calcular el resultado. i) an podr utilizarse, cambiando el prefactor del parntesis a 1.0/2.0, una prctica u a e a que ser conveniente adoptar como standard cuando queremos utilizar enteros dena tro de expresiones reales, para evitar errores que pueden llegar a ser muy dif ciles de detectar.

2.2
2.2.1

Control de ujo.
if, if... else, if... else if.

Las construcciones siguientes permiten controlar el ujo del programa en base a si una expresin lgica es verdadera o falsa. o o a) En el caso de la sentencia if se evaluar la expresin (a==b), si ella es cierta ejecutar a o a la o las l neas entre los parntesis de llave y si la expresin es falsa el programa se salta e o esa parte del cdigo. o if (a==b) { cout << "a es igual a b" << endl; }

58

CAP ITULO 2. UNA BREVE INTRODUCCION A C++. En este y en muchos de los ejemplos que siguen, los parntesis cursivos son opcionales. e Ellos indican simplemente un grupo de instrucciones que debe ser tratado como una sola instruccin. En el ejemplo anterior, los parntesis cursivos despus del if (o despus o e e e de un while, for, etc. ms adelante) indican el conjunto de instrucciones que deben o a no ejecutarse dependiendo de si cierta proposicin es verdadera o falsa. Si ese conjunto o de instrucciones es una sola, se pueden omitir los parntesis: e if (a==b) cout << "a es igual a b" << endl; b) En el caso if... else hay dos acciones mutuamente excluyentes. La sentencia if (c!=b) evaluar la expresin (c!=b). Si ella es cierta ejecutar la o las l a o a neas entre los parntesis de llave que le siguen, saltndose la o las l e a neas entre los parntesis e de llave que siguen a la palabra clave else. Si la expresin es falsa el programa se salta o la primera parte del cdigo y slo ejecuta la o las l o o neas entre los parntesis de llave e que siguen a else. if (c!=d) { cout << "c es distinto de d" << endl; } else { cout << "c es igual a d" << endl; } c) En el ultimo caso se evaluar la expresin que acompaa al if y si ella es cierta se a o n ejecutar la o las l a neas entre los parntesis de llave que le siguen, saltndose todo e a el resto de las l neas entre los parntesis de llave que siguen a las palabras claves e else if y else. Si la primera expresin es falsa el programa se salta la primera parte o del cdigo y evala la expresin que acompaa al primer else if y si ella es cierta o u o n ejecutar la o las l a neas entre los parntesis de llave que le siguen, saltndose todo el e a resto de las l neas entre los parntesis que siguen a otros eventuales else if o al else. e Si ninguna de las expresiones lgicas resulta cierta se ejecutar la o las l o a neas entre los parntesis que siguen al else. e if (e > f) { cout << "e es mayor que f" << endl; } else if (e == f) { cout << "e es igual a f" << endl; } else { cout << "e es menor que f" << endl; }

Para C++, una expresin verdadera es igual a 1, y una falsa es igual a 0. Esto es, cuando o escribimos if(e>f), y e>f es falsa, en realidad estamos diciendo if(0). A la inversa, 0

2.2. CONTROL DE FLUJO.

59

es considerada una expresin falsa, y cualquier valor no nulo es considerado una expresin o o verdadera. As podr , amos hacer que una porcin del cdigo siempre se ejecute (o nunca) o o poniendo directamente if(1) o if(0), respectivamente. Naturalmente, lo anterior no tiene mucho sentido, pero un error habitual (y particularmente dif de detectar) es escribir a = b en vez de a == b en una expresin lgica. Esto cil o o normalmente trae consecuencias indeseadas, pues la asignacin a = b es una funcin que se o o evala siempre al nuevo valor de a. En efecto, una expresin como a=3 siempre equivale a u o verdadero, y a=0 siempre equivale a falso. Por ejemplo, en el siguiente programa: #include <iostream.h> void main(){ int k=3; if (k==3){ cout << "k es igual a 3" << endl; } k=4; if (k=3){ cout << "k es igual a 3" << endl; } } la salida siempre es: k es igual a 3 k es igual a 3 aunque entre los dos if el valor de k cambia.

2.2.2

Expresin condicional. o

Una construccin if else simple, que slo asigna un valor distinto a una misma variable o o segn si una proposicin es verdadera o falsa, es muy comn en programacin. Por ejemplo: u o u o if (a==b) { c = 1; } else { c = 0; } Existen dos maneras de compactar este cdigo. Este se puede reemplazar por o if (a==b) c = 1; else c = 0; Sin embargo, esto no es recomendable por razones de claridad al leer el cdigo. Una expresin o o ms compacta y clara, se consigue usando el operador ternario ? : a c = (a==b) ? 1 : 0; Como en el caso de los operadores de incremento y decremento, el uso del operador ? es preferible para optimizar el ejecutable resultante.

60

CAP ITULO 2. UNA BREVE INTRODUCCION A C++.

2.2.3

switch.

La instruccin switch permite elegir mltiples opciones a partir del valor de una variable o u entera. En el ejemplo siguiente tenemos que si i==1 la ejecucin continuar a partir del caso o a case 1:, si i==2 la ejecucin continuar a partir del caso case 2: y as sucesivamente. Si o a i toma un valor que no est enumerado en ningn case y existe la etiqueta default, la a u ejecucin continuar a partir de ah Si no existe default, la ejecucin contina luego del o a . o u ultimo parntesis cursivo. e switch (i) { case 1: { cout << "Caso 1." << endl; } break; case 2: { cout << "Caso 2." << endl; } break; default: { cout << "Otro caso." << endl; } break; } La instruccin break permite que la ejecucin del programa salte a la l o o nea siguiente despus e de la serie de instrucciones asociadas a switch. De esta manera slo se ejecutarn las l o a neas correspondiente al case elegido y no el resto. Por ejemplo, si i==1 ver amos en pantalla solo la l nea Caso 1. En el otro caso, si no existieran los break, y tambin i==1, entonces e ver amos en pantalla las l neas Caso 1., Caso 2. y Otro caso. La instruccin default es o opcional.

2.2.4

for.

Una instruccin que permite repetir un bloque de instrucciones un nmero denido de veces o u es el for. Su sintaxis comienza con una o varias inicializaciones, luego una condicin lgica o o de continuacin mientras sea verdadera, y nalmente una o ms expresiones que se evalan o a u vuelta por vuelta no incluyendo la primera vez. Siguiendo al for(...) viene una instruccin o o un bloque de ellas encerradas entre parntesis de llave. En el ejemplo siguiente la variable e entera i es inicializada al valor 1, luego se verica que la condicin lgica sea cierta y se o o ejecuta el bloque de instrucciones. A la vuelta siguiente se evala la expresin a la extrema u o derecha (suele ser uno o ms incrementadores), se verica que la condicin lgica se mantenga a o o cierta y se ejecuta nuevamente el bloque de instrucciones. Cuando la condicin lgica es falsa o o

2.2. CONTROL DE FLUJO.

61

se termina el loop, saltando la ejecucin a la l o nea siguiente al parentsis que indica el n del e bloque de instrucciones del for. En este ejemplo, cuando i=4 la condicin de continuacin o o ser falsa y terminar la ejecucin del for. a a o for (int i = 1; i < 4; i++) { cout << "Valor del indice: " << i << endl; } El output correspondiente es: Valor del indice: 1 Valor del indice: 2 Valor del indice: 3 Cualquier variable declarada en el primer argumento del for es local al loop. En este caso, la variable i es local, y no interere con otras posibles variables i que existan en nuestro cdigo. o for es una instruccin particularmente exible. En el primer y tercer argumento del for o se puede colocar ms de una instruccin, separadas por comas. Esto permite, por ejemplo, a o involucrar ms de una variable en el ciclo. El cdigo: a o for (int i=0, k=20; (i<10) && (k<50); i++, k+=6) { cout << "i + k = " << i + k << endl; } resulta en el output: i i i i i + + + + + k k k k k = = = = = 20 27 34 41 48

Adems, la condicin de continuacin (segundo argumento del for), no tiene por qu a o o e depender de las variables inicializadas en el primer argumento. Y el tercer argumento no tiene por qu ser un incremento o decremento de las variables del loop; puede ser cualquier e expresin que queramos ejecutar cada vez que un ciclo termina. En el siguiente ejemplo, o adems de incrementar los contadores en cada ciclo, se env un mensaje a pantalla: a a for (int i=1, k=2;k<20 && i<20;k++, i+=2, cout << "Fin iteracion" << endl){ cout << " i = " << i << endl; cout << " k = " << k << endl; } El resultado: i = 1, k = 2 Fin iteracion i = 3, k = 3 Fin iteracion i = 5, k = 4

62

CAP ITULO 2. UNA BREVE INTRODUCCION A C++.

Todos los argumentos del for son opcionales (no los ;), por lo cual se puede tener un for que carezca de inicializacin y/o de condicin de continuacin y/o de una expresin que se o o o o evale en cada iteracin. u o Un caso t pico en que se aprovecha la opcionalidad de los argumentos del for es para tener un loop innito, que puede servir para dejar el programa en pausa indenida. Para salir del loop (y en general, para detener cualquier programa en C++), hay que presionar ^C: for (; ; ) cout << "Este es un loop infinito, ^C para detenerlo"<< endl; Se puede adems, salir abruptamente del loop con break. El cdigo: a o for(int indice=0; indice<10; indice++) { int cuadrado = indice*indice ; cout << indice << " " ; if(cuadrado > 10 ) break ; } cout << endl; da la salida a pantalla: 0 1 2 3 4 aun cuando la condicin de continuacin permite que indice llegue hasta 9. o o Finalmente, las variables involucradas en el for pueden ser modicadas dentro del ciclo. Por ejemplo, modiquemos uno de los ejemplos anteriores, cambiando la variable k en medio del ciclo: for (int i=1, k=2;k<5 && i<8;k++, i+=2, cout << "Fin iteracion" << endl){ cout << " i = " << i << ", k = " << k << endl; k = k+5; } El resultado es: i = 1, k = 2 Fin iteracion En vez de pasar por el ciclo tres veces como ocurr originalmente, ahora, al cabo del primer a ciclo, k = 2 + 5 = 7 > 5, y el programa sale del loop. En general no es una buena prctica modicar las variables internas del ciclo en medio a de l, porque no es muy ordenado, y el desorden normalmente conduce a los errores en e programacin, pero ocasionalmente puede ser util hacer uso de esta libertad que proporciona o el lenguaje.

2.2. CONTROL DE FLUJO.

63

2.2.5

while.

La instruccin while permite repetir un bloque de instrucciones encerradas entre parntesis o e de llave mientras la condicin lgica que acompaa al while se mantenga cierta. La condicin o o n o es evaluada antes de que comience la primera iteracin; si es falsa en sta o en una posterior o e evaluacin no se ejecuta el bloque de instrucciones que le siguen y se contina la ejecucin en o u o la l nea siguiente al parentsis que indica el n del bloque asociado al while. Hay que notar e que la instruccin while podr no ejecutarse ni una sola vez si la condicin no se cumple o a o inicialmente. Un ejemplo simple: int i=1; while (i < 3) { cout << i++ << " "; } que da por resultado: 1 2 3. En el siguiente loop, la salida ser: 5 4 3 2 1 (Por qu?) a e int k=5 ; while(k) { cout << k-- <<" "; }

2.2.6

do...

while.

La instruccin do... while es anloga a while, salvo que la condicin lgica es evaluada o a o o despus de la primera iteracin. Por tanto, el bloque se ejecuta al menos una vez, siempre. e o Un ejemplo simple: do { cout << i++ << endl; } while (i<=20); Podemos construir de otra manera un loop innito usando do while do { cout << "Este es un segundo loop infinito, ^C para detenerlo"<< endl; } while (1);

2.2.7

goto.

Existe tambin en C++ una instruccin goto que permite saltar de un punto a otro del e o programa (goto salto; permite saltar a la l nea que contiene la instruccin salto:). Sin o embargo, se considera una mala tcnica de programacin usar goto, y siempre se puede die o sear un programa evitndolo. Es altamente no recomendable, pero si su utilizacin simplica n a o el cdigo se puede usar. o

64

CAP ITULO 2. UNA BREVE INTRODUCCION A C++.

2.3

Funciones.

Las funciones nos permiten programar partes del procedimiento por separado. Un ejemplo simple de ellas lo vimos en la subseccin 2.1.2. o

2.3.1

Funciones tipo void.

Un caso especial de funciones es aqul en que el programa que llama la funcin no espera e o que sta le entregue ningn valor al terminar. Por ejemplo, en la subseccin 2.1.2, la funcin e u o o PrintHola simplemente imprime un mensaje en pantalla. El resto del programa no necesita de ningn resultado parcial proveniente de la ejecucin de dicha funcin. La denicin de u o o o estas funciones debe ir precedida de la palabra void, como en el ejemplo citado.

2.3.2

return.

Si deseamos denir una funcin que calcule una ra cuadrada, evidentemente esperamos o z que la funcin nos entregue un resultado: el valor de la ra cuadrada. En este caso hay que o z traspasar el valor de una variable desde la funcin al programa que la llam. Esto se consigue o o con return. Veamos un ejemplo muy simple: int numero(){ int i = 3; return i; } main(){ cout << cout << int i = i = i + cout << }

"Llamamos a la funcion" << endl; "El numero es: " << numero() << endl; 5; numero(); "El numero mas 5 es: " << i << endl;

En este caso, la funcin simplemente entrega el valor de la variable interna i, es decir 3, el o cual puede ser usado para salida en pantalla o dentro de operaciones matemticas corrientes. a Separando declaracin e implementacin de la funcin, el ejemplo anterior se escribe: o o o int numero(); main(){ ... } int numero(){ int i = 3; return i; } Dos observaciones utiles:

2.3. FUNCIONES.

65

a) La declaracin de la funcin lleva antepuesto el tipo de variable que la funcin entrega. o o o En el ejemplo, la variable entregada es un entero, i, y la declaracin debe ser por tanto: o int numero(). Podemos tener funciones tipo double, char, long, etc., de acuerdo al tipo de variable que corresponde a return. b) La variable i que se usa dentro de main() y la que se usa dentro de numero() son distintas. A pesar de que tienen el mismo nombre, se pueden usar independientemente como si se llamaran distinto. Se dice que i es una variable local. Despus de return debe haber una expresin que se evale a una variable del tipo coe o u rrespondiente, ya sea expl citamente o a travs de un cast impl e cito. Las siguientes funciones devuelven un double al programa: double f1(){ double l = 3.0; return l; } double f2(){ double l = 3.0, m = 8e10; return l*m; } double f3(){ int l = 3; return l; } Sin embargo, la siguiente funcin har que el compilador emita una advertencia, pues se o a est tratando de devolver un double donde deber ser un int, y la conversin implica una a a o prdida de precisin: e o int f4(){ double l=3.0; return l; } Naturalmente, podemos modicar la funcin anterior haciendo una conversin expl o o cita antes de devolver el valor: return int(l).

2.3.3

Funciones con parmetros. a

Volviendo al ejemplo de la ra cuadrada, nos gustar llamar a esta funcin con un parmetro z a o a (el nmero al cual se le va a calcular la ra cuadrada). Consideremos por ejemplo una funcin u z o que necesita un solo parmetro, de tipo int, y cuyo resultado es otro int: a int funcion(int i){ i+=4;

66 return i; } main(){ int i = cout << << cout << }

CAP ITULO 2. UNA BREVE INTRODUCCION A C++.

3; "El valor de la funcion es " << funcion(i) endl; "El valor del parametro es " << i << endl;

El resultado en pantalla es: El valor de la funcion es 7 El valor del parametro es 3 La funcin funcion entrega el valor del parmetro ms 4. Usamos el mismo nombre (i) o a a para las variables en main y funcion, pero son variables locales, as que no intereren. Lo importante es notar que cuando se llama la funcin, la reasignacin del valor de i (i+=4) o o ocurre slo para la variable local en funcion; el parmetro externo mantiene su valor. o a Separando declaracin e implementacin el ejemplo anterior se escribe: o o int funcion(int); main(){...} int funcion(int i){ i+=4; return i; } Si nuestra funcin necesita ms parmetros, basta separarlos con comas, indicando para o a a cada uno su tipo: int funcion2(int,double); void funcion3(double,int,float); double funcion4(float); El compilador verica cuidadosamente que cada funcin sea llamada con el nmero de o u parmetros adecuados, y que cada parmetro corresponda al tipo especicado. En los ejema a plos anteriores, funcion2 debe ser llamada siempre con dos argumentos, el primero de los cuales es int y el segundo double. Como siempre, puede ser necesario un cast impl cito (si se llama funcion2 con el segundo argumento int, por ejemplo), pero si no existe una regla de conversin automtica (llamando a funcion2 con el primer argumento double, por o a ejemplo), el compilador enviar una advertencia. Adems, el compilador verica que el valor a a de retorno de la funcin sea usado como corresponde. Por ejemplo, en las dos l o neas: double m = funcion2(2,1e-3); int k = funcion4(0.4);

2.3. FUNCIONES.

67

la primera compilar exitosamente (pero hay un cast impl a cito), y la segunda dar una a advertencia. Existen dos modos de transferir parmetros a una funcin: a o a) Por valor. Se le pasan los parmetros para que la funcin que es llamada copie sus a o valores en sus propias variables locales, las cuales desaparecern cuando la funcin a o termine y no tienen nada que ver con las variables originales. Hasta ahora, en todos los ejemplos de esta subseccin el traspaso de parmetros ha sido o a por valor. En la funcion int funcion(int), en el cdigo de la pgina 65, lo que ha o a ocurrido es que la funcin copia el valor de la variable externa i en una nueva variable o (que tambin se llama i, pero est en otra direccin de memoria). El valor con el que e a o trabaja la funcin es la copia, manteniendo inalterada la variable original. o b) Por referencia. Se le pasa la direccin de memoria de los parmetros. La funcin o a o llamada puede modicar el valor de tales variables. La misma funcin de la pgina 65 puede ser modicada para que el paso de parmetros o a a sea por referencia, modicando la declaracin: o int funcion(int &); main() { int i = 3; cout << "El valor de la funcion es " << funcion(i) << endl; cout << "El valor del parametro es " << i << endl; } int funcion(int & i) { i+=4; return i; } En vez de traspasarle a funcion el valor del parmetro, se le entrega la direccin de a o memoria de dicha variable. Debido a ello, funcion puede modicar el valor de la variable. El resultado en pantalla del ultimo programa ser: a El valor de la funcion es 7 El valor del parametro es 7 Debido a que las variables dejan de ser locales, el paso de parmetros por reerencia debe a ser usado con sabidur De hecho el ejemplo presentado es poco recomendable. Peor a. an, el problema es no slo que las variables dejan de ser locales, sino que es imposible u o saber que no lo son desde el main. En efecto, el main en ambas versiones de funcion es el mismo. Lo unico que cambi es la declaracin de la funcin. Puesto que un o o o

68

CAP ITULO 2. UNA BREVE INTRODUCCION A C++. usuario normal usualmente no conoce la declaracin e implementacin de cada funcin o o o que desea usar (pues pueden haber sido hechas por otros programadores), dejamos al usuario en la indefensin. o Por otro lado, hay al menos dos situaciones en que el paso de referencia es la unica opcin viable para entregar los parmetros. Un caso es cuando hay que cuidar el uso o a de la memoria. Supongamos que una funcin necesita un parmetros que es una matriz o a de 10 millones de las por 10 millones de columnas. Seguramente estaremos llevando al l mite los recursos de nuestra mquina, y ser una torpeza pasarle la matriz por valor: a a ello involucrar primero, duplicar la memoria utilizada, con el consiguiente riesgo de a, que nuestro programa se interrumpa; y segundo, har el programa ms lento, porque a a la funcin necesitar llenar su versin local de la matriz elemento por elemento. Es o a o decir, nada de eciente. En esta situacin, el paso por referencia es lo adecuado. o Un segundo caso en que el paso por referencia es recomendable es cuando efectivamente nuestra intencin es cambiar el valor de las variables. El ejemplo t o pico es el intercambio de dos variables entre s digamos a1=1 y a2=3. Luego de ejecutar la funcin queremos , o que a1=3 y a1=1. El siguiente cdigo muestra la denicin y el uso de una funcin para o o o esta tarea, y por cierto requiere el paso de parmetros por referencia: a #include <iostream.h> void swap(int &,int &); void main(){ int i = 3, k=10; swap(i,k); cout << "Primer argumento: " << i << endl; cout << "Segundo argumento: " << k << endl; } void swap(int & j,int & p){ int temp = j; j = p; p = temp; } El output es: Primer argumento: 10 Segundo argumento: 3

En el ejemplo de la matriz anterior, ser interesante poder pasar el parmetro por refea a rencia, para ahorrar memoria y tiempo de ejecucin, pero sin correr el riesgo de que nuestra o matriz gigantesca sea modicada por accidente. Afortunadamente existe el modo de hacerlo, usando una palabra que ya hemos visto antes: const. En el siguiente cdigo: o

2.3. FUNCIONES. int f5(const int &); main(){...} int f5(const int & i){...};

69

f5 recibir su unico argumento por referencia, pero, debido a la presencia del modicador a const, el compilador avisar si se intenta modicar el argumento en medio del cdigo de la a o funcin. o

2.3.4

Parmetros por defecto. a

C++ permite que omitamos algunos parmetros de la funcin llamada, la cual reemplaza los a o valores omitidos por otros predeterminados. Tomemos por ejemplo la funcin int funcion(int); o de la subseccin 2.3.3, y modiqumosla de modo que si no le entregamos parmetros, asuma o e a que el nmero entregado fue 5: u int funcion(int i = 5){ i+=4; return i; } main(){ cout << "El resultado default es " << funcion() << endl; int i = 3; cout << "Cuando el parametro vale " << i << " el resultado es " << funcion(i) << endl; } El output correspondiente es: El resultado default es 9 Cuando el parametro vale 3 el resultado es 7 Separando declaracin e implementacin: o o int funcion(int = 5); main(){...} int funcion(int i){ i+=4; return i; } Si una funcin tiene n argumentos, puede tener m n argumentos opcionales. La unica o restriccin es que, en la declaracin e implementacin de la funcin, los parmetros opcionales o o o o a ocupen los ultimos lugares m lugares: void f1(int,int = 4); int f2(double,int = 4, double = 8.2); double f3(int = 3,double = 0.0, int = 0);

70

CAP ITULO 2. UNA BREVE INTRODUCCION A C++.

En este caso, f1(2), f1(2,8), f2(2.3,5), f3(3), f3(), y muchas otras, son todas llamadas vlidas de estas funciones. Cada vez, los parmetros no especicados son reemplazados por a a sus valores predeterminados.

2.3.5

Ejemplos de funciones: ra cuadrada y factorial. z

Ra cuadrada. z Con lo visto hasta ahora, ya podemos escribir un programa que calcule la ra cuadrada de z una funcin. Para escribir una funcin, debemos tener claro qu se espera de ella: cuntos o o e a y de qu tipo son los argumentos que recibir, que tipo de valor de retorno deber tener, e a a y, por cierto, un nombre adecuado. Para la raiz cuadrada, es claro que el argumento es un nmero. Pero ese nmero podr ser un entero o un real, y eso al compilador no le da lo u u a mismo. En este punto nos aprovechamos del cast impl cito: en realidad, basta denir la ra z cuadrada con argumento double; de este modo, si se llama la funcin con un argumento o int, el compilador convertir automticamente el int en double y nada fallar. En cambio, a a a si la deniramos para int y la llamamos con argumento double, el compilador se quejar e a de que no sabe efectuar la conversin. Si el argumento es double, evidentemente esperamos o que el valor de retorno de la funcin sea tambin un double. Llamando a la funcin raiz, o e o tenemos la declaracin: o double raiz(double); Debido a la naturaleza de la funcin raiz cuadrada, raiz() no tendr sentido, y por tanto o a no corresponde declararla con un valor default. Ahora debemos pensar en cmo calcular la ra cuadrada. Usando una variante del mtodo o z e de Newton-Raphson, se obtiene que la secuencia xn+1 = 1 2 xn + a xn

converge a a cuando n . Por tanto, podemos calcular la ra cuadrada con aproximaz ciones sucesivas. El clculo terminar en el paso N , cuando la diferencia entre el cuadrado a a de la aproximacin actual, xN , y el valor de a, sea menor que un cierto nmero pequeo: o u n | xN a | < 1. El valor de determinar la precisin de nuestro clculo. Un ejemplo de a o a cdigo lo encontramos a continuacin: o o #include <iostream.h> #include <math.h> double raiz(double); void main(){ double r; cout.precision(20);

2.3. FUNCIONES. cout << "Ingrese un numero: " << endl; cin >> r; cout << raiz(r) << endl; } double raiz(double a){ double x, dx = 1e3, epsilon = 1e-8; while (fabs(dx)>epsilon){ x = (x + a/x)/2; dx = x*x - a; cout << "x = " << x << ", precision = " << dx << endl; } return x; }

71

Luego de la declaracin de la funcin raiz, est main, y al nal la implementacin de o o a o raiz. En main se pide al usuario que ingrese un nmero, el cual se aloja en la variable r, u y se muestra en pantalla el valor de su raiz cuadrada. La instruccin cout.precision(20) o permite que la salida a pantalla muestre el resultado con 20 cifras signicativas. En la implementacin de la funcin hay varios aspectos que observar. Se ha llamado x a la o o variable que contendr las sucesivas aproximaciones a la ra Al nal del ciclo, x contendr el a z. a valor (aproximado) de la raiz cuadrada. dx contiene la diferencia entre el cuadrado de x y el valor de a. epsilon es el nmero (pequeo) que determina si la aproximacin es satisfactoria u n o o no. El ciclo est dado por una instruccin while, y se ejecuta mientras dx>epsilon, es decir a o termina cuando dx es sucientemente pequeo. El valor absoluto del real dx se obtiene con la n funcin matemtica fabs, disponible en el header math.h incluido al comienzo del programa. o a Observar que inicialmente dx=1e3, esto es un valor muy grande; esto permite que la condicin o del while sea siempre verdadera, y el ciclo se ejecuta al menos una vez. Dentro del ciclo, se calcula la nueva aproximacin, y se env a pantalla un mensaje con o a la aproximacin actual y la precisin alcanzada (dada por dx). Eventualmente, cuando la o o aproximacin es sucientemente buena, se sale del ciclo y la funcin entrega a main el valor o o de x actual, que es la ultima aproximacin calculada. o Factorial. Otro ejemplo util es el clculo del factorial, denido para numeros naturales: a n! = n (n 1) 2 1 , 0! 1 .

La estrategia natural es utilizar un ciclo for, determinado por una variable entera i, que va desde 1 a n, guardando los resultados en una variable auxiliar que contiene el producto de todos los nmeros naturales desde 1 hasta i: u

72 #include <iostream.h> int factorial(int);

CAP ITULO 2. UNA BREVE INTRODUCCION A C++.

int main(){ int n=25 ; cout << "El factorial de " << n << " es: " << factorial(n) << endl; } int factorial(int i) { int f =1; for (int j=1;j<=i;j++){ f = f*j; } return f; } Observar que la variable auxiliar f que contiene el producto de los primeros i nmeros u naturales debe ser inicializada a 1. Si se inicializara a 0, factorial(n) ser 0 para todo n. a Esta funcin no considera el caso n=0, pero al menos para el resto de los naturales funo cionar bien. a

2.3.6

Alcance, visibilidad, tiempo de vida.

Con el concepto de funcin hemos apreciado que es posible que coexistan variables con el o mismo nombre en puntos distintos del programa, y que signiquen cosas distintas. Conviene entonces tener en claro tres conceptos que estn ligados a esta propiedad: a Alcance (scope) La seccin del cdigo durante la cual el nombre de una variable puede ser o o usado. Comprende desde la declaracin de la variable hasta el nal del cuerpo de la o funcin donde es declarada. o Si la variable es declarada dentro de una funcin es local . Si es denida fuera de todas o las funciones (incluso fuera de main), la variable es global. Visibilidad Indica cules de las variables actualmente al alcance pueden ser accesadas. En a nuestros ejemplos (subseccin 2.3.3), la variable i en main an est al alcance dentro o u a de la funcin funcion, pero no es visible, y por eso es posible reutilizar el nombre. o Tiempo de vida Indica cundo las variables son creadas y cundo destruidas. En general a a este concepto coincide con el alcance (las variables son creadas cuando son declaradas y destruidas cuando la funcin dentro de la cual fueron declaradas termina), salvo porque o es posible denir: (a) variables dinmicas, que no tienen alcance, sino slo tiempo de a o vida; (b) variables estticas, que conservan su valor entre llamadas sucesivas de una a funcin (estas variables tienen tiempo de vida mayor que su alcance). Para declarar o estas ultimas se usa un modicador static.

2.3. FUNCIONES. El efecto del modicador static se aprecia en el siguiente ejemplo: #include <iostream.h> int f(); int main(){ cout << f() << endl; cout << f() << endl; } int f(){ int x=0; x++; return x; }

73

La funcin f simplemente toma el valor inicial de x y le suma 1. Como cada vez que la o funcin es llamada la variable local x es creada e inicializada, el resultado de este programa o es siempre un 1 en pantalla: 1 1 Ahora modiquemos la funcin, haciendo que x sea una variable esttica: o a #include <iostream.h> int f(); int main(){ cout << f() << endl; cout << f() << endl; } int f(){ static int x=0; x++; return x; } Ahora, al llamar a f por primera vez, la variable x es creada e inicializada, pero no destruida cuando la funcin termina, de modo que conserva su valor cuando es llamada por o segunda vez:

74 1 2

CAP ITULO 2. UNA BREVE INTRODUCCION A C++.

Veamos un ejemplo de una variable esttica en el clculo del factorial: a a int factorial2(int i=1){ static int fac = 1; fac*=i; return fac ; } main (){ int n=5; int m=n; while(n>0) factorial2(n--); cout << "El factorial de "<< m << " es = " << factorial2() << endl; } La idea es, si se desea calcular el factorial de 5, por ejemplo, es llamar a la funcin factorial2 o una vez, con argumento n = 5, y despus disminuir n en 1. Dentro de la funcin, una variable e o esttica (fac) aloja el valor 1 5 = 5. Luego se llama nuevamente con n = 4, con lo cual a fac=1*5*4, y as sucesivamente, hasta llegar a n = 1, momento en el cual fac=1*5*4*3*2*1. Al disminuir n en 1 una vez ms, la condicin del while es falsa y se sale del ciclo. Al llamar a o una vez ms a factorial2, esta vez sin argumentos, el programa asume que el argumento a tiene el valor predeterminado 1, y as el resultado es 1*5*4*3*2*1*1, es decir 5!. Observemos el uso del operador de decremento en este programa: factorial2(n--) llama a la funcin con argumento n y despus disminuye n en 1. Esto es porque el operador de o e decremento est actuando como sujo, y es equivalente a las dos instrucciones: a factorial2(n); n--; Si fuera un prejo [factorial2(n--)], primero disminuir n en 1, y llamar luego a a a factorial2 con el nuevo valor de n Este ejemplo de clculo del factorial ilustra el uso de una variable esttica, que aloja los a a productos parciales de los nmeros enteros, pero no es un buen ejemplo de una funcin que u o calcule el factorial, porque de hecho esta funcin no lo calcula: es main quien, a travs de o e sucesivas llamadas a factorial2, calcula el factorial, pero la funcin en s no. o

2.3.7

Recursin. o

C++ soporta un tipo especial de tcnica de programacin, la recursin, que permite que una e o o funcin se llame a s misma (esto es no trivial, por cuanto si denimos, digamos, una funcin o o f, dentro del cuerpo de la implementacin no hay ninguna declaracin a una funcin f, y por o o o tanto en principio no se podr usar f porque dicho nombre no estar en scope; C++ permite a a soslayar este hecho). La recursin permite denir de modo muy compacto una funcin que o o calcule el factorial de un nmero entero n. u

2.3. FUNCIONES. int factorial3(int n){ return (n<2) ? 1: n * factorial(n-1); } int main(){ int n=5; cout << "El factorial de "<< m << " es = " << factorial3(n) << endl; }

75

En este tercer ejemplo, el factorial de n es denido en funcin del factorial de n 1. o Se ha usado la expresin condicional (operador ?) para compactar an ms el cdigo. Por o u a o ejemplo, al pedir el factorial de 5 la funcin se pregunta si 5 < 2. Esto es falso, luego, la o funcin devuelve a main el valor 5*factorial3(4). A su vez, factorial3(4) se pregunta si o 4 < 2; siendo falso, devuelve a la funcin que la llam (es decir, a factorial3(5)), el valor o o 4*factorial3(3). El proceso sigue hasta que factorial(2) llama a factorial3(1). En ese momento, 1 < 2, y la funcin factorial3(1), en vez de llamar nuevamente al factorial, o devuelve a la funcin que la llam el valor 1. No hay ms llamadas a factorial3, y el o o a proceso de recursin se detiene. El resultado nal es que main recibe el valor factorial3(5) o = 5*factorial3(4) = = 5*4*3*2*factorial3(1) = 5*4*3*2*1= 120. Este tercer cdigo para el clculo del factorial s considera el caso n = 0, y adems es ms o a a a eciente, al ser ms compacto. a La recursin debe ser empleada con cuidado. Es importante asegurarse de que existe una o condicin para la cual la recursin se detenga, de otro modo, caer o o amos en una recursin o innita que har intil nuestro programa. En el caso del factorial, pudimos vericar que a u dicha condicin existe, por tanto el programa es nito. En situaciones ms complicadas o a puede no ser tan evidente, y es responsabilidad del programador como siempre revisar que todo est bajo control. e

2.3.8

Funciones internas.

Exiten muchas funciones previamente implementadas en C++ almacenadas en distintas bibliotecas. Una de las bibliotecas importante es la matemtica. Para usarla uno debe incluir el a archivo de header <math.h> y luego al compilar agregar al nal del comando de compilacin o -lm:

g++ -o <salida> <fuente>.cc -lm

si se desea crear un ejecutable <salida> a partir del cdigo en <fuente>.cc. o Veamos algunas de estas funciones:

76 pow(x,y) fabs(x) sqrt(x) sin(x) cos(x) tan(x) atan(x) atan2(y, x) exp(x) log(x) log10(x) floor(x) ceil(x) fmod(x,y)

CAP ITULO 2. UNA BREVE INTRODUCCION A C++. Eleva a potencia, xy Valor absoluto Ra cuadrada z Seno y coseno Tangente Arcotangente de x en [, ] Arcotangente de y/x en [, ] Exponencial Logaritmo natural y logaritmo en base 10 Entero ms cercano hacia abajo (e.g. floor(3.2)=3) a Entero ms cercano hacia arriba (e.g. ceil(3.2)=4) a El resto de x/y (e.g. fmod(7.3, 2)=1.3)

Para elevar a potencias enteras, es ms conveniente usar la forma expl a cita en vez de la funcin pow, i.e. calcular x^3 como x*x*x es ms eciente computacionalmente que pow(x,3), o a debido a los algoritmos que usa pow para calcular potencias. Estos son ms convenientes a cuando las potencias no son enteras, en cuyo caso no existe una forma expl cita en trminos e de productos.

2.4

Punteros.

Una de las ventajas de C++ es permitir el acceso directo del programador a zonas de memoria, ya sea para crearlas, asignarles un valor o destruirlas. Para ello, adems de los tipos de a variables ya conocidos (int, double, etc.), C++ proporciona un nuevo tipo: el puntero. El puntero no contiene el valor de una variable, sino la direccin de memoria en la cual dicha o variable se encuentra. Un pequeo ejemplo nos permite ver la diferencia entre un puntero y la variable a la cual n ese puntero apunta: void main(){ int i = 42; int * p = &i; cout << "El valor del puntero es: " << p << endl; cout << "Y apunta a la variable: " << *p << endl; } En este programa denimos una variable i entera. Al crear esta variable, el programa reserv un espacio adecuado en algn sector de la memoria. Luego pusimos, en esa direccin o u o de memoria, el valor 42. En la siguiente l nea creamos un puntero a i, que en este caso denominamos p. Los punteros no son punteros a cualquier cosa, sino punteros a un tipo particular de variable. Ello es maniesto en la forma de la declaracin: int * p. En la o misma l nea asignamos a este puntero un valor. Ese valor debe ser tambin una direccin de e o memoria, y para eso usamos &i, que es la direccin de memoria donde est i. Ya hemos visto o a antes el uso de & para entregar una direccin de memoria, al estudiar paso de parmetros a o a funciones por referencia (2.3.3). Al ejecutar este programa vemos en pantalla los mensajes:

2.4. PUNTEROS. El valor del puntero es: 0xbffff9d8 Y apunta a la variable: 42

77

Primero obtenemos un nmero hexadecimal imposible de determinar a priori, y que corresu ponde a la direccin de memoria donde qued ubicada la variable i. La segunda l o o nea nos da el valor de la variable que est en esa direccin de memoria: 42. Puesto que * aplicado a o a un puntero entrega el contenido de esa direccin de memoria, se le denomina operador de o dereferenciacin. o En este ejemplo, hemos creado un puntero que contiene la direccin de memoria de una o variable preexistente: declaramos una variable, esa variable queda en alguna direccin de o memoria, y despus asignamos esa direccin de memoria a un puntero. En este caso, podemos e o referirnos a la variable tanto por su nombre (i) como por su puntero asociado (p_i). Tambin es posible crear directamente una direccin de memoria, sin necesidad de crear e o una variable antes. En este caso, la unica forma de manipular este objeto es a travs de su e puntero, porque no existe ninguna variable y por tanto ningn nombre asociado a l. Esto u e se hace con el operador new. El mismo ejemplo anterior puede ser reescrito usando slo o punteros: void main(){ int * p = new int; *p = 42; cout << "El valor del puntero es: " << p << endl; cout << "Y apunta a la variable: " << *p << endl; delete p; } La primera l nea crea un nuevo puntero a int llamado p. new verica que haya suciente memoria para alojar un nuevo int, y si es as reserva ese espacio de memoria. En p queda la direccin de la memoria reservada. Esto es equivalente a la declaracin int i; del programa o o anterior, salvo que ahora la unica manera de accesar esa direccin de memoria es a travs o e del puntero p. A continuacin se coloca dentro de esa direccin (observar la presencia del o o operador de dereferenciacin *) el nmero 42. El programa manda a pantalla la misma o u informacin que la versin anterior, salvo que seguramente el valor de p ser distinto. o o a Finalmente, ya que el puntero no volver a ser usado, la direccin de memoria debe a o ser liberada para que nuestro u otros programas puedan utilizarla. Ello se realiza con el operador delete. Todo puntero creado con new debe ser, cuando ya no se utilice, borrado con delete. Ello evitar desagradables problemas en nuestro programa debido a fuga de a memoria (memory leak ). Los punteros tienen gran importancia cuando de manejar datos dinmicos se trata, es a decir, objetos que son creados durante la ejecucin del programa, en nmero imposible de o u predecir al momento de compilar. Por ejemplo, una aplicacin X-windows normal que crea o una, dos, tres, etc. ventanas a medida que uno abre archivos. En este caso, cada ventana es un objeto dinmico, creado durante la ejecucin, y la unica forma de manejarlo es a travs a o e de un puntero a ese objeto, creado con new cuando la ventana es creada, y destruido con delete cuando la ventana es cerrada.

78

CAP ITULO 2. UNA BREVE INTRODUCCION A C++.

2.5
2.5.1

Matrices.
Declaracin e inicializacin. o o

Podemos declarar (e inicializar inmediatamente) matrices de enteros, reales de doble precisin, caracteres, etc., segn nuestras necesidades. o u int a[5]; double r[3] = {3.5, 4.1, -10.8}; char palabra[5]; Una vez declarada la matriz (digamos a[5]), los valores individuales se accesan con a[i], con i desde 0 a 4. Por ejemplo, podemos inicializar los elementos de la matriz as : a[0] = 3; a[3] = 5; ... o si queremos ingresarlos desde el teclado: for (i = 0; i < 5; i++){ cin >> a[i]; } Y si deseamos escribirlos en pantalla: for (i = 0; i < 5; i++){ cout >> a[i]; }

2.5.2

Matrices como parmetros de funciones. a

Si deseamos, por ejemplo, disear una funcin que mande los elementos de una matriz a n o pantalla, necesitamos entregarle como parmetro la matriz que va a utilizar. Para ello se a agrega [] luego del nombre de la variable, para indicar que se trata de una matriz: void PrintMatriz(int, double []); void main(){ double matriz[5] = {3.5, 5.2, 2.4, -0.9, -10.8}; PrintMatriz(5, matriz); } void PrintMatriz(int i, double a[]){ for (int j = 0; j < i; j++){ cout << "Elemento " << j << " = " << a[j] << endl; } }

2.5. MATRICES.

79

Observemos que la funcin debe recibir dos parmetros, uno de los cuales es la dimensin o a o de la matriz. Esto se debe a que cuando las matrices son usadas como parmetros la infora macin de su dimensin no es traspasada, y debe ser comunicada independientemente. Una o o ligera optimizacin al programa anterior es modicar main a: o main() { const int dim = 5; double matriz[dim] = {3.5, 5.2, 2.4, -0.9, -10.8}; PrintMatriz(dim, matriz); } De este modo, si eventualmente cambiamos de opinin y deseamos trabajar con matrices de o longitud distinta, slo hay que modicar una l o nea de cdigo (la primera) en todo el programa, o el cual puede llegar a ser bastante largo por cierto. (En el ejemplo, tambin habr que e a cambiar la l nea de inicializacin de la matriz, porque asume que la matriz requiere slo 5 o o elementos, pero de todos modos deber ser clara la enorme conveniencia.) Podemos reescribir a este programa con un comando de preprocesador para hacer la denicin de la dimensin: o o #include <iostream.h> #define DIM 5 main(){ double matriz[DIM] = {3.5, 5.2, 2.4, -0.9, -10.8}; PrintMatriz(DIM, matriz); } Sin embargo, ninguna de estas alternativas resuelve el problema de que el compilador espera que la dimensin de una matriz sea un entero constante, determinado en el momento o de la compilacin (no de la ejecucin). o o

2.5.3

Asignacin dinmica. o a

La reserva de memoria para la matriz podemos hacerla en forma dinmica ocupando el a operador new que pedir al sistema la memoria necesaria, si est disponible el sistema se la a a asignar. Como con cualquier puntero, una vez desocupado el arreglo debemos liberar la a memoria con el comando delete. #include <iostream.h> main() { cout<<"Ingrese la dimension deseada :" ; int dim ; cin >> dim ; double * matriz = new double[dim] ; // Reserva la memoria for(int i=0; i < dim; i++) { cout << "Ingrese elemento "<< i <<" : "; cin >> matriz[i] ;

80 }

CAP ITULO 2. UNA BREVE INTRODUCCION A C++.

for (int i=0;i<dim;i++){ cout << matriz[i] << ", "; } cout << endl; delete [] matriz } Este ejemplo permite apreciar una gran ventaja del uso de punteros, al permitirnos liberarnos de denir la dimensin de una matriz como una constante. Aqu dim es simplemente o , un int. La asignacin dinmica permite denir matrices cuya dimensin se determina recin o a o e durante la ejecucin. o Observemos nalmente que la liberacin de memoria, en el caso de arreglos, se hace con o el operador delete [], no delete como en los punteros usuales. // Libera la memoria reservada

2.5.4

Matrices multidimensionales.

Es fcil declarar e inicializar matrices de ms de una dimensin: a a o double array[10][8]; int array[2][3] = {{1, 2, 3}, {4, 5, 6}}; Una operacin usual es denir primero las dimensiones de la matriz, y luego llenar sus o elementos uno por uno (o desplegarlos en pantalla), recorriendo la matriz ya sea por las o por columnas. Hay que tener cuidado del orden en el cual uno realiza las operaciones. En el siguiente cdigo, denimos una matriz de 10 las y 3 columnas, la llenamos con ceros o elemento por elemento, y luego inicializamos tres de sus elementos a nmeros distintos de u cero. Finalmente desplegamos la matriz resultante en pantalla: #include <iostream.h> int main(){ const int dimx=3, dimy=10; double a[dimy][dimx]; for (int i=0;i<dimy;i++){ for (int j=0;j<dimx;j++){ a[i][j]=0; } cout << endl; }

2.5. MATRICES. a[0][0]=1; a[3][2]=2; a[9][2]=3; for (int i=0;i<dimy;i++){ for (int j=0;j<dimx;j++){ cout << a[i][j] << ", "; } cout << endl; } }

81

Inicializar los elementos a cero inicialmente es particularmente relevante. Si no, la matriz se llenar con elementos aleatorios. a Tambin es posible denir arreglos bidimensionales dinmicamente. En el siguiente ejeme a plo, se dene una matriz de 200 las y 400 columnas, inicializndose sus elementos a cero, y a nalmente se borra: int main() { int width; int height; width = 200; height = 400;

double ** matriz = new double * [width]; for (int i=0;i<width;i++){ matriz[i] = new double[height]; } for (int i=0;i<width;i++){ for (int j=0;j<height;j++){ matriz[i][j] = 0; } } for (int i=0;i<width;i++){ delete [] matriz[i]; } delete [] matriz; }

82

CAP ITULO 2. UNA BREVE INTRODUCCION A C++.

Primero se crea, con new, un puntero (matriz) de dimensin 200, que representar las o a las. Cada uno de sus elementos (matriz[i]), a su vez, ser un nuevo puntero, de dimensin a o 400, representando cada columna. Por tanto, matriz debe ser un puntero a puntero (de dobles, en este caso), y as es denido inicialmente (double ** ...). Esto puede parecer extrao a primera vista, pero recordemos que los punteros pueden ser punteros a cualquier n objeto, en particular a otro puntero. Luego se crean los punteros de cada columna (primer ciclo for). A continuacin se llenan los elementos con ceros (segundo ciclo for). Finalmente, o se libera la memoria, en orden inverso a como fue asignada: primero se libera la memoria de cada columna de la matriz (delete [] matriz[i], tercer ciclo for), y por ultimo se libera la memoria del puntero a estos punteros (delete [] matriz).

2.5.5

Matrices de caracteres: cadenas (strings).

Una palabra, frase o texto ms largo es representado internamente por C++ como una matriz a de chars. A esto se le llama cadena (string). Sin embargo, esto ocasiona un problema, pues las matrices deben ser denidas con dimensin constante (a menos que sean denidas o dinmicamente), y las palabras pueden tener longitud arbitraria. La convencin de C++ a o para resolver el problema es aceptar que una cadena tiene longitud arbitraria, pero debe indicar dnde termina. Esto se hace con el char nulo: \0. As para asignar a la variable o , palabra el valor Hola, debe denirse como una matriz de dimensin 5 (una ms que el o a nmero de letras): u char palabra[5] = {H, o, l, a, \0}; Para escribir Hola en pantalla basta recorrer los elementos de palabra uno a uno: for (i = 0; i < 5; i++) { cout << palabra[i]; } Si tuviramos que hacer esto cada vez que queremos escribir algo a pantalla no ser muy e a cmodo. Por ello, tambin podemos escribir Hola en pantalla simplemente con cout << "Hola", o e y de hecho se fue el primer ejemplo de este cap e tulo. De hecho, la declaracin de palabra o podr haberse escrito: a char palabra[5] = "Hola"; Esto ya es bastante ms cmodo, aunque persiste la inconsistencia de denir palabra con a o dimensin 5, cuando en realidad al lado derecho de la asignacin hay un objeto con slo 4 o o o elementos (visibles). Este y otros problemas asociados con el manejo convencional de cadenas en C++ se resuelven incluyendo el header string. string El cdigo anterior se puede reescribir: o

2.5. MATRICES. #include <iostream.h> #include <string> int main(){ string palabra = "Hola"; cout << texto1 << endl; }

83

Observar que la l nea a incluir es #include <string>, sin la extensin .h. Al incluir o string, las cadenas pueden ser declaradas como objetos tipo string en vez de arreglos de char. El hecho de que ya no tengamos que denir a priori la dimensin de la cadena es o una gran ventaja. De hecho, permite ingresar palabras desde el teclado trivialmente, sin preocuparse de que el input del usuario sea demasiado grande (tal que supere la dimensin o del arreglo que podamos haber declarado inicialmente) o demasiado corto (tal que se traduzca en un despilfarro de memoria por reservar ms memoria para el arreglo de la que realmente a se necesita): #include <iostream.h> #include <string> int main(){ string palabra; cin >> texto1; } Adems, este nuevo tipo string permite acceder a un sinnmero de funciones adicionales a u que facilitan enormemente el manejo de cadenas. Por ejemplo, las cadenas se pueden sumar, donde la suma de cadenas a y b est denida (siguiendo la intuicin) como la cadena que a o resulta de poner b a continuacin de a: o #include <iostream.h> #include <string> int main(){ string texto1 = "Primera palabra"; string texto2 = "Segunda palabra"; cout << texto1 << endl << texto2 << endl; cout << texto1 + ", " + texto2 << endl; // La ultima linea es equivalente a: // string texto3 = texto1 + ", " + texto2; // cout << texto3 << endl; } El output de este programa ser: Primera palabra, Segunda palabra. a Dijimos que es muy fcil ingresar una cadena desde el teclado, pues no es necesario a denir la dimensin desde el comienzo. Sin embargo, el cdigo anterior, usando cin, no o o es muy general, porque el input termina cuando el usuario ingresa el primer cambio de

84

CAP ITULO 2. UNA BREVE INTRODUCCION A C++.

l nea o el primer espacio. Esto es muy cmodo cuando queremos ingresar una serie de vao lores (por ejemplo, para llenar un arreglo), pues podemos ingresarlos ya sea en la forma: 1<Enter> 2<Enter> 3<Enter>, etc., o 1 2 3, etc, pero no es ptimo cuando deseamos ino gresar texto, que podr constar de ms de una palabra y, por tanto, necesariamente incluir a a a espacios (por ejemplo, al ingresar el nombre y apellido de una persona). Sin explicar demasiado por qu, digamos que la solucin a este problema es utilizar una funcin asociada a cin e o o llamada gets, y que espera input desde el teclado hasta que el usuario d el primer cambio e de l nea. Un ejemplo simple lo encontramos en el siguiente cdigo: o #include <iostream.h> #include <string> int main(){ string texto1 = "El resultado es: " ; char * texto2; cin.gets(&texto2); string texto cout << texto1 + string(texto2) << endl; } Observamos que gets acepta en realidad un argumento que es un puntero a puntero de caracteres (texto2 fue declarado como un puntero a char, y gets es llamado con el argumento &texto2, que es la direccin de memoria asociada a texto2, i.e. el puntero que o apunta a texto2.) De este modo, gets espera input desde el teclado hasta el primer cambio de l nea, y asigna la cadena ingresada a texto2. Sin embargo, si despus queremos utilizar texto2 en e conjunto con otras cadenas (denidas como string), ser necesario convertirla expl a citamente a string (ver seccin 2.1.9). En nuestro cdigo, desebamos sumar texto1 con texto2 y o o a enviar el resultado a pantalla.

2.6

Manejo de archivos

Una operacin usual en todo tipo de programas es la interaccin con archivos. Ya sea que o o el programa necesite conocer ciertos parmetros de conguracin, hacer anlisis estad a o a stico sobre un gran nmero de datos generados por otro programa, entregar las coordenadas de u los puntos de una trayectoria para gracarlos posteriormente, etc., lo que se requiere es un modo de ingresar datos desde, o poner datos en, archivos. En C++ ello se efecta incluyendo u el header fstream.h.

2.6.1

Archivos de salida

Observemos el siguiente programa: #include <iostream.h>

2.6. MANEJO DE ARCHIVOS #include <fstream.h> int main(){ ofstream nombre_logico("nombre_fisico.dat"); int i = 3, j; cout << i << endl; nombre_logico << i << endl; cout << "Ingrese un numero entero: "; cin >> j; cout << i << endl; nombre_logico << j << endl; nombre_logico.close(); }

85

La primera l nea de main dene un objeto de tipo ofstream (output le stream). Esto corresponde a un archivo de salida. Dentro de main este archivo ser identicado por una a variable llamada nombre_logico, y corresponder a un archivo en el disco duro llamado a nombre_fisico.dat. Naturalmente, el identicador nombre_logico puede ser cualquier nombre de variable vlido para C++, y nombre_fisico.dat puede ser cualquier nombre de a archivo vlido para el sistema operativo. En particular, se pueden tambin dar nombres que a e incluyan paths absolutos o relativos: ofstream nombre_logico_1("/home/vmunoz/temp/nombre_fisico.dat"); ofstream nombre_logico_2("../nombre_fisico.dat"); Las l neas tercera y sexta de main env an a nombre_logico (es decir, escribe en nombre_fisico.dat), las variables i y j. Observar la analog que existe entre estas operaa 4 ciones y las que env la misma informacin a pantalla. Si ejecutamos el programa y en an o el teclado ingresamos el nmero 8, al nalizar la ejecucin el archivo nombre_fisico.dat u o tendr los dos nmeros escritos: a u 3 8 Finalmente, el archivo creado debe ser cerrado (nombre_logico.close()). Si esta ultima operacin se omite en el cdigo, no habr errores de compilacin, y el programa se encargar o o a o a de cerrar por s solo los archivos abiertos durante su ejecucin, pero un buen programador o debiera tener cuidado de cerrarlos expl citamente. Por ejemplo, un mismo programa podr a desear utilizar un mismo archivo ms de una vez, o varios programas podr querer acceder a an al mismo archivo, y si no se ha insertado un close en el punto adecuado esto podr provocar a problemas. El archivo indicado al declarar la variable de tipo ofstream tiene modo de escritura, para permitir la salida de datos hacia l. Si no existe un archivo llamado nombre_fisico.dat es e creado; si existe, los contenidos antiguos se pierden y son reemplazados por los nuevos. No
Esta analog no es casual y se entiende con el concepto de clases (Sec. 2.8). fstream e iostream denen a clases que heredan sus propiedades de un objeto abstracto base, comn a ambas, y que en el caso de iostream u se concreta en la salida estndar pantalla, y en el de fstream en un archivo. a
4

86

CAP ITULO 2. UNA BREVE INTRODUCCION A C++.

siempre deseamos este comportamiento. A veces deseamos agregar la salida de un programa a un archivo de texto ya existente. En ese caso la declaracin del archivo es diferente, para o crear el archivo en modo append: #include <iostream.h> #include <fstream.h> int main(){ ofstream nombre_logico("nombre_fisico.dat",ios::app); int i = 3; nombre_logico << i << endl; nombre_logico.close(); } Si ejecutamos este programa y el archivo nombre_fisico.dat no existe, ser creado. El a resultado ser un archivo con el nmero 3 en l. Al ejecutarlo por segunda vez, los datos se a u e ponen a continuacin de los ya existentes, resultando el archivo con el contenido: o 3 3 La l nea del tipo ofstream a("b") es equivalente a una del tipo int i=3, declarando una variable (a/i) de un cierto tipo (ofstream/int) y asignndole un valor simultneamente a a "b"/3. Como para los tipos de variables predenidos de C++, es posible separar declaracin o y asignacin para una variable de tipo ofstream: o ofstream a; a.open("b"); es equivalente a ofstream a("b"). Esto tiene la ventaja de que podr amos usar el mismo nombre lgico para identicar dos archivos f o sicos distintos, usados en distintos momentos del programa: ofstream a; a.open("archivo1.txt"); // Codigo en que "archivo1.txt" es utilizado a.close(); a.open("archivo2.txt"); // Ahora "archivo2.txt" es utilizado a.close(); Observar la necesidad del primer close, que permitir liberar la asociacin de a a un nombre a o f sico dado, y reutilizar la variable lgica en otro momento. o En los ejemplos hemos escrito solamente variables de tipo int en los archivos. Esto por cierto no es restrictivo. Cualquiera de los tipos de variables de C++ float, double, char, etc. se puede enviar a un archivo del mismo modo. Dicho esto, en el resto de esta seccin o seguiremos usando como ejemplo el uso de int.

2.6. MANEJO DE ARCHIVOS

87

2.6.2

Archivos de entrada

Ya sabemos que enviar datos a un archivo es tan fcil como enviarlos a pantalla. Cmo a o hacemos ahora la operacin inversa, de leer datos desde un archivo? Como es de esperar, o es tan fcil como leerlos desde el teclado. Para crear un archivo en modo de lectura, basta a declararlo de tipo ifstream (input le stream). Por ejemplo, si en nombre_logico.dat tenemos los siguientes datos: 3 6 9 12 el siguiente programa, #include <iostream.h> #include <fstream.h> int main(){ ifstream nombre_logico("nombre_fisico.dat"); int i, j,k,l; nombre_logico >> i >> j >> k >> l; cout << i << "," << j << "," << k << "," << l << endl; nombre_logico.close(); } ser equivalente a asignar i=3, j=6, k=9, l=12, y luego enviar los datos a pantalla. Observar a que la sintaxis para ingresar datos desde un archivo, nombre_logico >> i, es idntica a e cin >> i, para hacerlo desde el teclado. Al igual que cin, espacios en blanco son equivalentes a cambios de l nea, de modo que el archivo podr haber sido tambin: a e 3 6 9 12 Por cierto, el ingreso de datos desde un archivo se puede hacer con cualquier tcnica, por e ejemplo, usando un for: ifstream nombre_logico("nombre_fisico.dat"); int i; for (int j=0;j<10;j++){ nombre_logico >> i; cout << i << ","; } nombre_logico.close(); }

88

CAP ITULO 2. UNA BREVE INTRODUCCION A C++. Como con ofstream, es posible separar declaracin e implementacin: o o

ifstream a; a.open("b"); a.close();

2.6.3

Archivos de entrada y salida

Ocasionalmente nos encontraremos con la necesidad de usar un mismo archivo, en el mismo programa, a veces para escribir datos, y otras veces para leer datos. Por ejemplo, podr amos tener una secuencia de datos en un archivo, leerlos, y de acuerdo al anlisis de esos datos a agregar ms datos a continuacin del mismo archivo, o reemplazar los datos ya existentes con a o otros. Necesitamos entonces un tipo de variable exible, que pueda ser usado como entrada y salida. Ese tipo es fstream. Todo lo que hemos dicho para ofstream y ifstream por separado es cierto simultnemente para fstream.5 Para especicar si el archivo debe ser a abierto en modo de escritura o lectura, open contiene el argumento ios::out o ios::in, respectivamente. Por ejemplo, el siguiente cdigo escribe el nmero 4 en un archivo, y luego o u lo lee desde el mismo archivo: #include <iostream.h> #include <fstream.h> int main(){ fstream nombre_logico; nombre_logico.open("nombre_fisico.dat",ios::out); int i = 4,j; nombre_logico << i << endl; nombre_logico.close(); nombre_logico.open("nombre_fisico.dat",ios::in); nombre_logico >> j; j = j++; cout << j << endl; nombre_logico.close(); } Las dos primeras l neas de main separan declaracin y asignacin, y son equivalentes o o a fstream nombre_logico("nombre_fisico.dat",ios::out);, pero lo hemos escrito as para hacer evidente la simetr entre el uso del archivo como salida primero y como entrada a despus. e De lo anterior, se deduce que:
Nuevamente, este hecho se debe al concepto de clases que subyace a las deniciones de estos tres tipos de variables; fstream es una clase derivada a la vez de ofstream y de ifstream, heredando las propiedades de ambas.
5

2.7. MAIN COMO FUNCION fstream archivo_salida("salida.dat",ios::out); fstream archivo_entrada("entrada.dat",ios::in); es equivalente a ofstream archivo_salida("salida.dat"); ifstream archivo_entrada("entrada.dat");

89

2.7

main como funcin o

Para ejecutar un programa compilado en C++, escribimos su nombre en el prompt: user@host:~/$ programa Si el mismo usuario desea ejecutar alguno de los comandos del sistema operativo, debe hacer lo mismo: user@host:~/$ ls Sin embargo, ls es en realidad el nombre de un archivo ejecutable en el directorio /bin, de modo que en realidad no hay diferencias entre nuestro programa y un comando del sistema operativo en ese sentido. Sin embargo, stos pueden recibir argumentos y opciones. Por e ejemplo, para ver todos los archivos que comienzan con l en el directorio local basta con darle a ls el argumento l*: ls l*. Si queremos ordenar los archivos en orden inverso de modicacin, basta dar otro argumento, en forma de opcin: ls -tr l*. Se ve entonces que o o los argumentos de un archivo ejecutable permiten modicar el comportamiento del programa de modos espec cos. Es posible hacer lo mismo con archivos ejecutables hechos por el usuario? La respuesta es s y para eso se usan los argumentos del main. Recordemos que main es una funcin, , o pero hasta el momento no hemos aprovechado esa caracter stica. Simplemente sabemos que el programa empieza a ejecutarse en la lnea donde est la funcin main. Adems, siempre a o a hemos escrito esa l nea como main(). Sin embargo, main, como cualquier funcin, es capaz de o aceptar argumentos. Espec camente, acepta dos argumentos, el primero es un entero (que cuenta el nmero de argumentos que main recibi), y el segundo es un puntero a un arreglo u o de caracteres (que contiene los distintos argumentos, en forma de cadenas de caracteres, que se le entregaron. Por ejemplo: #include <iostream.h> main( int argc, char * argv[]) { for(int i = 0; i < argc; i++) cout << argv[i] << endl ; } Si llamamos a este programa argumentos, obtenemos distintas salidas al llamarlo con distintos argumentos:

90

CAP ITULO 2. UNA BREVE INTRODUCCION A C++.

user@host:~/$ argumentos argumentos user@host:~/$ argumentos ap k argumentos ap k user@host:~/$ argumentos -t -s arg1 argumentos -t -s arg1 Observar que el primer argumento del programa es siempre el nombre del propio programa. Naturalmente, ste es un ejemplo muy simple. Es tarea del programador decidir cmo e o manejar cada una de las opciones o argumentos que se le entregan al programa desde la l nea de comandos, escribiendo el cdigo correspondiente. o Un segundo aspecto con el cual no hemos sido sistemticos es que main, como toda a funcin, tiene un tipo de retorno. En el caso de main, ese tipo debe ser int. Este int es o entregado al sistema operativo, y puede servir para determinar si el programa se ejecut con o normalidad o si ocurri algo anormal. Podr o amos hacer ese valor de retorno igual a 0 o 1, respectivamente. As la siguiente estructura es correcta: , int main(){ // Codigo return 0; } En este caso, el programa entrega siempre el valor 0 al sistema operativo. Los cdigos del tipo: o main(){ // Codigo } o void main(){ // Codigo } tambin compilan, pero el compilador emite una advertencia si es llamado con la opcin e o -Wall (Warning all ). En el primer caso, la advertencia es: warning: ANSI C++ forbids declaration main with no type En el segundo: return type for main changed to int En general, siempre es conveniente compilar con la opcin -Wall, para lograr que nuestro o cdigo est realmente correcto (g++ -Wall <archivo>.cc -o <archivo>). o e

2.8. CLASES.

91

2.8

Clases.

C++ dispone de una serie de tipos de variables con los cuales nos est permitido operar: a int, double, char, etc. Creamos variables de estos tipos y luego podemos operar con ellos: int x = y = int x, y; 3; 6; z = x + y;

No hay, sin embargo, en C++, una estructura predenida que corresponda a nmeros u complejos, vectores de dimensin n o matrices, por ejemplo. Y sin embargo, nos agradar o a disponer de nmeros complejos que pudiramos denir como u e z = (3,5); w = (6,8); y que tuvieran sentido las expresiones a b c d e f = = = = = = z + w; z * w; z / w; z + 3; modulo(z); sqrt(z);

Todas estas expresiones son completamente naturales desde el punto de vista matemtico, a y ser bueno que el lenguaje las entendiera. Esto es imposible en el estado actual, pues, a por ejemplo, el signo + es un operador que espera a ambos lados suyos un nmero. Sumar u cualquier cosa con cualquier cosa no signica nada necesariamente, as que slo est permitido o a operar con nmeros. Pero los humanos sabemos que los complejos son nmeros. Cmo u u o dec rselo al computador? Cmo convencerlo de que sumar vectores o matrices es tambin o e posible matemticamente, y que el mismo signo + deber servir para todas estas operaciones? a a La respuesta es: a travs del concepto de clases. Lo que debemos hacer es denir una clase e de nmeros complejos. Llammosla Complejo. Una vez denida correctamente, Complejo u e ser un tipo ms de variable que el compilador reconocer, igual que int, double, char, etc. a a a Y ser tan fcil operar con los Complejos como con todos los tipos de variables preexistentes. a a Esta facilidad es la base de la extensibilidad de que es capaz C++, y por tanto de todas las propiedades que lo convierten en un lenguaje muy poderoso. Las clases responden a la necesidad del programador de construir objetos o tipos de datos que respondan a sus necesidades. Si necesitamos trabajar con vectores de 5 coordenadas, ser natural denir una clase que corresponda a vectores con 5 coordenadas; si se trata de a un programa de administracin de personal, la clase puede corresponder a un empleado, con o sus datos personales como elementos. Si bien es cierto uno puede trabajar con clases en el contexto de orientacin al procedio miento, las clases muestran con mayor propiedad su potencial con la orientacin al objeto, o donde cada objeto corresponde a una clase. Por ejemplo, para efectuar una aplicacin para o Windows, la ventana principal, las ventanas de los archivos abiertos, la barra de men, las u cajas de dilogo, los botones, etc., cada uno de estos objetos estar asociado a una clase. a a

92

CAP ITULO 2. UNA BREVE INTRODUCCION A C++.

2.8.1

Denicin. o

Digamos que queremos una clase para representar los empleados de una empresa. Llammosla e Persona. La convencin aceptada es que los nombres de las clases comiencen con mayscula. o u Esto es porque las clases, recordemos, correspondern a tipos de variables tan vlidos como los a a internos de C++ (int, char, etc.). Al usar nombres con mayscula distiguimos visualmente u los nombres de un tipo de variable interno y uno denido por el usuario. La estructura m nima de la denicin de la clase Persona es: o class Persona { }; Todas las caracter sticas de la clase se denen entre los parentsis cursivos. e

2.8.2

Miembros.

Se denomina miembros de una clase a todas las variables y funciones declaradas dentro de una clase. Por ejemplo, para personas, es natural caracterizarlas por su nombre y su edad. Y si se trata de empleados de una empresa, es natural tambin tener una funcin que entregue e o su sueldo: class Persona { char nombre[20]; int edad; double sueldo(); } Los miembros de una clase pueden tener cualquier nombre, excepto el nombre de la propia clase dentro de la cual se denen, ese nombre est reservado. a

2.8.3

Miembros p blicos y privados. u

Una clase distingue informacin (datos o funciones) privada (accesible slo a otros miembros o o de la misma clase) y pblica (accesible a funciones externas a la clase). La parte privau da corresponde a la estructura interna de la clase, y la parte pblica a la implementacin u o (t picamente funciones), que permite la interaccin de la clase con el exterior. o Consideremos ahora nuestro deseo de tener una clase que represente nmeros complejos. u Un nmero complejo tiene dos nmeros reales (parte real e imaginaria), y sos son elementos u u e privados, es decir, parte de su estructura interna. Sin embargo, nos gustar poder modicar a y conocer esas cantidades. Eso slo puede hacerse a travs de funciones pblicas. o e u class Complejo { private:

2.8. CLASES. double real, imaginaria; public: void setreal(double); void setimag(double); double getreal(); double getimag(); };

93

En este ejemplo, los miembros privados son slo variables, y los miembros pblicos son slo o u o funciones. Este es el caso t pico, pero puede haber variables y funciones de ambos tipos.

2.8.4

Operador de seleccin (.). o

Hemos denido una clase de nmeros complejos y funciones que nos permiten conocer y u modicar las partes real e imaginaria. Cmo se usan estos elementos? Consideremos el o siguiente programa de ejemplo: class Complejo { private: double real, imaginaria; public: void setreal(double); void setimag(double); double getreal(); double getimag(); }; void main() { Complejo z, w; z.setreal(3); z.setimag(2.8); w.setreal(1.5); w.setimag(5); cout << "El primer numero complejo es: " << z.getreal() << " + i*" << z.getimag() << endl; cout << "El segundo es: " << w.getreal() << " + i*" << z.getimag() << endl; } Vemos en la primera l nea de main cmo la clase Complejo se usa del mismo modo que o usar amos int o double. Ahora Complejo es un tipo de variable tan vlido como los tipos a predenidos por C++. Una vez denida la variable, el operador de seleccin (.) permite o acceder a las funciones pblicas correspondientes a la clase Complejo, aplicadas a la variable u

94

CAP ITULO 2. UNA BREVE INTRODUCCION A C++.

particular que nos interesa: z.setreal(3) pone en la parte real del Complejo z el nmero u 3, y w.setreal(1.5) hace lo propio con w.

2.8.5

Implementacin de funciones miembros. o

Ya sabemos cmo declarar funciones miembros en el interior de la clase y cmo usarlas. o o Ahora veamos cmo se implementan. o void Complejo::setreal(double x) { real = x; } void Complejo::setimag(double x) { imaginaria = x; } double Complejo::getreal() { return real; } double Complejo::getimag() { return imaginaria; } Como toda funcin, primero va el tipo de la funcin (void o double en los ejemplos), luego o o el nombre de la funcin y los argumentos. Finalmente la implementacin. Lo diferente es o o que el nombre va precedido del nombre de la clase y el operador :: .

2.8.6

Constructor.

Al declarar una variable, el programa crea el espacio de memoria suciente para alojarla. Cuando se trata de variables de tipos predenidos en C++ esto no es problema, pero cuando son tipos denidos por el usuario C++ debe saber cmo construir ese espacio. La funcin o o que realiza esa tarea se denomina constructor. El constructor es una funcin pblica de la clase, que tiene el mismo nombre que ella. o u Agreguemos un constructor a la clase Complejo: class Complejo { private: double real,imaginaria; public:

2.8. CLASES. Complejo(double,double); void setreal(double); void setimag(double); double getreal(); double getimag(); }; Complejo::Complejo (double x, double y) : real(x), imaginaria(y) {}

95

Denir el constructor de esta manera nos permite crear en nuestro programa variables de tipo Complejo y asignarles valores sin usar setreal() o setimag(): Complejo z (2, 3.8); Complejo w = Complejo(6.8, -3); En el constructor se inicializan las variables internas que nos interesa inicializar al momento de crear un objeto de esta clase. Si una de las variables internas a inicializar es una cadena de caracteres, hay que inicializarla de modo un poco distinto. Por ejemplo, si estamos haciendo una clase Persona que slo tenga el nombre de una persona, entonces podemos denir la clase y su constructor en o la forma: class Persona { private: char nombre[20]; public: Persona(char []); }; Persona::Persona(a[]) { strcpy(nombre,a); } Si uno no especica el constructor de una clase C++ crea uno default, pero en general ser insuciente para cualquier aplicacin realmente prctica. Es una mala costumbre ser a o a descuidado y dejar estas decisiones al computador.

2.8.7

Destructor.

As como es necesario crear espacio de memoria al denir una variable, hay que deshacerse de ese espacio cuando la variable deja de ser necesaria. En otras palabras, la clase necesita tambin un destructor . Si la clase es Complejo, el destructor es una funcin pblica de ella, e o u llamada ~Complejo.

96

CAP ITULO 2. UNA BREVE INTRODUCCION A C++.

class Complejo { private: double real, imaginaria; public: Complejo(double,double); ~Complejo(void); void setreal(double); void setimag(double); double getreal(); double getimag(); }; Complejo::Complejo (double x, double y): real(x), imaginaria(y) { } Complejo::~Complejo(void) { } Como con los constructores, al omitir un destructor C++ genera un default, pero es una mala costumbre . . . , etc.

2.8.8

Matrices de clases.

Una clase es un tipo de variable como cualquier otra de las predenidas en C++. Es posible construir matrices con ellas, del mismo modo que uno tiene matrices de enteros o caracteres. La unica diferencia con las matrices usuales es que no se pueden slo declarar, sino que hay o que inicializarlas simultneamente. Por ejemplo, si queremos crear una matriz que contenga a 2 nmeros complejos, la l u nea Complejo z[2]; es incorrecta, pero s es aceptable la l nea Complejo z[2] = {Complejo(3.5,-0.8), Complejo(-2,4)};

2.9

Sobrecarga.

Para que la denicin de nuevos objetos sea realmente util, hay que ser capaz de hacer o con ellos muchas acciones que nos ser naturales. Como ya comentamos al introducir el an concepto de clase, nos gustar sumar nmeros complejos, y que esa suma utilizara el mismo a u signo + de la suma usual. O extraerles la ra cuadrada, y que la operacin sea tan fcil z o a como escribir sqrt(z). Lo que estamos pidiendo es que el operador + o la funcin sqrt() o sean polimrcos, es decir, que acte de distinto modo segn el tipo de argumento que se o u u

2.9. SOBRECARGA.

97

le entregue. Si z es un real, sqrt(z) calcular la ra de un nmero real; si es complejo, a z u calcular la ra de un nmero complejo. a z u La tcnica de programacin mediante la cual podemos denir funciones polimrcas se e o o llama sobrecarga.

2.9.1

Sobrecarga de funciones.

Digamos que la ra cuadrada de un nmero complejo a + ib es (a/2) + i(b/2). (Es ms z u a complicado en realidad, pero no queremos escribir las frmulas ahora.) o Para sobrecargar la funcin sqrt() de modo que acepte nmeros complejos basta denirla o u as : Complejo sqrt(Complejo z) { return Complejo (z.getreal()/2, z.getimag()/2); } Observemos que denimos una funcin sqrt que acepta argumentos de tipo Complejo, y que o entrega un nmero del mismo tipo. Cuando pidamos la ra de un nmero, el computador u z u se preguntar si el nmero en cuestin es un int, double, float o Complejo, y segn eso a u o u escoger la versin de sqrt que corresponda. a o Con la denicin anterior podemos obtener la ra cuadrada de un nmero complejo o z u simplemente con las instrucciones: Complejo z(1,3); Complejo raiz = sqrt(z);

2.9.2

Sobrecarga de operadores.

Cmo le decimos al computador que el signo + tambin puede aceptar nmeros complejos? o e u La respuesta es fcil, porque para C++ un operador no es sino una funcin, y la accin de a o o sobrecargar que ya vimos sirve en este caso tambin. La sintaxis es: e Complejo operator + (Complejo z, Complejo w) { return Complejo (z.getreal() + w.getreal(), z.getimag() + w.getimag()); }

2.9.3

Coercin. o

Sabemos denir a + b, con a y b complejos. Pero qu pasa si a o b son enteros? O reales? e Pareciera que tendr amos que denir no slo o Complejo operator + (Complejo a, Complejo b); sino tambin todas las combinaciones restantes: e

98

CAP ITULO 2. UNA BREVE INTRODUCCION A C++.

Complejo operator + (Complejo a, int b); Complejo operator + (Complejo a, float b); Complejo operator + (int a, Complejo b); etctera. e En realidad esto no es necesario. Por cierto, un nmero real es un nmero complejo con u u parte imaginaria nula, y es posible hacerle saber esto a C++, usando la posibilidad de denir funciones con parmetros default. Basta declarar (en el interior de la clase) el constructor a de los nmeros complejos como u Complejo (double, double = 0); Esto permite denir un nmero complejo con la instruccin: u o Complejo c = Complejo(3.5); resultando el nmero complejo 3.5 + i 0. Y si tenemos una l u nea del tipo: Complejo c = Complejo(3,2.8) + 5; el computador convertir impl a citamente el entero 5 a Complejo (sabe cmo hacerlo porque o el constructor de nmeros complejos acepta tambin un solo argumento en vez de dos), y u e luego realizar la suma entre dos complejos, que es entonces la unica que es necesario denir. a

2.10

Herencia.

Herencia es el mecanismo mediante el cual es posible denir clases a partir de otras, preservando parte de las propiedades de la primera y agregando o modicando otras. Por ejemplo, si denimos la clase Persona, toda Persona tendr una variable miembro a que sea su nombre. Si denimos una clase Hombre, tambin ser Persona, y por tanto e a deber tener nombre. Pero adems puede tener esposa. Y ciertamente no toda Persona a a tiene esposa. Slo un Hombre. o C++ provee mecanismos para implementar estas relaciones lgicas y poder denir una o clase Hombre a partir de Persona. Lo vemos en el siguiente ejemplo: class Persona { private: char nombre[20]; public: Persona(char [] = ""); ~Persona(void); char getname(); } class Hombre : public Persona {

2.10. HERENCIA. private: char esposa[20]; public: Hombre(char a[]) : Persona(a) { }; char getwife(); void setwife(); }

99

Primero denimos una clase Persona que tiene nombre. Luego denimos una clase Hombre a partir de Persona (con la l nea class Hombre : public Persona). Esto permite de modo automtico que Hombre tenga tambin una variable nombre. Y nalmente, dentro de la clase a e Hombre, se denen todas aquellas caracter sticas adicionales que una Persona no tiene pero un Hombre s esposa, y funciones miembros para modicar y obtener el nombre de ella. : Un ejemplo de uso de estas dos clases: Persona cocinera("Maria"); Hombre panadero("Claudio"); panadero.setwife("Estela"); cout << cocinera.getname() << endl; cout << panadero.getname() << endl; cout << panadero.getwife() << endl; Observemos que panadero tambin tiene una funcin getname(), a pesar de que la clase e o Hombre no la dene expl citamente. Esta funcin se ha heredado de la clase de la cual Hombre o se ha derivado, Persona.

También podría gustarte