Академический Документы
Профессиональный Документы
Культура Документы
CONTENIDO
Prefacio Captulo 1 Fundamentos del lenguaje. 1.1 Introduccin al lenguaje y a su entorno de desarrollo... 1.2 Comentarios..... 1.3 Variables y constantes... 1.4 Objetos que permiten E/S por consola.... 1.5 Operadores...... 1.6 Tipos de datos..... 1.6.1 Fundamentales. 1.6.2 Definidos por el usuario..... 1.7 Palabras reservadas... 1.8 Expresiones.. 1.9 Estructuras de control..... 1.9.1 Asignacin. 1.9.2 Seleccin....... 1.9.3 Iteracin. Captulo 2 Subprogramas... 2.1 Definicin de un subprograma.. 2.1.1 Estructura de un subprograma.. 2.1.2 Valor de retorno... 2.2 Declaracin de un subprograma... 2.3 Bibliotecas o librera de subprogramas... 2.4 Primer acercamiento a clases y objetos 2.5 mbito y tiempo de vida de variables.. 2.6 Argumentos y paso de parmetros.. 2.7 Sobrecarga de subprogramas.. 2.8 Recursividad....... Captulo 3 Punteros, referencias y arreglos.. 3.1 Creacin.... 3.2 Operaciones con punteros..... 3.3 Referencias.. 3.4 Arreglos unidimensionales, bidimensionales y multidimensionales.. 3.5 Cadenas de caracteres...... 3.6 Asignacin dinmica de memoria. 3.7 Uso de clases predefinidas para arreglos... Captulo 4 Clases y Objetos.. 4.1 Definicin de una clase.. 4.2 Declaracin de clases.... 4.3 Miembros de una clase.. 4.4 mbito referente a una clase. 4.5 Especificadores de acceso.... 4.6 Creacin de objetos.... 4.7 Puntero this...... 4.8 Constructores y destructores........ iv 1 1 5 8 15 17 21 21 27 33 35 35 36 36 51 58 58 59 60 62 63 67 69 73 77 78 82 82 85 91 94 106 116 129 132 132 132 133 133 134 135 138 142
Captulo 5 Herencia.. 5.1 Importancia de la herencia en la POO. 5.2 Jerarqua de herencia.... 5.2.1 Conceptos de herencia simple y mltiple 5.2.2 Principios generales de diseo de jerarquas. 5.2.3 Especificadores de acceso a jerarqua de clases... 5.3 Definicin de una clase base. 5.4 Definicin de una clase derivada.. 5.4.1 Constructores y destructores de clases derivadas. 5.4.2 Conversin implcita de objetos de clase derivada a objeto de clase base.... 5.5 Herencia mltiple..... Captulo 6 Polimorfismo..... 6.1 Concepto de polimorfismo. 6.2 Clase base abstracta..... 6.3 Subprogramas virtuales. 6.4 Destructores virtuales.... Captulo 7 Archivos.. 7.1 Clases de E/S Clase base abstracta.... 7.2 Realizar entrada y salida de texto. 7.3 Leer y escribir archivos... 7.4 Realizar entrada y salida binaria... Bibliografa.
156 156 156 157 158 159 160 165 166 166 173 179 179 182 186 190 193 193 194 194 203 209
Prefacio
Se intenta que cada programa de ejemplo sea una aplicacin completa y prctica en la medida que el conocimiento adquirido hasta ese punto lo permita, sin que llegue a ser compleja. Tambin se ha tratado de evitar en todo lo posible el tipo de escritura compacta tradicional de los programas en C/C++ para lograr mayor claridad en los ejemplos. Cuando la presentacin de los programas de ejemplo lo permite (no interfiere con la explicacin) siguen una notacin de anteponer la(s) primera(s) letras de los tipos de datos usados, basada un poco en la notacin hngara. Versin de C++ El material presentado aqu, sigue el estndar de programacin de C++ ISO/IEC 98-14882, por lo tanto los ejemplos podrn ser compilados con cualquier compilador que sigua el estndar ISO Internacional as como el ANSI de Estados Unidos. Los programas de ejemplo fueron compilados con Visual C++ 2008 Express Edition en el sistema operativo Windows XP y con el compilador GNU de C/C+ + en el sistema operativo Linux. Que sea sencillo: lo mas sencillo posible, pero no mas- Albert Einstein
Capitulo 1
Fundamentos del lenguaje
El lenguaje C es parte del lenguaje C++, la mayora de las instrucciones de C funcionan en C++, y existen muchas instrucciones equivalentes, en estos casos se ha utilizado las instrucciones de C++ y dejado afuera las de C. Esto no quiere decir que las del Lenguaje C no sean tiles, la capacidad de discernir cuales utilizar en cada caso se obtendr conforme se domine el lenguaje. Los programas de ejemplo siguen la notacin de anteponer la(s) primera(s) letras de los tipos de datos usados, basada un poco en la notacin hngara. Para evitar complejidad innecesaria algunos programas de ejemplo no siguen tal notacin. En C++ el entorno de desarrollo no esta ligado a un solo programa hecho por una sola compaa como en el caso del lenguaje de programacin Visual Basic de Microsoft. Siendo C++ un lenguaje estndar, el entorno de desarrollo depende de la seleccin de entre una variedad de ofertas, as como del sistema operativo que se este utilizando. Independientemente del entorno de desarrollo utilizado, la creacin de programas usando el lenguaje C++ sigue un proceso como se muestra en la figura de la siguiente pagina.
fig1. Diagrama del proceso de creacin de un programa ejecutable usando el lenguaje C++
Editor Un programa usado para escribir el texto del lenguaje, este texto es sin formato. Compilador Programa que checa que el lenguaje sea usado correctamente y lo traduce a un lenguaje objeto cercano a lenguaje mquina. Encadenador Programa que toma programas compilados separadamente y los une en un lenguaje de maquina, los deja listo para ejecutarse. Lenguaje Maquina Instrucciones que la computadora entiende directamente. El proceso llamado preprocesador, compilador y el de encadenador se realizan generalmente invocando un solo programa, por ejemplo en una terminal de Linux usando: g++ programa.cpp g++ realiza los tres procesos y deja un programa ejecutable, llamado por omisin "a.out". En el caso de Windows, en la ventana de comandos utilizando borland C++ por ejemplo es como sigue: bcc programa.cpp Dando como salida el programa ejecutable llamado por omisin igual que el de entrada pero con la extensin exe, en este caso "programa.exe". A estos programas g++ y bcc se les llama por el termino general de "compilador" aunque realicen mas cosas que la compilacin (generalmente estos programas mandan llamar otros programas que realizan cada uno un proceso mostrado en la figura de arriba), adems estos compiladores cuentan con opciones para que solo se ejecute uno de los procesos arriba mencionados porque en ocasiones es til hacerlo de esa manera.
La seleccin de un entorno de desarrollo para C++ depende en gran parte de las necesidades particulares de cada programador, la plataforma de desarrollo y/o la imposicin de este por parte de un empleador, por ejemplo. El entorno de desarrollo puede ser tan escueto como utilizar un editor de texto y un compilador o tan basto como el uso de un entrono de desarrollo integrado, por ejemplo Microsoft Visual C++ en el caso de Windows y Eclipse con el plugin CDT en Windows/Linux.
1.2 Comentarios
Dos diagonales seguidas indican el inicio de un comentario; las diagonales deben estar pegadas sin espacios intermedio. // Primer.cpp // Escribe un mensaje en pantalla Todo lo que esta despus de estos caracteres hasta el fin de la lnea es ignorado por el compilador, se utilizan para poner notas para el programador. Tambin se pueden utilizar los caracteres /* */ para poner comentarios, estos son los usados por el lenguaje C, la ventaja de estos es que pueden ser utilizados para crear bloques de varias lneas, por ejemplo los comentarios anteriores pueden ser lnea por lnea: /* Primer.cpp */ /* Escribe un mensaje en pantalla */ O pueden enmarcar varias lneas: /* Primer.cpp Escribe un mensaje en pantalla */ Los caracteres, diagonal y asterisco deben de ir pegados. El primer programa en C++, el siguiente ejemplo muestra un pequeo programa, que despliega un mensaje en la pantalla, utilizando el objeto cout para realizar salida a la consola. Listado
// Primer.cpp // Escribe un mensaje en pantalla #include <iostream> using namespace std; int main()
Salida
Este es mi primer programa en C++
Nota: El C++ hace distincin entre minsculas y maysculas, por ejemplo: La palabra Main, es diferente de mAin, o main que es la que reconoce C++.
Aunque el programa de ejemplo anterior es bastante pequeo, este incluye elementos clave que conforman la mayora de los programas en C++. // Primer.cpp // Escribe un mensaje en pantalla Es recomendado incluir como comentarios al inicio de cada programa fuente el nombre del programa as como una breve descripcin de lo que hace. #include <iostream> #include se utiliza para especificar la inclusin de un archivo de texto, el nombre del archivo se pone entre los smbolos menor que y mayor que. En este caso el nombre del archivo de texto es iostream el cual contiene definiciones para realizar entrada y salida de datos por ejemplo leer del teclado y desplegar en pantalla- entre otras cosas. using namespace std; Se asocia, el namespace std para todo el programa e indica que el programa va a utilizar el conjunto de variables globales definidos en el espacio de nombres estndar.
10
Los espacios entre las palabras se utilizan para separar las instrucciones, las lneas en blanco son ignoradas por el compilador y se utilizan para darle legibilidad al cdigo. int main() main indica donde empieza la ejecucin del programa, int es un tipo de datos (esto se discute mas adelante) e indica el tipo de valor que el programa va a regresar al proceso que lo ejecute. { La llave que abre indica que inicia la ejecucin de un bloque de cdigo } La llave que cierra termina el bloque y ambas { y } enmarcan el cdigo que integra la funcin. cout << "Este es mi primer programa en C++"; Imprime en la consola lo que esta entre comillas "Este es mi primer programa en C++". return 0; return regresa el valor indicado al proceso que lo mando llamar, en este caso un cero y probablemente al Sistema Operativo. Nota: Si esta usando el Sistema Operativo Windows y un IDE (Integrated Development Environment/Entorno de Desarrollo Integrado) como por ejemplo Visual C++ o Dev-C++, puede detener la ejecucin del programa para evitar que se cierre la ventana de comandos (msdos), utilizando:
system("PAUSE > NUL");
11
De lo contrario al terminar la ejecucin regrese al IDE como si no hubiera realizado nada el programa. Nota (continua de la pgina anterior):
#include <iostream> using namespace std; int main() { cout << "Este es mi primer programa en C++"; //para pasusa si se ejecuta en un IDE como dev-c++ o vc++ por ej. system("PAUSE > NUL"); return 0; }
system() Es una funcin que permite ejecutar comandos externos. PAUSE es un comando batch del msdos- para detener la ejecucin de un script, la sentencia: PAUSE > NUL redireccina la salida del comando PAUSE a nulo, -la salida de este comando es un mensaje. Los programas de ejemplo no incluyen tal sentencia porque un Entorno de Desarrollo Integrado solo se utiliza cuando se esta desarrollando el programa, cuando el programa (o programas segn sea el caso) esta terminado, este se ejecuta fuera del Entorno de Desarrollo Integrado, por lo tanto tal sentencia dejara de ser til o peor, entorpecera la funcin del programa terminado.
12
Una declaracin especifica un tipo y le sigue una lista de una o ms variables de ese tipo. Todas las variables deben de ser declaradas antes de utilizarlas. Ejemplo: int a, b; char c; Al declarar una variable tambin se le pude asignar un valor. Ejemplo: int i = 0; float x = 3.4; Listado
// variables.cpp // Declaracion de variables #include <iostream> using namespace std; int main() { int n, c; c = 2; n = c * c; cout << "El cuadrado de 2 es " << n; return 0; }
Salida
El cuadrado de 2 es 4
Nota:
13
Un programa es perfectamente legal para el compilador si se escribe en una sola lnea, por Ejemplo: int main() { cout << "Hola"; return 0; } Nota (continua de la pgina anterior): Escribir los programas en varias lneas y con la sangra adecuada mejora la legibilidad de los mismos, algo trivial en programas pequeos pero de de suma importancia en programas reales donde el numero de lneas suman docenas si no es que cientos (Miles en ocasiones). Los programas de ejemplo de aqu en adelante llevan la llave que abre { en la misma lnea del main() principalmente para ahorrar una lnea de espacio y mostrar mas lneas de cdigo en una pantalla de edicin, sin sacrificar legibilidad ya que el int main() indica claramente el inicio del programa (y el nombre de la funcin, estructura, etc., en los ejemplos posteriores).
Ejercicios Declare una variable para cada inciso que pueda ser usada para almacenar el valor mencionado a) 3.1416 b) -554 c) 30000 d) Resultado de 10 * 3 e) Resultado de 10/3 f) 'a' g) "a" h) "HOLA" i) Resultado de 'a' + 'b' j) Resultado de "a" + "b"
Constantes
14
Son los valores fijos que el programa no puede alterar, en C++ las constantes tienen un tipo de dato (ver tipos de datos mas adelante) por ejemplo las constantes de carcter, las cuales se encierran entre apstrofes.
15
Ejemplo: 'a', '%' Distngase que son diferentes: 'a' y "a" 'a' es una constante de carcter compuesta por la letra a y "a" es una cadena de caracteres compuesta por la letra a y el carcter nulo y esta formada de dos elementos. "" es una cadena nula con un elemento (\0). Ejemplo: 123L constante long 123.3 constante float Un cero que encabeza un nmero significa octal Ejemplo: 011 9 en decimal Un cero y una equis antes significa hexadecimal. Ejemplo: 0xFF 255 en hexadecimal. Constantes de carcter no imprimibles Cdigo \b \f \n \r \t \ \ \0 \\ \v \a Significado Retroceso Alimentacin de hoja Nueva lnea Retorno de carro Tabulador horizontal Doble comilla Comilla simple Nulo Barra invertida Tabulador vertical Alerta
(c) copyright 2008-2012 Rogelio Cesar Rodriguez Cervantes 16
\o \x
Una constante de carcter no imprimible no tiene una representacin grfica solo realiza una accin. Ejemplo: cout << \n; No imprime nada, solo brinca de lnea, es como si presionramos la tecla ENTER/RETORNO. Los dos caracteres \n son interpretados como un solo carcter. Ejemplo: Para desplegar un mensaje con una parte entre comillas por ejemplo: Referencia: C++ Simplificado-pagina 123, se necesita utilizar el carcter no imprimible \ cout << Referencia: \C++ Simplificado\-pagina 123; Si no se antepone la diagonal invertida a las comillas el string termina en las segundas comillas y las terceras comillas produciran un error. Listado
// caracter.cpp // Formato de texto en la salida estndar // utilizando las constantes de carcter no imprimibles #include <iostream> using namespace std; int main(){ cout << "CARACTERES\t\t"; cout << "NO IMPRIMIBLES"; cout << endl << "Esta linea esta en otro renglon"; cout << "\n\n\n\n\nEsta despues de cuatro renglones";
17
return 0; }
Salida
CARACTERES NO IMPRIMIBLES Esta linea esta en otro rengln
cout << "CARACTERES\t\t"; La secuencia de caracteres \t, indica imprimir un TAB, es como si se presionara la tecla TAB. cout << endl << "Esta linea esta en otro renglon"; La palabra endl despus de los caracteres << indica un salto de lnea antes de imprimir el mensaje. cout << "\n\n\n\n\nEsta despues de cuatro renglones"; El endl se utiliza para cambiar al siguiente rengln, pero en ocasiones es mas practico usar el carcter no imprimible \n. Ejercicio Escriba un programa que imprima sus datos personales centrados lo ms posible en pantalla, utilizando las constantes de carcter no imprimibles.
18
Mediante esta construccin al principio de un programa se puede definir un nombre caracteres. Listado
// pascual.cpp // Emulacion del PASCAL mediante el preprocesador #include <iostream> using namespace std; #define PROGRAM int main() #define BEGIN { #define END return 0; } #define WRITE(A) cout << A PROGRAM BEGIN WRITE("Pascal en C++!!!"); END
Salida
Pascal en C++!!!
El preprocesador remplaza todas las apariciones no entre comillas del nombre por su cadena correspondiente. Ejercicio Investigue todas las palabras reservadas del preprocesador, su sintaxis y un ejemplo de cada una. Constantes con nombre Para crear una constante con nombre se utiliza la palabra reservada const al declarar un objeto. Ejemplo:
19
const int numero_constante = 10; const char caracter_constante = X; A las constantes no se les puede asignar valores despus de creadas as que se les asigna al momento de declararlas.
20
Listado
// control.cpp // utilizacin del objeto cout con el endl #include <iostream> using namespace std; int main() { cout << "Ahora vamos a controlar" << endl; cout << "el cambio de linea "; cout << "para dominar" << endl << "los mensajes"; cout << endl << endl << endl << "Y DESPUES EL C++..."; return 0; }
Salida
Ahora vamos a controlar el cambio de linea para dominar los mensajes
Y DESPUES EL C++...";
cout << "Ahora vamos a controlar" << endl; La palabra endl despus de los caracteres << indica un salto de lnea despus de imprimir el mensaje. cout << "para dominar" << endl << "los mensajes"; cout << endl << endl << endl << "Y DESPUES EL C++..."; El endl se puede incluir en cualquier parte despus de los caracteres << y tantas veces como sea necesario. Nota: Las siguientes lneas producen la misma salida: cout << Hola a todos;
21
cout << Hola << a << todos; Objeto cin cin es un el nombre de un objeto que representa la entrada estndar (Console INput/Entrada en la consola), por omisin el teclado de una computadora. Ejemplo: cin >> fDolares Ingresa una entrada de datos de la entrada estndar y lo almacena en la variable fDolares. Los caracteres >> son para simular una flecha la cual indica que lo que sigue va de la entrada estndar hacia una variable. El objeto cin, como la mayora de los objetos cuenta con mtodos (Se estudian en el siguiente captulo) que se pueden llamar. Ejemplo: cout << Presione ENTER para continuar; // Ignora el ultimo ENTER ledo, que puede estar en el buffer de entrada cin.ignore(1, '\n'); cin.get(); // espera por un ENTER
El cdigo anterior detiene la ejecucin hasta que el usuario presione la tecla ENTER.
1.5 Operadores
Lo operadores son utilizados para realizar operaciones matemticas, lgicas y/o de relacin. Operadores aritmticos Operador + Accin Resta y menos unario Suma
22
* / % -++ += -= *= /=
Multiplicacin Divisin Modulo de divisin Decremento en uno Incremento en uno Incremento y asignacin Resta y asignacin Multiplicacin y asignacin Divisin y asignacin
Operadores ++ y -Lo mas comn al utilizar estos operadores es aplicarlos dentro de una sentencia de una sola variable, en este caso no importa el orden del operador, ya sea en forma prefija o pos fija actan de la misma manera, incrementa o decrementa en uno segn sea el caso. Ejemplo: i++; Es equivalente a: i = i + 1; Prefijo y posfijo de los operadores ++, -Los operadores ++ y actan diferente dependiendo de la forma en que se usen, prefija o postfija Ejemplo: n = i++; Es equivalente a: n = i; i = i +1;
n = ++i;
23
Es equivalente a: i = i +1; n = i; Al usar la forma de prefijo la variable se incrementa o decrementa antes de ser usada; en la forma pos fijo la variable primero se usa y despus se incrementa o decrementa segn sea el caso. Listado
// mas_mas.cpp // Desmostracion del operador unario ++ #include <iostream> using namespace std; int main() { int i; i = 10; i++; cout << "i = " << i; cout << "\nSalida en posfija de i++ = " << i++; cout << "\n\nSalida de i = " << i; cout << "\nSalida prefija de ++i = " << ++i; return 0; }
Salida
i = 11 Salida en posfija de i++ = 11 Salida de i = 12 Salida prefija de ++i = 13
24
Operadores relacionales Operador > >= < <= == != Accin mayor que mayor que o igual que menor que menor que o igual que Igual no igual
Nota: El doble signo igual == es la notacin de C++ para "es igual a". Este smbolo se distingue de la condicin de igualdad del simple = empleado en las asignaciones.
Precedencia y orden de evaluacin de operadores Existen reglas de precedencia y orden de evaluacin de operadores para cuando estos se mezclan en una misma sentencia. Operador () [] -> . ! ~ ++ -- - * & sizeof */% +<< >> < <= > >= == != & Asociatividad Izquierda a derecha Derecha a izquierda Izquierda a derecha Izquierda a derecha Izquierda a derecha Izquierda a derecha Izquierda a derecha Izquierda a derecha
25
^ | && || ?: = += -= *= /= ,
Izquierda a derecha Izquierda a derecha Izquierda a derecha Izquierda a derecha Derecha a izquierda Izquierda a derecha Derecha a izquierda
double flotante, nmero con fraccin void indica que nada o indefinido Nota:
Nmero de 64 bits y de rango igual a 1.7+/308 con una precisin en general de 15 dgitos.
Para saber exactamente cuantos bytes estn disponibles para un determinado tipo de dato en el compilador que se este usando, se puede usar la funcin sizeof. Ejemplo: cout << sizeof(char); Imprime el nmero de bytes usados por el tipo de datos char.+
Listado
// condolar.cpp // Demuestra la declaracin de variables // as como la entrada y salida de datos // Convierte dolares a pesos #include <iostream> using namespace std; int main(){ float iPesos, fDolares; cout << endl << "Cuantos dolares desea convertir a pesos? "; cin >> fDolares; iPesos = fDolares * 12.5; cout << "Pesos = " << iPesos; return 0; }
27
float iPesos, fDolares; Se declaran dos variables de tipo punto flotante, para almacenar nmeros con parte fraccionaria, nmeros como 3.1416, 2.5 y 1234, este ltimo es un nmero entero pero es almacenado como 1234.00. Nota: Tambin se puede declarar cada variable en una lnea separada: float iPesos; float fDolares; Para guardar un nmero entero; nmeros sin parte faccionaria como 5, 100 y 15 pero no 3.14 seria: int pesos;
28
Ejercicios 1. Modifique el programa condolar.cpp, para que pida el tipo de cambio. 2. Realice un programa que calcule el rea de un tringulo. 3. Escriba un programa que calcule el rea de un cuadrado.
Modificadores de tipo Excepto para void, los tipos de datos bsicos tienen varios modificadores que los proceden. Se usa un modificador para alterar el significado de un tipo base para encajar con las necesidades diversas mas precisamente. En otras palabras estos modificadores de tipos combinados con los tipos de datos fundamentales dan paso a diferentes tipos de almacenamiento. Modificador de Tipo Signed Unsigned Descripcin Forza al compilador a utilizar un tipo de dato con signo si antes se declar como de tipo unsigned. Se aplica a los tipos de datos enlistados arriba, su efecto es eliminar el signo a el tipo de dato aplicado, por ejemplo, para un tipo de dato int podemos especificar unsigned int en cuyo caso el rango para el tipo de dato int cambia de ser -2147483648 a 2147483647, por ste long Short Volatile nuevo rango: 0 a 4294967295. Un nmero entero de 32 bits de rango igual a -2147483648 a 2147483647. Un nmero de 16 bits de rango igual a -32763 a 32762. Especfica una variable que almacena datos cuyo contenido puede cambiar en cualquier momento sea por la accin del programa como reaccin de la interaccin Auto Register Extern del usuario con el programa. Es lo mismo que si no se usara ningn modificador El compilador procurar almacenar la variable cualificada de este modo en un registro de la CPU. La variable se considera declarada en otro fichero. No se
29
Static
le asignar direccin ni espacio de memoria. Cuando se invoca a una funcin por segunda vez se pierden los valores que las variables locales de la funcin tenan al acabar la anterior llamada. Declarando una variable de este tipo cuando se llama por segunda vez a la subrutina la variable static (esttica) contiene el mismo valor que al acabar la llamada anterior.
Conversiones de tipo Cuando se mezclan constantes y variables de diferentes tipos en una expresin, C++ las convierte en el mismo tipo. El compilador de C++ convertir todos los operandos al tipo del operando mas grande en una operacin segn la base de esta operacin. Reglas de conversin de tipos 1. Todos los char y short int se convierten a int. Todos los float a double 2. Para todo par de operandos, lo siguiente ocurre en secuencia: Si uno de los operandos es long double, el otro se convierte a long double. Si uno de los operandos es double el otro se convierte a double Si uno de los operandos es long, el otro se convierte a long. Si uno de los operandos es unsigned, el otro se convierte a unsigned. Despus de que el compilador aplique estas reglas de conversin, cada par de operandos ser del mismo tipo, y el resultado de la operacin ser del tipo de los operandos. Ejemplo: char c; int i; float f; c = 65;
(c) copyright 2008-2012 Rogelio Cesar Rodriguez Cervantes 30
i = 10; f = i + c; La variable c se convierte a int al hacer la suma i + c, al realizar la asignacin el resultado de la suma se convierte a flota. Construccin cast Se usa para forzar la conversin explcita de tipos de datos. Listado
// lconst.cpp // Ejemplo de constantes literales #include <iostream> using namespace std; int main(){ cout << endl << 1/2; cout << endl << 1.0/2; cout << endl << float(1)/2; return 0; }
Salida
0 0.5 0.5
Nota: En la ltima lnea se usa la construccin cast para forzar la conversin explcita del tipo de dato.
Ejercicio
31
Quite la construccin cast (Sustituya la lnea: cout << endl << float(1)/2; por
cout << endl << 1/2;)
union int_char dosv; En la unin dosv, las 2 variables el int y el char comparten la misma localidad de memoria, el dato int ocupa toda la memoria disponible y el dato char solo una parte. Para acceder a una variable de la union se utiliza el operador de acceso . o > segn sea el caso (-> para punteros, estos sern estudiados en el capitulo 3): Ejemplo: dosv.c = x;
fig 4. Ejemplo de la comparticion de memoria usada por la unin en una maquina de 16 bits.
Uniones annimas Las uniones annimas son aquellas que no tienen etiqueta y no se utilizan para nombrar un objeto. Forma general: union { tipo nombre_de_variable; . . . tipo nombre_de_variable; };
33
Sus miembros pueden ser accedidos directamente en el mbito de su declaracin sin necesidad de usar ninguno de los operadores de acceso x.y o p->y. Listado
// union.cpp // Ejemplo de union con etiqueta y union anonima #include <iostream> using namespace std; int main(){ union { float f; char c; }; union int_char { int i; char c; }; int_char dos_var; c = 'X'; dos_var.c = 'Y'; cout << dos_var.c << "\t" << c << endl; return 0; }
Salida
Y X
34
Enumeraciones Una enumeracin es un conjunto de nombres de constantes enteras las cuales especifican todos los valores legales que debe tener un tipo de variable. Forma General: enum nombre {lista de enumeracin} lista_variables;
35
Typedef Declara identificadores que se pueden utilizar para dar nombres a tipos de datos bsicos o derivados excepto funciones. El identificador se transforma en el equivalente sintctico a una palabra reservada. Forma general: typdef nombre_tipo; Ejemplo: Definicin de la palabra Entero: typedef int Entero; Uso: Entero n;
Estructuras Una estructura es un mtodo lgico de organizar datos y funciones (ver capitulo de funciones). La estructura es un patrn a seguir para crear variables de tipo estructura. Al definir una estructura se crea un tipo de dato definido por el usuario, y este tipo sirve para crear instancias de este tipo de dato. Se puede ver como un conjunto de una o ms variables, posiblemente de tipos diferentes, agrupadas bajo un mismo nombre. Principalmente se usa para organizar datos y esta parte es la que se describe a continuacin. Forma general: struct etiqueta { tipo nombre_variable; tipo nombre_variable; . . tipo nombre_variable; } variables_de_estructura;
36
Listado
// structFecha.cpp // Estructuras #include <iostream> using namespace std; // definicion de la estructura (tipo) struct Fecha { int dd; int mm; int aa; }; int main() { Fecha fecha; // Declaracion de una variable de tipo estructura
cout << "Numero de dia "; cin >> fecha.dd; cout << "Numero de mes "; cin >> fecha.mm; cout << "A#o "; cin >> fecha.aa; cout << fecha.dd << "/" << fecha.mm << "/" << fecha.aa; return 0; }
Salida
Numero de dia 24 Numero de mes 12 A#o 2010 24/12/2010
37
struct Fecha { int dd; int mm; int aa; }; Estas lneas definen la estructura, aqu solo se crea el tipo de dato definido por el usuario llamado Fecha. Fecha fecha; Se declara la variable fecha del tipo Fecha, que es del tipo de estructura definida anteriormente. cin >> fecha.dd; Se ingresa desde la entrada estndar, un valor y se le asigna al elemento entero dd que es parte de la variable fecha.
38
continue default Do double else enum extern float for goto If int long register return short signed sizeof static struct switch typedef union unsigned void volatile while
bool catch class compl const_cast delete dynamic_cast explicit export false friend inline mutable namespace new not not_eq operator or or_eq private protected public reinterpret_cast static_cast template this throw true try typeid typename usin virtual w_char xor xor_eq
1.8 Expresiones
(c) copyright 2008-2012 Rogelio Cesar Rodriguez Cervantes 39
Una expresin es una secuencia de operadores y operandos que especifica un calculo. Una expresin puede dar como resultado un valor y puede originar efectos secundarios (efectos colaterales) (Ellis/Stroustrup 1994) Sintaxis de las expresiones C++ Las expresiones son definidas recursivamente, de forma que las subexpresiones pueden ser anidadas sin ningn lmite formal, aunque quizs el compilador pueda reportar un error de lmite de memoria si no puede compilar una expresin muy compleja.
1.9.1 Asignacin
El hecho que las expresiones sean definidas recursivamente permite por ejemplo la asignacin mltiple. Ejemplo: int x, y, z; x = y = z = 0; Todas las variables se inicializan a cero.
1.9.2 Seleccin
C++ cuenta con dos tipos de tratamiento de condiciones, para condiciones de cierto / verdadero y para condiciones mltiples.
40
if-else
Permite seleccionar la ejecucin de dos bloques de cdigo mediante la evaluacin de una condicin. Forma general 1: if (expresin) bloque de cdigo; Si la expresin es verdadera se ejecuta el bloque de cdigo, si la expresin es falsa no se ejecuta el bloque de cdigo. Forma general 2: if (expresin) bloque de cdigo 1; else bloque de cdigo 2; Si la expresin es verdadera se ejecuta el bloque de cdigo 1, si la expresin es falsa se ejecuta el bloque de cdigo 2. Donde expresin: a) variables de tipo boolean b) operacin con operadores aritmticos, lgicos y/o relacinales Donde bloque de cdigo: a) una sola instruccin b) { varias instrucciones } Listado // adivina.cpp // Ejemplo if..else // juego de adivina el numero que la computadora genera aleatoriamente
41
#include <iostream> #include <ctime> using namespace std; int main() { int n, pc; srand( (unsigned)time( NULL ) ); pc = rand() % 5; cout << "\nAdivina el numero que pienso (0-4) " << endl; cin >> n; if (n == pc) cout << "Adivinaste!"; else cout << endl << "Intenta otra vez"; return 0; } Salida en base a la entrada de ejemplo
Adivina el numero que pienso (0-4) 2 Intenta otra vez
#include <ctime>; ctime es para usar la funcin time() para plantar la semilla de la funcin rand(). srand( (unsigned)time( NULL ) );
42
La funcin srand, se usa para plantar la semilla de los nmeros aleatorios usando el tiempo regresado por la funcin time(), el NULL dentro de time() es para que agarre el tiempo actual. pc = rand() % 5; rand(), genera el nmero aleatorio, el % 5, le dice que genere un numero en un rango de 5 nmeros, desde el 0 hasta el 4. En el resto del cdigo se lee un nmero de la entrada y lo compara con el nmero generado en forma aleatoria, if (n == pc), en caso que la evaluacin sea verdadera despliega Adivinaste! de lo contrario despliega Intenta otra vez. Los bloques a seleccionar mediante la condicin estn compuestos solo por una lnea y mediante el punto y coma el compilador sabe exactamente cual bloque pertenece a la parte del if y cual a la del else. Note que al final de la comprobacin de la condicin del if este no tiene un punto y coma sino hasta el final de la sentencia: if (n == pc) cout << "Adivinaste!"; Igual pasa con la sentencia else, el punto y coma esta al finalizar la lnea que va a ejecutarse en caso de que la condicin hecha en el encabezado del if no se cumpla: else cout << endl << "Intenta otra vez"; En caso de que el bloque a ejecutar al hacerse la seleccin se componga de mas de una lnea de cdigo hay que indicarle al compilador donde empieza este bloque mediante el uso de la llave que abre { y tambin donde termina el bloque de instrucciones utilizando la llave que cierra }.
(c) copyright 2008-2012 Rogelio Cesar Rodriguez Cervantes 43
Ejemplo: if (n == pc) { cout << "Adivinaste!" << endl; cout << "\t\tTienes poderes siquicos?" << endl; } else { cout << endl << "No le atinaste"; cout << endl << "\t\tIntenta otra vez"; } Otras combinaciones pueden ser por ejemplo: if (n == pc) { cout << "Adivinaste!" << endl; cout << "\t\tAcaso tienes poderes siquicos?" << endl; } else cout << endl << "Intenta otra vez"; Tambin: if (n == pc) cout << "Adivinaste!" << endl; else { cout << endl << "No le atinaste"; cout << endl << "\t\tIntenta otra vez"; } Las llaves de inicio y fin de bloque se pueden usar aunque sea solo una sentencia, por ejemplo lo siguiente es perfectamente valido:
44
if (n == pc) { cout << "Adivinaste!" << endl; } else { cout << endl << "Intenta otra vez"; } En el siguiente fragmento de cdigo el else no tiene llaves, por lo tanto el bloque de instrucciones perteneciente al else es de una sola lnea: if (n == pc) { cout << "Adivinaste!" << endl; cout << "\t\tAcaso tienes poderes siquicos?" << endl; } else cout << endl << "No le atinaste"; cout << endl << "\t\tIntenta otra vez"; La ultima lnea en este fragmento de cdigo no pertenece al else, esta lnea siempre se ejecuta no importa si se cumple la condicin del if o no; lo nico que tiene es la sangra y a simple vista pudiera parecer que pertenece al cuerpo del else. En el siguiente ejemplo el bloque de cdigo perteneciente al if es de una sola lnea ya que no tiene llaves: if (n == pc) cout << "Adivinaste!" << endl; cout << "\t\tAcaso tienes poderes siquicos?" << endl; else {
45
cout << endl << "No le atinaste"; cout << endl << "\t\tIntenta otra vez"; } El if se compone realmente por: if (n == pc) cout << "Adivinaste!" << endl; La lnea que le sigue est fuera del if: cout << "\t\tAcaso tienes poderes siquicos?" << endl; El else no forma parte del if porque este termino con el punto y coma, por lo tanto marca un error de sintaxis por el compilador ya que else solo puede ir acompaado de un if. else { cout << endl << "No le atinaste"; cout << endl << "\t\tIntenta otra vez"; } Al mostrar el prrafo de cdigo anterior con sangra se aprecia mas el error (otra razon del porque utilizar sangras) if (n == pc) cout << "Adivinaste!" << endl; cout << "\t\tAcaso tienes poderes siquicos?" << endl; else { cout << endl << "No le atinaste"; cout << endl << "\t\tIntenta otra vez"; } Ejercicios
46
1.
Modifique el programa condolar.cpp, para presentarle al usuario la opcin de convertir de dolares a pesos y de pesos a dlares, realice la operacin seleccionada por el usuario.
2.
Realice un programa que lea un nmero de la entrada estndar y despliegue si el nmero ledo es par o impar, utilice el operador % para obtener el residuo de la divisin, note que el residuo de una divisin entre dos de un nmero par siempre es cero.
Evaluacin de la expresin En C++ la evaluacin de la expresin dentro de los parntesis despus del if, para que sea verdadera debe ser distinta de cero y es falsa cuando es cero. Ejemplo: if (1) cout << Expresion verdadera; // <-- se imprime esto en pantalla if (0) cout << Expresion verdadera; else cout << Expresion falsa; // <-- se imprime esto en pantalla El lenguaje C++ no pone restriccin al tipo de expresiones a solo las que invocan operadores relacionales y lgicos. Todo lo que se requiere es que la expresin evaluada de cero o no cero. Listado
// divide.cpp // Divide el primer numero por el segundo #include <iostream> using namespace std; int main() { float a, b;
47
cout << endl << "Introduce dos numeros enteros: "; cin >> a >> b; if (b) // equivalente a (b != 0) cout << a/b << endl; else cout << "No puedo dividir por cero" << endl; return 0; }
48
if (b) Evala si la variable es distinta de cero (verdadero) o cero (falso) if anidado Se le llama if anidado cuando un bloque de cdigo perteneciente a un if-else contiene otro if o if-else. Listado
// mayor.cpp // compara dos numeros y dice si son iguales o cual es el mayor #include <iostream> using namespace std;
int main(){ int a, b; cout << "\nDame un numero "; cin >> a; cout << "Dame otro numero "; cin >> b; if (a == b) cout << "Los numero son iguales"; else if (a > b) cout << "El numero " << a << " es mayor que " << b; else cout << "El numero " << b << " es mayor que " << a; return 0; }
49
if (a == b) cout << "Los numero son iguales"; else if (a > b) cout << "El numero " << a << " es mayor que " << b; else cout << "El numero " << b << " es mayor que " << a; El segundo if se encuentra dentro del bloque de cdigo perteneciente al else, ambos if tienen la parte del else, cuando esto no sucede se puede prestar a confusiones. Ejemplo: if (a != b) if (a > b) cout << "El numero " << a << " es mayor que " << b; else cout << "El numero " << b << " es mayor que " << a; En esta situacin el ltimo else pertenece al if inmediato anterior. if (a != b) if (a > b) cout << "El numero " << a << " es mayor que " << b; else cout << "numeros iguales ";
50
Aqu aunque el else este alineado con el primer if, este sigue perteneciendo al if inmediato anterior, para lograr que el else pertenezca al primer if se necesita utilizar las llaves de inicio y fin de cdigo. if (a != b) { if (a > b) cout << "El numero " << a << " es mayor que " << b; } else cout << "numeros iguales ";
switch La sentencia switch es usada para decisiones mltiples que comprueba si una expresin iguala uno entre varios valores constantes. Forma General: switch (variable) { case constante1: bloque de cdigo; case constante2: bloque de cdigo; break; . . . default: bloque de cdigo; } Descripcin
51
1. El switch difiere del if porque la primera solo puede comprobar por igualdad, mientras que la expresin condicional del if puede ser de cualquier tipo. 2. No pueden tener dos constantes case con idnticos valores en el mismo switch. Una sentencia switch que esta encerrada por otro switch puede tener constantes case que son las mismas. case Acta como una etiqueta. break Opcional dentro de la sentencia switch. Se usa para terminar la secuencia que esta asociada con cada constante. Si se omite, la ejecucin continuara en las sentencias del siguiente case hasta encontrar un break, un return o el final del switch. default Es opcional y se ejecuta si no se satisface ninguna opcin. Listado
// fechaSwitch.cpp // demostracion del switch #include <iostream> using namespace std; int main(){ int iMes = 9; cout << iMes << " "; switch(iMes) { case 1: cout << "enero"; break; case 2:
52
cout << "febrero"; break; case 3: cout << "marzo"; break; case 4: cout << "abril"; break; case 5: cout << "mayo"; break; case 6: cout << "junio"; break; case 7: cout << "julio"; break; case 8: cout << "agosto"; break; case 9: cout << "septiembre"; break; case 10: cout << "octubre"; break; case 11: cout << "noviembre"; break; case 12: cout << "diciembre"; break; default: cout << " *error*"; } return 0; }
53
Salida
9 septiembre
int iMes = 9; La variable mes es inicializada al momento de declararla, al entrar al switch este compara la variable iMes con las constantes literales en los cases hasta encontrar el case 9: y se ejecuta la lnea debajo de este desplegando septiembre, a continuacin se ejecuta la siguiente lnea conteniendo la instruccin break la cual rompe el switch. En caso que la variable no se encuentre en ninguno de los case, se ejecuta la sentenciaque le sigue a default. Note el uso del break al terminar cada case, esto es necesario para que termine la ejecucin del switch y no continu con los siguientes case, lo cual es til en algunas situaciones, como en el siguiente programa por ejemplo. Listado
// switch_dolar.cpp // Convierte dolares a pesos o viceversa, pide el tipo de cambio // ejemplo del switch con las clasusulas case sin el break #include <iostream> using namespace std; int main(){ char opcion; float dolares, pesos, cambio; cout << "Cual es el tipo de cambio? "; cin >> cambio; cout << "*** M E N U ***" << endl << "ingrese la primera letra de la palabra"
54
<< endl; cout << "D olares a pesos" << endl; cout << "P esos a dolares" << endl; cout << "Opcion? "; cin >> opcion; switch (opcion) { case 'd': case 'D': cout << endl << "Cuantos dolares desea convertir a pesos? "; cin >> dolares; pesos = dolares * cambio; cout << "Pesos = " << pesos; break; case 'p': case 'P': cout << endl << "Cuantos pesos desea convertir a dolares? "; cin >> pesos; dolares = pesos / cambio; cout << "Pesos = " << dolares; break; default: cout << "opcion no implemenrada"; } return 0; }
55
Pesos = 225
56
case 'd': case 'D': cout << endl << "Cuantos dolares desea convertir a pesos? "; cin >> dolares; pesos = dolares * cambio; cout << "Pesos = " << pesos; break; El case d: no contiene un break (ni cdigo) por lo tanto si el usuario del programa ingresa una d minscula entra a este case continua hacia abajo y se ejecuta el cdigo del case con la D mayscula, el cuerpo de este case tiene un break porque no debe ejecutar lo que esta en el siguiente case. Ejercicios
1. 2.
Modifique el programa fechaSwitch.cpp para que pida el mes del teclado Escriba un programa que lea un carcter del teclado y si se trata de un nmero despliegue el nmero con letra, si no es un nmero mencionarlo, utilize la sentencia switch. Discuta la posibilidad de hacer un programa que escriba su nombre completo 100 veces; 10,000 veces?; 9,000,000 de veces?
3.
1.9.3 Iteracin
Las sentencias de repeticin permiten ejecutar una porcin de cdigo varias veces. do-while Hace la comprobacin al final despus de cada pasada a travs del cuerpo del ciclo. Y este se ejecuta al menos una vez. Forma general: do bloque de cdigo; while (expresin);
(c) copyright 2008-2012 Rogelio Cesar Rodriguez Cervantes 57
Listado
// doadivina.cpp // do..while, adivina el numero generado aleatoriamente // Termina cuando el usuario as lo desea #include <iostream> #include <ctime> using namespace std; int main() { int n, pc; char c; srand( (unsigned)time( NULL ) ); pc = rand() % 5; do { cout << "\nAdivina el numero que pienso (0-4) " << endl; cin >> n; if (n == pc) cout << "ADIVINASTE!!!" << endl; else cout << "NO ES!!!" << endl; cout << endl << "Intenta otra vez? (s/n)"; cin >> c; } while (c != 'n'); return 0; }
58
Adivina el numero que pienso (0-4) 3 ADIVINASTE!!! Intenta otra vez? (s/n)n
Este es el ejemplo adivina.cpp presentado en el listado 2.1, pero dentro de un ciclo. do { Inicia el ciclo, lnea 14, en la lnea 22 y 23 pregunta si se quiere continuar intentando. } while (c != 'n'); En la lnea 24, checa lo que se ley en la variable c y la compara, con en caso que sea verdadera la evaluacin el ciclo termina, de lo contrario regresa al do {. Ejercicios
1.
Modifique el programa switch_dolar.cpp, que lee un carcter del teclado y si se trata de un nmero despliega el nmero con letra y si no es un nmero lo menciona para que termine hasta que se ingrese una 'X'.
2. Discuta como se puede mejorar el ejemplo anterior (doadivina.cpp), por ejemplo, que sucede si el usuario adivina el nmero y desea seguir jugando?, realice las modificaciones sugeridas. 3. Hacer un programa que escriba su nombre completo 10,000 veces
while Hace la comprobacin al inicio de cada pasada a travs del cuerpo del ciclo. Si la evaluacin es falsa no se ejecuta ni una sola vez. Forma general:
59
Salida
60
while (i < 255) { Se evala la variable i si es menor a 255 empieza el ciclo y se ejecuta la primera sentencia despus de la llave { as sucesivamente hasta que se encuentra la llave que cierra } al pasar esto, se evala la condicin dentro de los parntesis del while si la i todava es menor a 255 vuelve a ejecutar una por una las instrucciones dentro de las llaves { y }, cuando la variable i sea igual a 255 el ciclo termina. La instruccin char(i), regresa el carcter ASCII correspondiente al entero de la variable i. for Forma general: for (inicializacin; condicin; incremento) bloque de cdigo;
61
Gramaticalmente, los tres componentes de un for son expresiones. Mas comnmente, inicializacin e incremento son asignaciones o llamadas a funcin y condicin es una expresin de relacin. Puede omitirse cualquiera de las tres partes, aunque deben permanecer los puntos y comas. Ejemplo: for (; ;) ; Este es un ciclo infinito; que podramos terminar con un break o con un return. La proposicin for es equivalente a: inicializacin; while (condicin) { bloque de cdigo; incremento; } Listado
// for_prom.cpp // ejemplo del for el promedio de la suma de 5 nmeros #include <iostream> using namespace std; int main() { float fCalificacion, fSuma, fPromedio; int i; cout << "Ingresa calificaciones " << endl; fSuma = 0; for (i = 0; i < 5; i++) { cout << i << ". Calificacion? "; cin >> fCalificacion; fSuma = fSuma + fCalificacion;
62
} fPromedio = fSuma / 5; cout << endl << "El promedio es " << fPromedio; return 0; }
63
for (i = 0; i < 5; i++) { Inicia el ciclo for ejecutndose primero la parte de inicializacin i = 0; despus se evala i < 5; la primer a vez se cumple la condicin y entra al cuerpo del for, en el cuerpo del for (todas las sentencias que estn en medio de la llave que abre { que le sigue al for y la llave que cierra } correspondiente) se pide la calificacin y se acumula en fSuma. Al terminar el cuerpo del for regresa a la lnea 12 e incrementa i++ despus evala i < 5, si la evaluacin es verdadera vuelve al cuerpo del for si no es as termina (ntese que despus de la primera vez que se ejecuta el for, ya no se realiza la parte de inicializacin y el encabezado del for se evala de derecha a izquierda) fPromedio = fSuma / TAM; En la lnea 17 se calcula el promedio y lo despliega en la siguiente lnea. Ejercicios
1.
Modifique programa ejemplo for_prom.cpp para que utilice la iteracin while en vez del for. Modifique el programa ejemplo ascii.cpp para que utilice la iteracin for en vez del while (asciifor.cpp) .
2.
3. Discuta la posibilidad de hacer un programa a) Que almacene 3 nmeros y los despliegue en orden de menor a mayor b) Que almacene 100 nmeros y los despliegue en orden de menor a mayor
64
Capitulo 2
Subprogramas
58
mantenible los programas. La modularidad es la capacidad de dividir el problema en pequeas partes independientes entre si [Hernandez 2002]
59
Las variables definidas dentro de una funcin son variables locales dinmicas (empiezan a existir cuando la funcin es llamada y se destruyen al terminar). Forma general: especificador_tipo nombre_funcion(lista_de_argumentos) { cuerpo de la funcin <return valor> } La lista_de_argumentos es una lista de nombres de variables separados por comas que recibe los valores de los argumentos cuando se llama la funcin. El especificador_de_tipo es el tipo de valor que la funcin devuelve mediante el return.
60
void linea() linea es el nombre de la funcin, y void antes del nombre indica que esta funcin no va a regresar un valor al proceso que la llame, el parntesis que abre y el parntesis que cierra despus del nombre indican que es una funcin, entre estos parntesis se ponen los argumentos de la funcin, en este caso la funcin no necesita argumentos. linea() En la primera lnea de la funcin main se hace el llamado a la funcin definida anteriormente, note que aunque la funcin no recibe parmetros el uso de los parntesis es obligatorio.
61
Nota: La funcin linea() esta definida antes de la funcin main() esto es para que el compilador ya la conozca cuando se haga la llamada dentro de main(), si se escribiera la funcin linea() abajo o en otro archivo, abra que poner la declaracin arriba del main(), ya sea escribindola directamente en el archivo principal o en un archivo de encabezado, mediante un #include.
62
void linea() { int i; for (i = 0; i < 40; i++) cout << "-"; }
63
Estticas Son tambin denominadas libreras-objeto, son colecciones de archivos objeto (compilados) agrupados en un solo archivo con la extensin .lib, .a, etc. junto con uno o varios archivos de cabecera (generalmente con la extensin .h). Los componentes utilizados de estas libreras quedan incluidos en el programa ejecutable. Dinmicas Son las denominadas libreras de enlazado dinmico, es decir los componentes utilizados se agregan cunado el programa se esta ejecutando y permite que la misma librera pueda ser usada por varios ejecutables sin necesidad que se embeba permanentemente en un solo archivo ejecutable. Generalmente conocidas como DLLs, acrnimo de su nombre en ingls ("Dynamic Linked Library"). Estas libreras se utilizan mucho en la programacin para el Sistema Operativo Windows. Listado // tolower.cpp // // Muestra el uso de la funcion tolower que convierte un caracter a minusculas
#include <iostream> using namespace std; int main() { char a, b, c; a = 'R'; b = tolower(a); cout << a << " " << b; cout << endl << "Ingrese una letra en Mayusculas "; cin >> c; b = tolower(c); cout << "Letra en minusculas " << b; return 0; } Salida Rr
64
Ejercicio Modifique el programa doadivina.cpp del cap 1- para que acepte minsculas y maysculas cuando pregunta si desea continuar, utilice la funcin toupper().
Clase string
El tipo de dato string no existe en C++, este se implementa mediante tipo de dato definido por el usuario mediante una clase (esta clase se incluye en una de las libreras del C++ estndar proporcionadas por el compilador). Un string es una clase para declarar objetos y estos objetos contienen bsicamente una secuencia de caracteres. Los programas que usen la clase string deben incluir la sentencia using namespace std y el archivo de encabezado: #include <string> Listado // string1.cpp #include <iostream> #include <string> using namespace std; int main() { string strNombre; cout << "Cual es tu nombre? "; cin >> strNombre; cout << "\nSaludos " << strNombre << endl; cout << "\nTu nombre tiene " << strNombre.length() << " letras"; return 0; } Salida Cual es tu nombre? Batman
65
strNombre.length() Objeto strNombre llamando un metodo, la cual regresa cuantos caracteres tiene el string. Nota: Para ingresar oraciones (palabras con espacios intermedios) usar: getline(cin, str, \n); Donde str es un objeto de tipo string.
Ejercicio
Tome como base el ejemplo string1.cpp para hacer un programa donde declare 3 variables string s1, s2, s3
a) Asigne s1 y s2 con datos proporcionados por el usuario
Nota: Algunos compiladores de C++ incluyen subprogramas que no son parte de las libreras estndar, esto facilita de alguna forma la programacin, pero limita la portabilidad del cdigo a otros compiladores y/o plataformas.
66
Describe un conjunto de objetos los cuales tienen las mismas caractersticas (datos) y el mismo comportamiento (cdigo), una clase es realmente un tipo de dato definido por el usuario, es una extensin del concepto del tipo de dato registro, el cual nos permite organizar diferentes tipos de datos en un solo lugar. La clase nos permite combinar datos y cdigo dentro de un solo paquete. Objeto Un objeto es una instancia de una clase, la clase es el tipo de dato y el objeto es la variable. Un objeto tiene un estado, comportamiento e identidad. Una identidad porque cada objeto pose un nombre (identificador) nico, un estado porque los datos tienen un valor determinado dependiendo del proceso en que se encuentre el objeto, y un comportamiento porque sabe que hacer con esos datos, es decir tiene sus propios algoritmos para procesar sus datos.
67
Listado // claseFecha.cpp // Clases #include <iostream> #include <string> using namespace std; class Fecha { public: int dd; string mm; int aa; }; main() { Fecha fecha; cout << "Dia "; cin >> fecha.dd; cout << "Mes en letra "; cin >> fecha.mm; cout << "A#o "; cin >> fecha.aa; cout << fecha.dd << " de " << fecha.mm << " de " << fecha.aa; }
68
Listado
// alcance.cpp // Ejemplo de alcance de variables y del operador de alcance :: #include <iostream> using namespace std; int a = 0; int main() { int a = 10; cout << a << endl; cout << ::a << endl;
69
Salida
10 0 20
int a = 0; Declara una variable global, para todos los bloques de cdigo que le siguen hacia abajo, en este caso el bloque de main() que empieza y el bloque sin nombre que empieza abajo del segundo cout. int a = 10; Sobrescribe a la variable con el mismo nombre declarada anteriormente antes de empezar la funcin main(). El primer cout despliega el contenido de la variable local a. El segundo cout despliega el contenido de la variable global, utilizando el operador de alcance :: La llave que abre { despus del segundo cout indica el inicio de un bloque de cdigo sin nombre.
int x = 20; Declara una variable local para el bloque que inicio en la lnea anterior, la variable empieza a existir aqu y termina al cerrar el bloque con la llave que cierra }.
El ultimo cout esta como comentario porque marca un error, de variable no encontrada ya que la variable x dejo de existir al terminarse el bloque sin nombre.
70
Espacio de nombres
Por omisin cuando un nuevo tipo de dato es definido este existe globalmente. Lo cual significa que est disponible a cualquier funcin en la aplicacin. Para evitar conflictos entre las diferentes libreras (mdulos de cdigo para ser rehusado), cada librera puede crear su propio bloque de espacio de definicin de datos, utilizando la palabra reservada namespace. namespace Un espacio de nombres definido por la palabra namespace es el alcance en el cual los tipos de datos pueden ser definidos para evitar la sobrecarga del espacio de alcance global. El uso de namespace permite agrupar un conjunto de variables globales dentro de un nombre. Es decir el alcance global se subdivide en espacios de nombres. Forma general: namespace identificador { cuerpo-del-namespace }
using namespace La directiva using seguida del namespace sirve para asociar el nivel en que un identificador esta con cierto namespace, para que los objetos, funciones y variables de ese namespace pueden ser accesibles directamente como si estuvieran definidos en el alcance global. Forma general: using namespace identificador;
Listado
// namespace.cpp #include <iostream> using namespace std; namespace espacio1 { int a = 0;
71
}; namespace espacio2 { int a = 100; }; int main() { cout << "namespace espacio1 " << espacio1::a; cout << "\nnamespace espacio2 " << espacio2::a; return 0; } Salida namespace espacio1 0 namespace espacio2 100
En este ejemplo se utiliza el operador de alcance :: para especificar que espacio de nombre utilizar; para evitar poner el nombre cada vez que se use lo que esta definido en el espacio de nombres se puede utilizar using namespace.
Listado
72
// sqr.cpp // Usa una funcion que recibe y regresa un valor entero #include <iostream> using namespace std; int sqr(int num) { num = num * num; return num; } int main() { int n, x; cout << "Ingresa un numero "; cin >> n; x = sqr(n); cout << endl << "El cuadrado de " << n << " es " << x; return 0; }
int sqr(int num) { El primer int indica que la funcin regresa un valor de tipo entero y el segundo que recibe un entero como argumento. return num; Aqu se regresa el valor contenido en la variable num, el tipo de dato que se regrese con return debe de corresponder al tipo de dato que aparece antes del nombre de la funcin. El return puede aparecer en cualquier parte del cuerpo de la funcin e inmediatamente regresa el control a la lnea de donde fue llamada la funcin, devolviendo el valor indicado despus del
73
return en caso que exista tal valor ya que tambin puede estar solo y en ese caso solamente regresar el control a la parte de donde fue llamada. x = sqr(n); La funcin es llamada con n como argumento y regresa el nmero indicado en el return asignndoselo a la variable x. Ejercicios 1 2 3 Escriba una funcin que reciba un nmero y regrese su valor absoluto. Realice una funcin eleva(n, x) que eleve el valor de n a la potencia x, regrese el numero elevado. Hacer un programa que pruebe las funciones de un modulo compuesto de un archivo de encabezado (.h) y un archivo fuente (.cpp) que contenga las siguientes funciones: - Convierta de centmetros a pulgadas - Convierta de pulgadas a centmetros - Convierta de metros a yardas - Convierta de yardas a metros - Convierta de kilmetros a millas - Convierta de millas a kilmetros
Argumentos por omisin Si la funcin tiene un prototipo los argumentos por omisin se declaran en el prototipo y no en la definicin. Si la funcin no tiene prototipos se pueden declarar en la definicin. Hay que notar que no todos los argumentos deben de estar definidos por omisin; puede ser uno solo pero este solo puede estar al final de la lista de argumentos. Ejemplo: Formato(int x, int y, int z = 0); Formato(int x, int y = 0, int z = 0); Esto es valido, los ltimos argumentos en la lista son por defecto.
74
Formato(int x, int y = 0, int z); Formato(int x = 0, int y, int z = 0); Esto es invlido, una vez que se pone un argumento por omisin en la lista todos los dems que le siguen deben ser por omisin. Listado
// omision.cpp // Argumentos por omision de las funciones // en los prototipos de las funciones de C++ #include <iostream> using namespace std; void muestra(int = 1, float = 2.3, long = 4); int main() { muestra(); muestra(5); muestra(6, 7.8); // Los tres parametros por defecto // Provee el primer parmetro // Provee los primeros dos
muestra(9, 10.11, 12L); // Provee los tres parametros return 0; } void muestra(int pr, float seg, long ter) { cout << "\n Primero = " << pr; cout << ", Segundo = " << seg; cout << ", Tercero = " << ter; }
Salida
Primero = 1, Segundo = 2.3, Tercero = 4 Primero = 5, Segundo = 2.3, Tercero = 4 Primero = 6, Segundo = 7.8, Tercero = 4 Primero = 9, Segundo = 10.11, Tercero = 12
75
void muestra(int = 1, float = 2.3, long = 4); Se declara el prototipo de la funcin con los argumentos por omisin. En el main() se manda llamar la funcin muestra() con diferente nmero de argumentos para que se utilicen los argumentos por omisin.
76
return 0; }
//Funcion sobrecargada para valores int int cuadrado(int y ) { return y*y; } //Funcion sobrecargada para valores double double cuadrado(double y) return y*y; } Salida El cuadrado de 5 es 25 El cuadrado de 1.5 es 2.25 {
2.8 Recursividad
Una funcin es recursiva si una sentencia en el cuerpo de una funcin se llama a si Listado // recursiva.cpp // Usa una funcion normal y una recursiva para sacar el factorial #include <iostream> using namespace std; int factorial(int n) { // no recursiva int i, f = 1; for (i = 1; i <= n; i++) f = f * i; return f; } misma.
77
int factorial_recusiva(int n) { // Funcion recursiva if (n == 0) return 1; else return n * factorial_recusiva(n-1); } int main() { int n; cout << "ingrese un numero "; cin >> n; cout << "El factorial de " << n << " es " << factorial(n) << endl; cout << "El factorial de " << n << " es " << factorial_recusiva(n) << endl; return 0; } Salida en base a la entrada de ejemplo ingrese un numero 5 El factorial de 5 es 120 El factorial de 5 es 120
Funciones inline Las funciones en lnea se declaran anteponiendo la palabra inline al tipo de dato de la funcin, y stas funciones actan como macros; el compilador pone el cdigo que se encuentra dentro de la funcin en la parte donde est el llamado como si fuera escrito ah originalmente, esto hace que el programa sea mas grande pero se evita el tiempo del llamado a la funcin haciendo el programa mas rpido, con la ventaja de tener el cdigo centralizado en una sola parte y con el chequeo de sintaxis de una funcin. Las funciones en lnea que contienen ciclos no son expandidas y generalmente el compilador da una advertencia. Nota:
78
El compilador puede ignorar una declaracin inline; y tambin dependiendo de las optimizaciones que se usen, el compilador puede hacer funciones en lnea que no se declararon como inline.
Listado // enlinea.cpp // funciones en linea // utiliza una funcin "inline" para intercambiar dos valores #include<iostream> #include <ctime> using namespace std; // Las funciones inline (en linea) se expanden donde se llaman inline void cambia(char &a, char &b) { char c; if (rand()%2) { c = a; a = b; b = c; } } void tabs(int n) { int i; for (i = 0; i < n; i++) cout << "\t"; } int main() { int i; char x, y; x = '#'; y = 'O'; srand( (unsigned)time( NULL ) ); for (i = 0; i < 10; i++) { tabs(i); cout << x << endl;
79
Salida
# # O O O O O # # #
for (i = 0; i < 10; i++) { tabs(i); cout << x << endl; cambia(x, y); } Aqu se encuentra la llamada a la funcin cambia y en esta parte se incluye el cdigo de la funcin en lugar del cdigo para llamarla por parte del compilador.
80
Capitulo 3
Punteros, referencias y arreglos
3.1 Creacin
Un puntero es un a variable que almacena una direccin de memoria. Esta direccin es usualmente la localidad de otra variable en memoria. Si una variable contiene la direccin de otra variable, entonces se dice que la primera variable apunta a la segunda. Los punteros son usados para accesar en forma expedita porciones de memoria.
82
El operador & solo se puede aplicar a variables y a elementos de un arreglo. Son ilegales construcciones como: &(x+1) &3 Listado // apunta.cpp // ejemplo de punteros e indireccion // recomendable analisar la salida al mismo tiempo que el codigo #include <iostream> using namespace std; int main() { int n; int *pn; n = 123; cout << endl << "n = 123;"; cout << endl << "valor de n=" << n << " Direccion de n=" << &n << endl; pn = &n; // pn apunta a n (contiene la direccion de n) cout << endl << "pn = &n;"; cout << endl << "Valor de pn=" << pn << " Valor a donde apunta pn=" << *pn << endl; *pn = 321; // se accesa n indirectamente cout << endl << "*pn = 321;"; cout << endl << "valor de n=" << n
83
<< endl; n++; cout << endl << "n++;"; cout << endl << "acceso indirecto al valor de *pn=" << *pn; return 0; }
La salida explica el programa al mostrar los valores, las direcciones de las variables, y como se asigna estas direcciones a la variable de tipo puntero, adems del acceso indirecto que se logra por medio del puntero.
84
fig3. Simplificacin de memoria, en base a una maquina de 16 bits Listado // operap.cpp // operaciones con punteros #include <iostream> using namespace std; int main() { char a, b, *pc1, *pc2; a = 'A'; b = 'B'; pc1 = &a; cout << "pc1 = &a;" << endl;
85
cout << " Acceso indirecto variable \"a\" mediante pc1 " << *pc1 << endl; pc1 = &b; cout << "pc1 = &b;" << endl; cout << " Acceso indirecto variable \"b\" mediante pc1 " << *pc1 << endl; pc2 = pc1; cout << "pc2 = pc1;" << endl; cout << " Acceso indirecto variable \"b\" mediante pc2 " << *pc1 << endl; if (pc1 == pc2) cout << "Los dos punteros apuntan a la misma direccion" else cout << "Los dos punteros apuntan a diferentes direcciones" << endl; pc1 = &a; cout << "pc1 = &a;" << endl; if (pc1 == pc2) cout << "Los dos punteros apuntan a la misma direccion" << endl; else cout << "Los dos punteros apuntan a diferentes direcciones" << endl; // Se incrementa el contenido de donde apunta pc1 cout << endl << "Valor de donde apunta pc1 = " << *pc1 << endl << "(Valor de donde apunta pc1) + 1 = " << char(*pc1 + 1) << endl << "(Valor de donde apunta pc1) + 2 = " << char(*pc1 + 2) << endl; return 0; } << endl;
Salida
(c) copyright 2008-2012 Rogelio Cesar Rodriguez Cervantes 86
pc1 = &a; Acceso indirecto variable "a" mediante pc1 A pc1 = &b; Acceso indirecto variable "b" mediante pc1 B pc2 = pc1; Acceso indirecto variable "b" mediante pc2 B Los dos punteros apuntan a la misma direccion pc1 = &a; Los dos punteros apuntan a diferentes direcciones Valor de donde apunta pc1 = A (Valor de donde apunta pc1) + 1 = B (Valor de donde apunta pc1) + 2 = C
pc1 = &a; Se asigna la direccin de la variable a. char(*pc1 + 1) Incrementa el contenido de donde apunta pc1, el valor de la variable a es A que es 65 en cdigo ASCII, 65 + 1 = 66 y lo despliega como carcter con la conversin a tipo char(). Nota: La aritmtica de punteros se analiza despus de estudiar arreglos, en el siguiente tema.
87
Puntero nulo Un puntero nulo es un puntero que apunta a NULL, es decir apunta a una direccin cero, se dice que es un puntero 0. El puntero NULL no concide con ninguna direccin de variable o funcin. Muy til en manejo de colas, listas, pilas, etc. Punteros a void Es aquel que apunta a un tipo void. Significa que el puntero no apunta a ningn tipo de valor... y es ms o menos genrico, es decir se puede usar para apuntar a diferentes tipos de memoria. (Ms o menos genrico porque no se permite la asignacin de la direccin de una constante o de punteros a constantes, punteros a miembros de una clase, etc) Ejemplo: void *puntero;
88
Ejemplo: float **n; Aqu n no es un puntero a un numero en punto flotante, sino un puntero aun puntero float. Listado // indir_multiple.cpp // Ejemplo de indireccion multiple #include <iostream> using namespace std; int main() { int x, *p, **q; x = 10; p = &x; q = &p; cout << endl << "q=" << q << endl << "*q=" << *q << endl << "**q=" << return 0; } Salida (Dependiente de la ejecucin) q=0xbfff1f40 *q=0xbfff1f44 **q=10 **q;
89
Llamada por punteros En este mtodo, la direccin en memoria del argumento es copiada dentro del argumento de la funcin. La direccin es usada para accesar el valor en memoria de la variable usada en la llamada. Para usar este mtodo se pasa una direccin de memoria como argumento. Los punteros son pasados a la funcin como cualquier otro valor pero declarando los parmetros de tipo apuntador. Listado // cambia.cpp // intercambia dos numeros con una funcion #include <iostream> using namespace std; void cambia(int *x, int *y) { int paso; paso = *x; *x = *y; *y = paso; } int main() { int x = 20, y = 10; cout << "x=" << x << " cambia(&x, &y); cout << "x=" << x << " return 0; } Salida 10 20 y=" << y; y=" << y << endl;
90
Las variables dentro de los parntesis indican que se va a recibir direcciones, al igual void indica que la funcin no va a regresar un valor.
cambia(&x, &y);
El smbolo & antes del nombre de la variable indica que se manda la direccin de la variable.
Punteros a funciones Las funciones se encuentran dentro de la memoria de segmento de cdigo, por lo tanto la direccin de inicio de la funcin se puede asignar a un puntero. Teniendo una funcin: int nombre_funcion(int char *); Se declara un puntero a funcin: int (*puntero_a_funcion) (int, char *) Para inicializar el puntero: puntero_a_funcion = nombre_funcion;
3.3 Referencias
Una referencia es la asociacin de un nombre a un objeto determinado; una referencia es como un puntero constante al que se le aplica una indireccin cada vez que se hace referencia a ste. Es como un puntero porque aloja una direccin y como una variable comn porque no se tiene que usar una notacin especfica como en el caso de los punteros cuando se usa la variable. La variable tipo referencia debe inicializarse al momento de declararse. Listado // referencias.cpp // ejemplo de referencias #include <iostream> using namespace std; int main() { int n = 123; int &rn = n; cout << "n=" << n << endl;
91
cout << "rn=" << rn << endl << endl; rn++; cout << "rn++=" << rn << endl; cout << "n=" << n << endl << endl; n++; cout << "n++=" << n << endl; cout << "rn=" << rn << endl; return 0; }
92
int &rn = n;
Se declara la variable de referencia rn, con el signo & antes de la variable y se hace referencia a la variable n.
rn++;
Se incrementa indirectamente la variable n por medio de la referencia.
Llamada por referencia El argumento de la funcin es inicializado con la direccin en memoria del argumento, la referencia es usada para accesar el valor en memoria de la variable usada en la llamada. Listado // cambia_rf.cpp // Usa una funcion que intercambia los valores de sus argumentos // utilizando paso por Referencia #include <iostream> using namespace std; void cambia(int &a, int &b) { int paso; paso = a; a = b; b = paso; } int main() { int x, y; x = 10; y = 20;
cambia(x, y); cout << endl << "X = " << x; cout << endl << "Y = " << y;
93
return 0; }
Salida X = 20 Y = 10
void cambia(int &a, int &b) { Se declara la funcin con paso por referencia cambia(x, y); Se llama la funcin. Note que en la llamada no se encuentra diferencia entre la referencia. llamada por valor y la de por
94
Declara un arreglo llamado lista con tres elementos desde lista[0] hasta lista[2]. Listado // fecha.cpp // elementos del arreglo accesados fuera de un ciclo #include <iostream> using namespace std; int main() { int fecha[3]; fecha[0] = 1; fecha[1] = 1; // mes // dia
fecha[2] = 2010; // aa cout << "Fecha "; cout << fecha[0] << '/' << fecha[1] << '/' << fecha[2]; return 0; } Salida Fecha 1/1/2010
int fecha[3]; Se declara un arreglo de enteros de tres elementos. fecha[0] = 1; fecha[1] = 1; fecha[2] = 2010; Se le asigna valores al arreglo elemento por elemento.
95
Ejercicio 1. Modifique el ejemplo anterior (fecha.cpp) para que lea la fecha del teclado.
Listado // ordena_c.cpp // Lee un arreglo de enteros y ordena los elementos de mayor a menor #include <iostream> using namespace std; const int TAM = 3; int main() { int iCalificacion[TAM], iCalifTemp; int i, j; cout << "Ingresa calificaciones" << endl; for (i = 0; i < TAM; i++) { cout << "Calificacion " << i << "? "; cin >> iCalificacion[i]; } // Ordena califcaciones en base a la califiacion de mayor a menor for (i = 0; i < TAM; i++) for (j = 0; j < TAM; j++) { // ordena de mayor a menor if (iCalificacion[i] > iCalificacion[j]) { // intercambia la califcacion iCalifTemp = iCalificacion[i]; iCalificacion[i] = iCalificacion[j]; iCalificacion[j] = iCalifTemp; } } cout << endl << "Calificaciones de mayor a menor: " << endl; for (i = 0; i < TAM; i++) cout << iCalificacion[i] << endl;
96
return 0; }
const int TAM=3; Declara una constante de tipo entero, es decir un almacenamiento con el nombre TAM el cual no puede ser cambiado durante la ejecucin del programa. for (i = 0; i < TAM; i++) { cout << "Calificacion " << i << "? "; cin >> iCalificacion[i]; } Se llena el arreglo dentro del ciclo for. for (i = 0; i < TAM; i++) for (j = 0; j < TAM; j++) { // ordena de mayor a menor if (iCalificacion[i] > iCalificacion[j]) { // intercambia la califcacion iCalifTemp = iCalificacion[i]; iCalificacion[i] = iCalificacion[j]; iCalificacion[j] = iCalifTemp; } } En estas lneas el arreglo es ordenado en base al valor mayor. El ordenamiento del arreglo se realiza al comparar el primer elemento del arreglo con todos los dems y si se cumple la condicin, el elemento que es comparado con todos se intercambia, el
97
segundo con todos y as sucesivamente, en este algoritmo el ciclo interno es el que controla el ordenamiento, en este caso moviendo el mayor hacia el primer elemento del arreglo. Ejemplo 1: Ingresa calificaciones Calificacion 0? 1 Calificacion 1? 4 Calificacion 2? 7 Calificaciones de mayor a menor: 7 4 1 Ejemplo 2: Ingresa calificaciones Calificacion 0? 4 Calificacion 1? 1 Calificacion 2? 7 Calificaciones de mayor a menor: 1 4 7 Primera pasada: 417 147 Segunda pasada: 147 147 Tercera pasada: 147 147 Primera pasada: 147 147 Segunda pasada: 147 417 Tercera pasada: 417 741
Este mtodo de ordenamiento es fcil de implementar, pero definitivamente no es el ms eficiente, se puede observar que se pierden ciclos al repetir las comparaciones y redundar el ordenamiento. Se puede optimizar al pasar el control del ordenamiento al primer ciclo haciendo lo siguiente: for (i = 0; i < TAM; i++) for (j = i+1; j < TAM; j++) { if (iCalificacion[i] < iCalificacion[j]) { iCalifTemp = iCalificacion[i]; iCalificacion[i] = iCalificacion[j]; iCalificacion[j] = iCalifTemp; } }
98
Note que se asigna a la variable de control del ciclo interno el valor de la variable de control del ciclo externo mas uno, para no repetir la comparacin, tambin se cambia la condicin de mayor que a menor que. Ejercicios 1. Modifique el ejemplo anterior (ordena_c.cpp) para que ordene el arreglo de menor 2. Realice un programa que a) Pida diez nmeros enteros y los almacene en un arreglo b) Busque el nmero menor y el nmero mayor en el arreglo ledo c) Despliegue el nmero menor y el nmero mayor
99
Comprobacin de contornos El lenguaje C++ no realiza comprobacin de contornos en los arreglos. Si se sobrepasa el final durante una operacin de asignacin, entonces se asignarn valores a otras variables en memoria o memoria usada por el propio cdigo del programa, lo que puede provocar resultados no deseados, o un error que detiene la ejecucin del programa. Las comprobaciones son responsabilidad del programador. Listado // for_out.cpp // Programa que sobrepasa el limite de indexacion #include <iostream> using namespace std; int main() { int lista[10], i; for (i = 0; i < 100; i++) { lista[i] = i; cout << lista[i] << ' '; } return 0; } Salida (depende de la ejecucin, puede marcar errores de memoria y no haber salida) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
Nota: Este programa compilar bien. Y sirve de ejemplo para la comprobacin de los lmites del arreglo.
Arreglos bidimensionales y multidimensionales Los arreglos multidimensionales son arreglos con ms de un ndice, por ejemplo los bidimensionales son arreglos con dos subndices que son usados para
100
representar tablas de valores, con informacin organizada en reglones y columnas. Para accesar un elemento particular de la tabla hay que identificar el rengln del elemento seguido por la columna. Forma general:
tipo nombre[longitud_1][longitud_2] Ejemplo: int matriz[3][2];
Declara un arreglo 3 x 2 llamado matriz con seis elementos desde matriz[0][0] hasta matriz[2][1]. Listado // sumamat.cpp // Suma dos matrices dejando el resultado en una tercera #include <iostream> using namespace std; const int LON = 2; int main(){ int i, j, a[LON][LON], b[LON][LON], c[LON][LON]; cout << "Ingresa la matriz A " << endl; for (i=0; i < LON; i++) for (j=0; j < LON; j++) { cout << "A[" << i << "][" << j << " ] = ? "; cin >> a[i][j]; } cout << endl << "Ingresa la matriz B " << endl;
101
for (i=0; i < LON; i++) for (j=0; j < LON; j++) { cout << "B[" << i << "][" << j << " ] = ? "; cin >> b[i][j]; } // Suma las matrices y deja el resultado en una tercera matriz for (i=0; i < LON; i++) for (j=0; j < LON; j++) c[i][j] = a[i][j] + b[i][j]; cout << endl << "El resultado de la suma de A + B es:" << endl; for (i=0; i < LON; i++) { for (j=0; j < LON; j++) cout << c[i][j] << "\t"; cout << endl; } return 0; }
102
int i, j, a[LON][LON], b[LON][LON], c[LON][LON]; Se declaran las variables a ser usadas, i y j para usarlas como subndices y tres arreglos de dos dimensiones. for (i=0; i < LON; i++) for (j=0; j < LON; j++) { cout << "A[" << i << "][" << j << " ] = ? "; cin >> a[i][j]; } Se usan dos for, el primero para accesar los renglones y el segundo que est dentro del cuerpo del primer for para accesar las columnas. 1 for (i=0; i < LON; i++) 2 3 for (j=0; j < LON; j++) c[i][j] = a[i][j] + b[i][j];
103
Realice un programa que pida un arreglo de 3x3 y sume la diagonal principal, despliegue la suma.
Arreglos de ms de dos dimensiones Un arreglo de enteros de tres dimensiones de 2 x 2 x 2 se declarara de la siguiente manera: int cubo[2][2][2]; Para acceder el primer elemento seria: cubo[0][0][0] = 0; //asigna cero al primer elemento del arreglo tridimensional Para un arreglo de enteros de cuatro dimensiones de 2 x 2 x 2x2 se declarara de la siguiente manera: int cuatridimensional[2][2][2][2]; Para acceder el primer elemento seria: cuatridimensional [0][0][0][0] = 0; //asigna cero al primer elemento del arreglo Este tipo de arreglos son raramente usados ya que aumenta la complejidad del programa y generalmente la solucin del problema se pude replantear para evitar complejidades innecesarias, en caso de que sea inevitable siempre se puede usar este tipo de arreglos.
104
Arreglos y punteros
Cuando se declara un arreglo el compilador debe asignar una direccin base y la cantidad de almacenamiento suficiente para alojar a todos los elementos del arreglo. Los punteros y arreglos se utilizan casi de la misma manera para acceder a la memoria. Nota El nombre de un arreglo es una constante que contiene la direccin del inicio del arreglo tambin llamado puntero fijo y un puntero es una variable cuyos valores son direcciones.
Listado // a_apunta.cpp // Relacion entre arreglos y punteros #include <iostream> using namespace std; int main(){ char cstr1[] = "ARREGLO"; char cstr2[] = "PUNTERO"; char *pc; cout << "cstr1: " << cstr1 << endl; cout << "cstr2: " << cstr2 << endl; pc = cstr1; cout << "\npc: " << pc; pc = cstr2; cout << "\npc: " << pc; return 0; }
105
char cstr1[] = "ARREGLO"; char cstr2[] = "PUNTERO"; Se declaran dos arreglos de caracteres y se inicializan,. char *pc; Se declara la variable de tipo puntero. cout << "cstr1: " << cstr1 << endl; cout << "cstr2: " << cstr2 << endl; Se despliega el contenido de cstr1 y cstr2, (lneas 11 y 12). pc = cstr1; Se asigna la direccin de la cadena de caracteres y en la siguiente lnea se despliega el contenido de cstr1 por medio del puntero pc.
106
Listado // cadena.cpp // Cadenas de caracteres #include <iostream> using namespace std; int main() { char s[16]; char s2[] = {'h', 'o', 'l', 'a', '\0'}; char s3[] = "HOLA A TODOS"; cout << endl << "Ingresa una cadena de caracteres "; // El "cin" por si solo, deja de leer cuando encuentra // un espacio cin.getline(s, 15); // para incluir espacios en la cadena cout << "s=" << s << endl << "s2=" << s2 << endl << "s3=" << s3 << endl; s3[5] = '\0'; cout << "s3=" << s3 << endl; return 0; }
107
char s[16]; Declara un arreglo de caracteres. char s2[] = {'h', 'o', 'l', 'a', '\0'}; Se declara un arreglo de caracteres y se inicializa, este tipo de inicializacin en arreglos solo es permitido al momento de declarar el arreglo. La asignacin del elemento '\0' al final del arreglo es para tratar el arreglo como una cadena de caracteres. Hay que notar que no se indica cuantos elementos tiene el arreglo; como se est inicializando se deja la cantidad fuera de la declaracin para que el compilador incluya la cantidad en s2[]. char s3[] = "HOLA A TODOS"; Se declara un arreglo de caracteres y se inicializa con una cadena de caracteres. Al hacer la inicializacin de esta forma, el compilador incluye adems de la cantidad de elementos en s3[] el carcter \0 al final, este tipo de inicializacin en arreglos solo es permitido al momento de declarar el arreglo. La cadena s3 se vera en memoria as:
s3[5] = '\0'; Se asigna el carcter nulo en el quinto elemento, por lo tanto: La cadena s3 se vera en memoria as:
cout << s3 << endl; Despliega la cadena s3, siendo la salida HOLA , porque el cout se detiene al encontrar el primer carcter '\0' que es el que indica el fin de cadena. Listado // mayusculas.cpp // Convierte una cadena de caracteres a mayusculas #include <iostream> using namespace std;
108
char mensaje[80]; void mayusculas(void) { int i; for (i = 0; mensaje[i] != '\0'; i++) mensaje[i] = toupper(mensaje[i]); } main() { cout << "\Ingresa un mensaje "; cin.getline(mensaje, 79); mayusculas(); cout << "\nEl mensaje en mayusculas es " << mensaje; }
109
Salida
Ingresa un mensaje issac asimov El mensaje en mayusculas es ISSAC ASIMOV
char mensaje[80]; Se declara una cadena de caracteres global para las funciones que estn abajo de sta. void mayusculas(void) { El primer void significa que la funcin no regresa nada, acta como un procedimiento. mayusculas es el nombre de la funcin y void dentro de los parntesis significa que la funcin no recibe parmetros. En el cuerpo de la funcin se convierte cada elemento de la cadena de caracteres a maysculas. mayusculas(); Manda llamar la funcin.
Ejercicios 1. Realice un programa que ingrese y despliegue una cadena de caracteres elemento por elemento. 2. Hacer un programa que lea y almacene una cadena caracteres con un mximo de 80 caracteres y despliegue la cadena en maysculas, investigue el uso de la funcin toupper() (#include <ctype>) 3. Hacer un programa que lea y almacene una cadena caracteres con un mximo de 80 caracteres y despliegue la cadena en orden inverso, recuerde que el carcter nulo \0 indica el fin del texto.
Apuntadores a caracteres
110
Si se declara mensaje como: char *mensaje; La sentencia: mensaje = "esta es una cadena" Asigna a mensaje un apuntador a los caracteres. No es la copia de una cadena. Listado // strlen_punteros.cpp // Ejemplo de como puede estar implementada la funcion de la // biblioteca estandard strlen (implementada usando apuntadores) int strlen(char *s) { char *p = s; while (*p != '\0') p++; return p-s; } Ejercicios 1. Analice como es que la funcin cuenta el nmero de caracteres contenidos en la cadena 2. Implemente la funcin main para probar la funcin
Listado // copia_cadena.cpp // Copia origen a destino usando apuntadores #include <iostream> using namespace std; void copia_cadena(char *destino, char *origen) { do { *destino = *origen; destino++; origen++; } while(*origen != '\0'); *destino = *origen; // copia el caracter '\0' }
111
int main(){ char str1[80], str2[80]; cout << "Ingresa un string "; cin.getline(str1, 79); copia_cadena(str2, str1); cout << str2 << endl; return 0; } Salida en base a la entrada de ejemplo Ingresa un string Bjarne Stroustrup Bjarne Stroustrup
Ejercicio 1. Escriba una funcin str_busca(str, c) que reciba un apuntador a una cadena de caracteres str, y un carcter c. Busque el carcter en la cadena y regrese la posicin en que se encuentra el carcter en la cadena o un -1 si no se encuentra utilizando apuntadores. 2. 3. Realice una funcin concatena(str1, str2) que agregue la cadena de caracteres str2 al final de la cadena str1. Realice una funcin reversa(str) que reciba un apuntador a una cadena de caracteres e invierta la cadena. Utilizando apuntadores.
Aritmtica de punteros Ejemplo: Teniendo: int n[3] = {1, 2, 3}, *p; Las siguientes sentencias son equivalentes; ambas asignan la direccin de inicio del arreglo al puntero p. p = n; p = &n[0];
112
fig5. Simplificacin de memoria, maquina de 16 bits Las siguientes sentencias son equivalentes; ambas asignan la direccin de memoria del segundo elemento al puntero p. p = n + 1; p = &n[1];
fig6. Simplificacin de memoria, maquina de 16 bits Las siguientes sentencias son equivalentes para accesar el contenido de un elemento: n[i]; *(p+i) Las siguientes sentencias son equivalentes para accesar la direccin de un elemento: &n[i]; n+i; Un apuntador es una variable, por lo que son correctas las siguientes operaciones: p = n; p++; El nombre de un arreglo es una constante; as que son ilegales construcciones como: n = p; n++; p = &n; n = n + 2; Listado // aritmetica_puntero.cpp
113
// Aritmetica de punteros #include <iostream> using namespace std; int main() { char *p, s[] = "ABCDEFG"; p = s; // despliega la cadena a traves del puntero cout << endl << p << endl << p[0] << endl << p+2; return 0; } // toda la cadena // un elemento, el primero // a partir del tercer elemento
Salida
ABCDEFG A B CDEFG
char *p, s[] = "ABCDEFG"; Se declara un carcter, una cadena de caracteres y un puntero a caracteres, que se utiliza para accesar el contenido de la cadena y del carcter en el resto del programa. p = s; Se asigna la direccin de s al puntero p. cout << endl << p[0] Despliega el primer elemento de la cadena s indirectamente, esto es equivalente a decir *p, el contenido de donde apunta p. << endl << *(p+1) Despliega, el contenido del segundo elemento de s, esto es equivalente a p[1], aqu se realiza primero la operacin de lo que esta entre parntesis, se incrementa la direccin del puntero y despus accesa el contenido de la direccin que contiene p despus de la suma *(p+1).
114
<< endl << p+1; Incrementa el contenido de la variable (en una direccin note que esto solo es temporalmente) y despliega la cadena a partir de la direccin del elemento 2, como se muestra.
115
116
ca = new int[3]; // Toma memoria para el arreglo ca[0] = 8; ca[1] = 21; ca[2] = 2020; cout << "Fecha "; cout << ca[0] << '/' << ca[1] << '/' << ca[2]; delete []ca; // Regresa la memoria al almacenamiento libre return 0; }
Salida
Fecha 8/21/2020
int *ca; Se declara un puntero a enteros. ca = new int[3]; new, separa memoria necesaria para almacenar 3 enteros y le asigna la direccin de inicio del bloque de memoria separado. En el programa se trata el bloque como un arreglo normal (los arreglos y el manejo de punteros esta ntimamente ligado) y se le asigna enteros a cada elemento. delete []ca; Se libera el bloque de memoria separado anteriormente con el operador new. Listado // asientos.cpp // anejo dinmico de memoria utilizando un arreglo de strings, // el programa administra un lugar con asientos numerados. #include <iostream> #include <string>
117
using namespace std; int main() { int total_asientos, numero, i; string *asiento; cout << "\nCantidad de asientos con que cuenta el local? "; cin >> total_asientos; // Toma la longitud del arreglo
asiento = new string[total_asientos]; // Aloja el arreglo do { for (i=0; i < total_asientos; i++) cout << endl << "Asiento " << i << ": " << asiento[i]; cout << endl << "Numero de Asiento? (-1 termina)"; cin >> numero; if (numero < 0) break; cout << "Nombre "; cin >> asiento[numero]; } while(1); delete []asiento; return 0; }
118
Numero de Asiento? (-1 termina)1 Nombre Batman Asiento 0: Asiento 1: Batman Asiento 2: Numero de Asiento? (-1 termina)0 Nombre Superman Asiento 0: Superman Asiento 1: Batman Asiento 2: Numero de Asiento? (-1 termina)
string *asiento; Se declara un puntero a string asiento = new string[total_asientos]; Se crea un bloque de memoria suficiente para almacenar el nmero asignado a la variable total_asientos. El resto del programa muestra los elementos y se accesa el elemento seleccionado por el usuario, en caso de que el numero sea -1 se cumple la condicin del if y se ejecuta la instruccin break que termina el ciclo do .. while delete []asiento; delete, regresa el bloque de memoria al almacenamiento libre. Ejercicio
Modifique el programa ordena_c.cpp para que pida la cantidad de calificaciones al momento de ejecucin, es decir cree el arreglo dinmicamente
119
El uso de punteros es para generar llamadas por referencia al llamar una funcin, para crear listas encadenadas as como estructuras dinmicas de datos. Cuando un puntero a estructura es pasado a una funcin, solo la direccin de la estructura es puesta en el stack de datos. Esto permite unas llamadas muy rpidas a las funciones. Ejemplo: struct bal { float balance; char nombre[40]; } persona; Declarando un apuntador a estructura. struct bal *p; Esto pone la direccin de la estructura persona en el apuntador p: p = &persona; Para accesar los elementos de la estructura usando un apuntador a esa estructura, usamos: (*p).balance; Los parntesis son necesarios alrededor del apuntador porque el operador punto (.) tiene mayor prioridad mayor que el operador *. El segundo mtodo de acceso es utilizando el operador ->; lo cual nos facilita la programacin. Ejemplo: p->balance;
120
struct Fecha { int mes; int dia; int ayo; }; int main() { Fecha *fecha = new Fecha; fecha->mes = 3; fecha->dia = 30; fecha->ayo = 1968; cout << "\nFecha: " << fecha->mes << '/' << fecha->dia << '/' << fecha->ayo; delete fecha; return 0; } // Regresa la memoria al almacenamiento libre // Toma memoria para una fecha
Salida
Fecha: 3/30/1968
Fecha *fecha = new Fecha; Se declara el puntero de tipo estructura Fecha, y se inicializa con la direccin de inicio del bloque de memoria alojado con el operador new. Operador -> fecha->mes = 3; Es equivalente a: (*fecha).mes
(c) copyright 2008-2012 Rogelio Cesar Rodriguez Cervantes 121
los parntesis son necesarios ya que el punto (.) tiene mayor procedencia que el asterisco (*) y sin ellos la expresin: *fecha.mes
Significa: (*fecha.mes) Lo cual es ilegal porque mes no es un puntero. Nota: El operado -> se implemento ya que los punteros a estructuras son bastante comunes.
Arreglo de punteros y manejo de memoria dinmica Los apuntadores son variables, as que los podemos almacenar en un arreglo. Declaracin: int *x[10]; Asignacin: x[2] = &v; Encontrar el valor de v. *x[2] Diferencias de arreglos bidimensionales y arreglos de punteros Ejemplo sin punteros: char cap[3][32] = { "introduccion", "tipos, operadores y expresiones", "control de flujo" // No se pone coma };
122
introduccion\0 tipos, operadores y expresiones\0 control de flujo\0 Se separan 96 bytes de almacenamiento, siempre, sin importar si se usan o no. Ejemplo con punteros: char *cap[3] = { "introduccion", "tipos, operadores y expresiones", "control de flujo" }; 2000 2000 2002 3000 3001 3002 3000 3001 3002 introduccion\0 tipos, operadores y expresiones\0 control de flujo\0
Se separa solo 63 bytes de almacenamiento + 6 bytes para el almacenamiento de los punteros, es decir solo el espacio estrictamente requerido. Listado // nombres.cpp // Manejo de memoria dinamica, arreglo de punteros #include <iostream> using namespace std; const int LIMITE=5; int main() { char str[80], *nombre[LIMITE]; int i; cout << "Ingresa una lista de nombres" << endl; for (i=0; i < LIMITE; i++) { cout << i << ".-Nombre ? "; cin.getline(str, 79); // Asigna memoria al elemento i del arreglo nombre nombre[i] = new char[strlen(str)+1];
123
strcpy(nombre[i], str); // Copia str a nombre } cout << endl << "Lista de nombres:" << endl; for (i=0; i < LIMITE; i++) cout << i << ".- " << nombre[i] << endl; // desaloja la memoria for (i=0; i < LIMITE; i++) delete nombre[i]; return 0; } Salida en base a la entrada de ejemplo Ingresa una lista de nombres 0.-Nombre ? Stroustrup 1.-Nombre ? Abrash 2.-Nombre ? Lamothe 3.-Nombre ? Schildt 4.-Nombre ? Kernighan Lista de nombres: 0.- Stroustrup 1.- Abrash 2.- Lamothe 3.- Schildt 4.- Kernighan
Ejemplo de Asignacin de memoria dinmica con punteros a punteros Listado // punteros_punteros.cpp // manejo de memoria dinamica, punteros a punteros // Modificacion del programa nombres.cpp #include <iostream>
124
using namespace std; int main() { char str[80], **nombre; int i, n; cout << endl << "Cantidad de elementos en la lista ? "; cin >> n; // Desecha el ultimo caracter del buffer de entrada // en este caso el caracter nueva linea: '\n' cin.ignore(); nombre = new char *[n]; cout << endl << "Ingresa una lista de nombres"; for (i=0; i < n; i++) { cout << endl << i << ".-Nombre? "; cin.getline(str, 79); // Asigna memoria a nombre nombre[i] = new char[strlen(str)+1]; // Copia str a nombre strcpy(nombre[i], str); } cout << endl << "lista de nombres"; for (i=0; i < n; i++) cout << endl << i << ".-Nombre? " << nombre[i]; // desaloja la memoria de cada elemento for (i=0; i < n; i++) delete nombre[i]; // Desaloja el total de elementos delete nombre; return 0;
125
} Salida en base a la entrada de ejemplo Cantidad de elementos en la lista ? 3 Ingresa una lista de nombres 0.-Nombre? Bill Watterson 1.-Nombre? Charles M. Schulz 2.-Nombre? Jim Davis lista de nombres 0.-Nombre? Bill Watterson 1.-Nombre? Charles M. Schulz 2.-Nombre? Jim Davis
Argumentos para main Algunas veces es necesario pasar informacin al programa cuando antes de ejecutarse; esta informacin se pasa a la funcin main va la lnea de argumentos. Argumentos en la lnea de comandos Es la informacin que sigue incluyendo el nombre del programa en la lnea de comandos del sistema operativo separada por espacios. Cuando se invoca a main al comienzo de la ejecucin se llama con dos argumentos llamados por costumbre: argc y argv. argc Contiene el nmero de argumentos que estn presentes en la lnea de comandos cuando se invoco el programa.
126
argv Variable tipo puntero que contiene la direccin al inicio de un arreglo de cadenas de caracteres. Este arreglo contienen los argumentos donde cada cadena contiene un argumento. Ejemplo: 1.- Hacer ejecutable el programa llamado arg.exe por ejemplo. 2.- Ejecutarlo: arg buenos das Nota: Es un arreglo de apuntadores. argc siempre ser 1 porque el primer argumento es el nombre del programa (algunos sistemas operativos incluyen el path).
Listado // args.cpp // argumentos en la linea de comandos #include <iostream> using namespace std; int main(int argc, char *argv[]) { int i; cout << "Numero de Argumentos " << argc; for (i = 0; i < argc; i++) cout << endl << i << " - " << argv[i]; return 0; }
127
128
<< endl << "Longitud maxima del vector: " << enteros.max_size(); // vacia el vector enteros.clear(); // agrega 10 elementos al vector for (i=1; i <=10; i++) enteros.push_back(i); for (i=0; i < 10; i++) cout << endl << enteros[i]; cout << endl << "Ahora hay en el vector: " << enteros.size() << " elementos\n"; //Elimina el ultimo elemento del vector enteros.pop_back(); cout << endl << "Hay en el vector: " << enteros.size() << " elementos\n"; return 0; }
129
Salida Ingresa un numero: 55 En la posicion 0 esta: 55 Longitud del vector : 1 Capacidad del vector : 1 Longitud maxima del vector: 1073741823 1 2 3 4 5 6 7 8 9 10 Ahora hay en el vector: 10 elementos Hay en el vector: 9 elementos
Ejercicio 1. 2. Modifique el ejemplo de la pagina 89 (fecha.cpp) para que use la clase vector en vez de un arreglo. Modifique el ejemplo de la pagina 90 (ordena_c.cpp) para que use la clase vector en vez de un arreglo.
130
Capitulo 4
Clases y objetos
132
133
public Los miembros que le siguen a esta palabra reservada pueden ser accesados en cualquier lugar dentro del alcance de la clase. La funcionalidad que brinda el control de acceso protected es usada principalmente cuando se prev que la clase creada va a ser heredada; este tipo de acceso se cubre en el capitulo de herencia.
La funciones estn implementadas dentro de la definicin de la clase, si cumplen con las especificaciones de una funcin inline el compilador las implementa como tales (ver capitulo de subprogramas).
134
135
Salida
:O :] :) :]
class CEmoticon {
Se declara la clase CEmoticon usando la palabra reservada class seguida del nombre de la clase.
public: void Muestra(void){ cout << "\n" << m_sIcon; } void Apariencia(string a_sIcon){ m_sIcon = a_sIcon; }
Se declaran los miembros que pueden ser accesados en cualquier parte donde se este usando una instancia de esta clase (objeto).
136
La funcin miembro Muestra() es usada para desplegar el contenido de la variable privada m_sIcon y la funcin Apariencia() para cambiar el valor de esta variable. Nota: Las funciones miembro solo pueden ser llamadas en asociacin de un objeto, a diferencia de las funciones definidas fuera de una clase. Las funciones en el ejemplo anterior seran innecesarias si se declarara la variable m_sIcon como publica pero esto ira en contra de la ocultacin de la informacin que es uno de los principios fundamentales de la programacin orientada a objetos. Al definir la clase utilizando miembros privados y pblicos, se puede decir que un objeto tiene un estado interno (miembros privados) y operaciones externas (miembros pblicos).
objeto_icon1.Apariencia(":O"); objeto_icon2.Apariencia(":]");
Cada objeto llama la funcin Apariencia() para cambiar sus respectivas variables m_sIcon.
objeto_icon1.Muestra(); objeto_icon2.Muestra();
Los objetos despliegan el valor de la variable privada m_sIcon mediante sus funciones miembro Muestra().
Ejercicio Cree una clase Persona que contenga el nombre, direccin, telfono y una funcin para actualizar y otra para desplegar los datos.
Cada vez que se crea una instancia de una clase, el objeto ocupa espacio en memoria y this es la palabra reservada con el valor de la direccin en memoria del objeto que est siendo ejecutado. En un objeto, this es un puntero a si mismo, ste es utilizado implcitamente para referenciar todos los miembros del objeto; tambin puede ser utilizado en forma explicita. El siguiente ejemplo es una modificacin a la clase CFecha e incluye una funcin miembro que recibe una referencia a un objeto del mismo tipo y comprueba si los datos que contienen los objetos son iguales o diferentes, adems comprueba s trata de si mismo.
Listado
// cfechacompara.h #pragma once class CFecha { private: int mm, dd, aa; public: CFecha(int a_dd, int a_mm, int a_aa) { mm = a_mm; dd = a_dd; aa = a_aa; } void Fecha(); void Compara(CFecha &fecha); };
138
void CFecha::Fecha() { cout << dd << '/' << } void CFecha::Compara(CFecha &fecha) { if (&fecha == this) { cout << "Fechas iguales, mismo objeto"; return; } // if (fecha.mm == this.mm // && fecha.dd == this.dd & fecha.aa == this.aa) if (fecha.mm == mm && fecha.dd == dd & fecha.aa == aa) cout << "Fechas iguales"; else cout << "Fechas diferentes"; } if (&fecha == this) { cout << "Fechas iguales, mismo objeto"; return; } Se compara la direccin en memoria de la referencia al objeto recibido con la direccin actual del objeto que recibi la referencia, usando el this. mm << '/'<< aa;
if (fecha.mm == mm && fecha.dd == dd & fecha.aa == aa) cout << "Fechas iguales"; else cout << "Fechas diferentes";
Listado // testcfechacompara.cpp #include <iostream> #include "cfechacompara.h"
139
using namespace std; int main() { CFecha f1(10, 1, 05); CFecha f2(22, 1, 05); f1.Fecha(); cout << "\t\t"; f1.Compara(f1); cout << endl; f2.Fecha(); cout << "\t\t"; f2.Compara(f1); return 0; }
Salida
10/1/5 22/1/5 Fechas iguales, mismo objeto Fechas diferentes
CFecha f1(10, 1, 05); CFecha f2(22, 1, 05); Se declaran dos objetos de tipo CFecha y se inicializan mediante su constructor. f1.Compara(f1); El objeto f1 manda llamar la funcin Compara() y le manda la direccin de s mismo. f2.Compara(f1); El objeto f2 manda llamar la funcin Compara() y le manda la direccin del objeto f1. Ejercicios
140
3.
Agregue funciones miembro a la clase CFecha en cfecha.h y cfecha.cpp para que actualice el ao, el mes y el da independientemente, as como funciones para que regrese solo el ao, mes y da respectivamente.
4. Hacer un programa con una clase hora que implemente una funcionalidad parecida a la de la clase de fecha.
141
int main(){ CEmoticon objeto_icon1; CEmoticon objeto_icon2; objeto_icon1.Muestra(); objeto_icon2.Muestra(); cout << endl; objeto_icon1.Apariencia(":D"); objeto_icon1.Muestra(); objeto_icon2.Muestra(); return 0; } // Declara un objeto de tipo CEmoticon
Salida
:| :| :D :|
CEmoticon() { Apariencia(":|"); } La funcin CEmoticon() tiene el mismo nombre de la clase, por lo tanto es una funcin constructor y esta es llamada automticamente cuando se crean instancias de la clase. Ntese que en el cuerpo de la funcin constructor se llama la funcin miembro Apariencia(), aqu no se necesita el nombre del objeto, porque automticamente se usa el objeto que se este ejecutando (ver el operador this, al final de este capitulo)
En vez de llamar el mtodo Apariencia() la funcin miembro anterior se pudo haber escrito como: CEmoticon() { m_sIcon = ":|"; }
142
void Apariencia(string a_sIcon); A diferencia de la funcin miembro Muestra() la cual esta completamente implementada dentro de la clase (En forma inline, ver capitulo de subprogramas), aqu solo se incluye el encabezado de la funcin (El prototipo, ver capitulo de subprogramas) para indicar que pertenece a la clase, y la implementacin de la misma se realiza fuera de la declaracin de la clase. El anlisis de esta manera de crear las clases se discute en el siguiente tema en este capitulo: Clases y mdulos.
Al declarar los objetos objeto_icon1 y objeto_icon2 se llama automticamente la funcin constructor CEmoticon().
objeto_icon1.Muestra(); objeto_icon2.Muestra();
Muestra el contenido de cada objeto, a diferencia del ejemplo anterior esto se hace sin antes llamar la funcin Apariencia() gracias a que el constructor ha inicializado la variable miembro m_sIcon al crear los objetos.
143
Ejercicio Discuta cuales son las ventajas y desventajas de usar constructores como inicializadores de las variables miembro.
Clases y mdulos Al disear una clase es conveniente separar la interfase de la implementacin, la interfase dice aqu est como es el objeto y estos son los mtodos del objeto, la implementacin muestra cmo los mtodos trabajan [Eckel 1993] La declaracin de la clase es la interfase en C++, que a excepcin de las funciones implementadas dentro de la clase, solo indica cmo es el objeto. La prctica en C++ es escribir la declaracin de la clase en un archivo de encabezado y la implementacin en un archivo con extensin cpp. La siguiente aplicacin muestra una clase con una funcin constructor con argumentos y cmo mandarle los argumentos al constructor al crear el objeto; tambin sigue la prctica comn en C+ + de crear un archivo de encabezado con la especificacin de la clase y un archivo de cdigo (.cpp) con la implementacin de la clase. Listado // cfecha.h // clase implementada con mdulos // un constructor con #pragma once class CFecha { private: int mm, dd, aa; public: CFecha(int a_dd, int a_mm, int a_aa) { mm = a_mm; dd = a_dd; aa = a_aa; } void Fecha(); void FechaCompleta(); }; argumentos
144
"Julio", "Agosto",
145
cout << dd << " de " << meses[mm-1] << " del " << (2000 + aa);
Despliega la fecha completa usando las variables con el numero de da, el numero de mes como ndice del arreglo de nombres de los meses (menos uno ya que los arreglos en C++ empiezan en cero) y el numero de ao mas el milenio meses[mm-1] (claro que esta clase solo es til si se utiliza en este milenio, pero espero que para entonces salga una edicin nueva de este libro con los ajustes necesario ) Listado // testcfecha.cpp #include <iostream> #include "cfecha.h" using namespace std; int main() { CFecha f1(10, 1, 9); CFecha f2(15, 1, 10); f1.Fecha(); cout << "\t\t"; f1.FechaCompleta(); cout << endl; f2.Fecha(); cout << "\t\t"; f2.FechaCompleta(); return 0; }
146
Destructores Un destructor es una funcin miembro con el mismo nombre que la clase, pero precedido por un carcter tilde ~. Una clase slo tiene una funcin destructor, no tiene argumentos y no devuelve ningn valor. Al igual que las dems funciones miembro puede estar definido dentro o fuera de la clase. El destructor es usado para realizar las operaciones necesarias para el objeto cuando este deja de existir, como por ejemplo liberar espacio adquirido durante el tiempo de vida del objeto. El siguiente programa es una modificacin del ejemplo emoticon2.cpp presentado en el listado 7.2. Se usa un destructor y se muestra el orden de llamado del constructor y el destructor.
147
Listado // emoticon_con.cpp // uso de constructores y destructores #include <iostream> using namespace std; class CEmoticon { private: string m_sIcon; public: CEmoticon(string a_sIcon); ~CEmoticon() { cout << endl << "Adios mundo cruel! " << m_sIcon; } void Muestra(void){ cout << endl << "Yo estoy " << m_sIcon; } }; CEmoticon::CEmoticon(string a_sIcon) { m_sIcon = a_sIcon; cout << endl << "Acabo de nacer! " << m_sIcon; } int main(){ CEmoticon emoticon1(":)"); { CEmoticon emoticon2(":("); emoticon2.Muestra(); } emoticon1.Muestra(); return 0; }
148
Salida
Acabo de nacer! :) Acabo de nacer! :( Yo estoy :( Adios mundo cruel! :( Yo estoy :) Adios mundo cruel! :)
~CEmoticon() { cout << ende << "Adios mundo cruel! " << m_sIcon; }
Aqu se implementa el destructor el cual solo despliega un mensaje para anunciar que ha sido llamado, igual que el constructor.
149
Miembros estticos
Al crearse un objeto, se separa espacio para las variables por cada objeto creado, a excepcin de las variables miembro estticas; stas pertenecen a todos los objetos derivados de la clase. Las variables estticas son globales para todos los objetos de la misma clase; stas variables tambin son llamadas variables de la clase ya que su contenido no depende de cada objeto. Una clase puede contener datos y funciones estticas. Los siguientes tres listados de cdigo presentan una aplicacin donde dentro de un ciclo se crean aleatoriamente varios objetos dentro de un arreglo de punteros, se muestran los objetos creados y despus se destruyen; todo esto llevando un conteo de los objetos creados y destruidos en una variable miembro de tipo static. Listado // static_emoticon.h // miembros estaticos // definicin clase CEmoticon #pragma once #include <string> using namespace std; class CEmoticon { private: static int m_iPoblacion; string m_sIcon; public: CEmoticon(string a_sIcon); ~CEmoticon(); static int Poblacion() { return m_iPoblacion; } void Muestra(); };
150
#include "static_emoticon.h" int CEmoticon::m_iPoblacion = 0; CEmoticon::CEmoticon(string a_sIcon) { m_iPoblacion++; m_sIcon = a_sIcon; } CEmoticon::~CEmoticon() { m_iPoblacion--; } void CEmoticon::Muestra() { cout << " " << m_sIcon; }
int CEmoticon::m_iPoblacion = 0;
Como la variable tipo static pertenece a la clase y no a un objeto en particular se inicializa haciendo referencia a la clase.
m_iPoblacion++;
La variable se incrementa dentro del constructor; como el constructor se llama cada vez que se crea un objeto, se lleva un conteo de los objetos en esta variable.
m_iPoblacion--;
La variable se decrementa cada vez que un objeto ejecuta al destructor el cual se llama automticamente cada vez que el objeto se destruye. Listado // test_static_emoticon.cpp // prueba la clase CEmoticon incluida en static_emoticon.cpp #include <iostream> #include <string> #include <cstdio> #include <ctime> #include "static_emoticon.h"
151
int main(){ int nc, i; CEmoticon *emoticon[10]; string op; nc = 0; srand((unsigned)time( NULL )); do { nc = rand() % 10; for (i = 0; i < nc; i++) emoticon[i] = new CEmoticon(":)"); cout << "Poblacion: " << CEmoticon::Poblacion() << endl; for (i = 0; i < nc; i++) emoticon[i]->Muestra(); for (i = 0; i < nc; i++) delete emoticon[i]; cout << endl << "Continuar (s/n) ?"; cin >> op; } while (op != "n" && op != "N"); return 0; } Salida Poblacion: 2 :) :) Continuar (s/n) ?s Poblacion: 9 :) :) :) :) :) :) :) :) :) Continuar (s/n) ?s Poblacion: 6 :) :) :) :) :) :) Continuar (s/n) ?n
152
CEmoticon *emoticon[10];
Se declara un arreglo de punteros de tipo CEmoticon, note que aqu no se crean los objetos, solo el espacio para almacenar diez direcciones a diez objetos del tipo CEmoticon.
nc = rand() % 10;
Dentro del ciclo se genera un nmero aleatorio entre el cero y nueve, para crear esta cantidad de objetos y guardar sus direcciones en el arreglo de punteros.
153
154
Capitulo 5
Herencia
156
Por ejemplo si se quiere realizar un programa para dibujar figuras, se puede empezar por el elemento principal que todo dibujo contiene, un punto, de ah una lnea, un rectngulo, etc., y se va reutilizando el cdigo ya escrito de la clase anterior. Ejemplo:
Donde: 1. La clase lnea hereda de clase punto 2. La clase rectngulo hereda de clase lnea (y a la vez de punto a travs de lnea) Herencia mltiple La herencia mltiple es cuando una clase se deriva de ms de una a la vez y hereda las caractersticas de todas las clases de donde se deriva. Ejemplo:
157
Solo los elementos de la clase que sean absolutamente necesarios para la comunicacin externa de los objetos deben de ser de tipo puiblic. Los miembros que solo sean necesarios para las clases derivadas deben marcarse como protected. Todos los dems miembros de la clase deben ser de tipo private, esto es para encapsular el objeto y reducir al mnimo los conflictos de acceso a los mismos, as como para aumentar la flexibilidad del mantenimiento de la clase, al no estar expuestos es mas fcil cambiarlos, ya sea de tipo de datos, nombres, etc. Lo recomendable es hacer o seguir un anlisis y diseo orientado a objetos antes de empezar a codificar.
158
159
private: string strPrivado; protected: string strProtegido; public: string strPublico; CBase(){ strPrivado = "Privado, dominio de esta clase"; strProtegido = "Protegido, dominio de la familia"; strPublico = "Publico, dominio publico"; } }; Esta clase contiene los tres tipos de acceso y servir como base para ser heredada mas adelante, as como para mostrar el tipo de acceso de los miembros al usarla. Listado // testcbase.cpp #include "cbase.h" #include <iostream> using namespace std; int main(){ CBase base; //cout << base.strPrivado << endl; cout << base.strPublico << endl; return 0; } Salida Publico, dominio publico // no accesible // accesible porque es publico
Note que el campo miembro de tipo protected acta como tipo private para la clase base.
160
Listado // CJugadorVirtual.h // clase base, para ser heredada por otra // los objetos generan un numero aleatorio entre // 0 y 9 #pragma once #include <iostream> #include <cstdlib> // rand() #include <ctime> using namespace std; class CJugadorVirtual { protected: int numero; public: CJugadorVirtual() { numero = 0; } void Adivina() { numero = rand() % 10; } int MiNumero() { return numero; } }; protected: int numero; Aqu se declara un entero con control de acceso protegido, cada objeto debe generar un numero aleatorio al llamar el mtodo Adivina() y regresar el numero con el mtodo MiNumero(), la idea es que sea parte de un jugador artificial dentro de un programa y a la vez servir como base para una clase que represente a un jugador humano.
161
Listado // test_jugadorvirtual.cpp // se genera un numero aleatorio en la funcion main // y los objetos CJugadorVirtual "juegan" a // "adivinar" el numero generado en main #include <iostream> #include <cstdlib> // srand() #include <ctime> // time() #include "CJugadorVirtual.h" using namespace std; int main() { bool adivinado = false; int numeroGenerado; CJugadorVirtual j1; CJugadorVirtual j2; srand((unsigned)time( NULL )); numeroGenerado = rand() % 10; cout << "Estoy pensando en un numero entre 0 y 9..." << endl; while (true) { j1.Adivina(); j2.Adivina(); cout << endl << "Jugador uno dice " << j1.MiNumero(); cout << endl << "Jugador dos dice " << j2.MiNumero(); if (j1.MiNumero() == numeroGenerado) adivinado = true; if (j2.MiNumero() == numeroGenerado) adivinado = true; if (adivinado){ cout << endl << "Numero Adivinado!, el numero era "
162
<< numeroGenerado; break; } else cout << endl << "Los jugadores deben de intentar otra vez.\n"; } return 0; } Salida Estoy pensando en un numero entre 0 y 9... Jugador uno dice 9 Jugador dos dice 9 Los jugadores deben de intentar otra vez. Jugador uno dice 1 Jugador dos dice 4 Numero Adivinado!, el numero era 1
El programa genera un nmero aleatorio y usa la clase CJugadorVirtual para crear objetos que generen nmeros y compararlos con el nmero generado dentro de main, esto para crear un juego de adivina el nmero que pienso, en este caso no hay intervencin externa y los programas juegan solos. Ejercicios 3.Modifique la siguiente clase para ser usada como clase base, siguiendo los principios de diseo de la programacin orientada a objetos, tip: es una sola lnea la que hay que modificar. // cpersona.h // Ejercicio modificar para ser clase base #pragma once #include <iostream> #include <string> using namespace std; class CPersona {
163
private: string Nombre; string Direccion; string Telefono; public: void Captura(void); void Despliega(void); }; void CPersona::Captura(void) { cout << endl << "Nombre : "; getline(cin, Nombre, '\n'); cout << "Direccion: ";
getline(cin, Direccion, '\n'); cout << "Telefono : "; getline(cin, Telefono, '\n'); } void CPersona::Despliega(void) { cout << endl << Nombre; cout << endl << Direccion; cout << endl << Telefono; } 2. Realice un programa principal para que pruebe la clase CPersona
164
Si la clase base y la clase derivada cuentan con un constructor primero de ejecuta el constructor de la clase base y despus el de la clase derivada. El orden de llamado de inicializacin de las clases en la jerarqua de herencia es primero las clases base de una clase derivada. En el caso del destructor el orden de ejecucin es inverso, primero la clase derivada y despus las clases base.
Listado // hereda_cbase.cpp // ejemplo de tipos de acceso herencia #include <iostream> #include <string> #include "cbase.h" using namespace std; // Derivando en forma privada class CDerivadaPrivada : private CBase { public: void Despliega() { // no accesible, privado solo lo puede usar "CBase" // cout << strPrivado << endl;
165
cout << strProtegido << endl; cout << strPublico << endl; } }; // Derivando en forma protegida class CDerivadaProtegida : protected CBase { public: void Despliega() { // no accesible, privado solo lo puede usar "CBase" // cout << strPrivado << endl; cout << strProtegido << endl; cout << strPublico << endl; } }; // Derivando en forma publica class CDerivadaPublica : public CBase { public: void Despliega() { //cout << strPrivado << endl; cout << strProtegido << endl; cout << strPublico << endl; } }; int main(){ CDerivadaPrivada privada; privada.Despliega(); // no accesible al heredar se uso "private", // y un publico se convierte en privado // cout << privada.strPublico << endl; CDerivadaProtegida protegida; protegida.Despliega(); // no accesible al heredar se uso "protected", // un publico se convierte en potegido // cout << protegida.strPublico << endl;
166
CDerivadaPublica publica; publica.Despliega(); // accesible al heredar se uso "public", // un publico se queda como publico cout << publica.strPublico << endl; return 0; } Salida Protegido, dominio de la familia Publico, dominio publico Protegido, dominio de la familia Publico, dominio publico Protegido, dominio de la familia Publico, dominio publico Publico, dominio publico
#include "cbase.h" La clase CBase se incluye con aqu. class CDerivadaPrivada : private CBase { En esta lnea se hereda en forma privada la clase Cbase y la clase CDerivadaPrivada obtiene derecho a todos los miembros permitidos de la clase CBase como si fueran incluidos directamente en la clase derivada. class CDerivadaProtegida : protected CBase { class CDerivadaPublica : public CBase { En estas lneas se hereda de la clase CBase en forma protegida y publica respectivamente obteniendo el acceso a los miembros de la clase CBase segn las reglas de acceso y se pueden usar como de la misma manera que los miembros propios. En la funcin principal main() se crean objetos de las tres clases derivadas de CBase y se prueban sus miembros para mostrar el acceso que se obtiene con los diferentes controles de acceso al heredar.
167
Listado // CJugadorHumano.h #pragma once #include <iostream> #include "CJugadorVirtual.h" using namespace std; class CJugadorHumano : public CJugadorVirtual { public: void Adivina() { cout << " Numero que piensas? "; cin >> numero; } };
168
class CJugadorHumano : public CJugadorVirtual { En esta lnea la clase CJugadorHumano hereda pblicamente a la clase CJugadorVirtual obteniendo el acceso a todos sus miembros permitidos. protected: int numero; El campo numero es heredado y se vuelve de tipo protected para que pueda ser heredado en clases derivadas si as se necesitara. El acceso esta garantizado para la clase CJugadorHumano como si fuera escrito directamente en la clase. CJugadorVirtual() { int MiNumero() { Estos mtodos son heredados tal y como estn, ya que la el constructor CJugadorVirtual() no usa argumentos este se manda llamar automticamente y no es necesario tomar medidas dentro de la clase derivada. void Adivina() { En el caso de este mtodo, se sobre-escribe ya que la implementacin del mtodo dentro de la clase CJugadorVirtual no se aplica para esta clase y es necesario pedir el numero al usuario. Al sobrescribir este mtodo la clase derivada desecha el uso del mtodo de la clase base, al menos automticamente, ya que aun puede ser llamado indicando el nombre de la clase base, por ejemplo: CBase::Adivina() y ejecutara le mtodo de la clase padre en ve del mtodo propio. Listado // JuegoAdivina3.cpp #include <iostream> #include <ctime> #include <cstdlib> #include "CJugadorHumano.h" using namespace std; int main() { bool adivinado = false; int numeroGenerado; CJugadorVirtual j1; CJugadorVirtual j2; CJugadorHumano jh;
169
srand((unsigned)time( NULL )); numeroGenerado = rand() % 10; cout << "Estoy pensando en un numero entre 0 y 9..." << endl; while (true) { j1.Adivina(); j2.Adivina(); jh.Adivina(); cout << endl << "Jugador uno dice " << j1.MiNumero(); cout << endl << "Jugador dos dice " << j2.MiNumero(); cout << endl << "Jugador tres dice " << jh.MiNumero(); if (j1.MiNumero() == numeroGenerado) adivinado = true; if (j2.MiNumero() == numeroGenerado) adivinado = true; if (jh.MiNumero() == numeroGenerado) adivinado = true; if (adivinado){ cout << endl << "Numero Adivinado!, el numero era " << numeroGenerado; break; } else cout << endl << "Los jugadores deben de intentar otra vez.\n"; } return 0; } Salida Estoy pensando en un numero entre 0 y 9...
170
Numero que piensas? 3 Jugador uno dice 7 Jugador dos dice 6 Jugador tres dice 3 Los jugadores deben de intentar otra vez. Numero que piensas? 4 Jugador uno dice 1 Jugador dos dice 1 Jugador tres dice 4
Este ejemplo es casi idntico al del programa test_jugadorvirtual.cpp con la diferencia que se crea un objeto de tipo CJugadorHumano y este objeto permite la participacin externa al preguntar por un nmero para comparar con el generado en el programa principal. Ejercicios 4 Realic una clase derivada de la clase CPersona, la cual se sugiere se llame CEstudiante que adems contenga los siguientes miembros: string numero_control int calificacion Mtodo Captura() Debe de capturar tanto los campos heredados como los propios. Mtodo Despliega() Debe de desplegar en pantalla tanto los campos heredados como los propios. Mtodo RegresaCalificacion() - regresar la calificacin 2. Realice un programa principal para que pruebe la clase CEstudiante
171
La siguiente clase es para usarla como una de las clases padres en el ejemplo de herencia mltiple; es una modificacin a la clase de CFecha mostrada en el capitulo de clases, la modificacin consiste en inicializar las variables que representan la fecha desde el sistema. Listado // autofecha.h // clase CFecha, el constructor toma la fecha del sistema #pragma once #include <iostream> #include <time> using namespace std; class CFecha { protected: int mm, dd, aa; public: CFecha(){ time_t ahora = time(NULL); dd = tiempo->tm_mday; mm = tiempo->tm_mon + 1; aa = tiempo->tm_year - 100; } void Despliega() { cout << dd << '/' << } }; mm << '/'<< aa; // Hora y fecha de hoy struct tm *tiempo = localtime(&ahora);
time_t ahora = time(NULL); // lee la fecha/hora del sistema Se asigna el tiempo que regresa la funcin time(), al incluir el NULL como argumento esta funcin regresa el tiempo actual del sistema.
struct tm *tiempo = localtime(&ahora); tm es una estructura para almacenar informacin correspondiente a varias funciones de manejo del tiempo.
172
La funcin localtime() acomoda la informacin del la variable ahora para que se formatee el tiempo para ser desplegado.
En las siguientes lneas se asignan las variables miembro de la estructura tiempo a las variables miembro de la clase, que son dd, mm y aa. La siguiente clase es para usarla como una de las clases padres en el ejemplo de herencia mltiple, una clase para representar la hora la inicializacin de las variables es desde el sistema.
Listado // autohora.h // clase CHora, el constructor toma la hora del sistema #pragma once #include <iostream> #include <time> using namespace std; class CHora { protected: int hh, mm, ss; public: CHora(){ time_t ahora = time(NULL); hh = tiempo->tm_hour; mm = tiempo->tm_min; ss = tiempo->tm_sec; } void Despliega() { cout << hh << ':' << } }; mm << ':'<< ss; // Hora y fecha de hoy struct tm *tiempo = localtime(&ahora);
173
La diferencia entre esta clase y la de CFecha es que se asignan las variables de la estructura tiempo correspondientes a la hora a las variables miembro de la clase, que son hh, mm y ss.
El siguiente ejemplo implementa la Clase CTiempo y esta hereda de dos clases CFecha y CHora. Listado // tiempo.h // Clase CTiempo, ejemplo de multiple herencia #include "autofecha.h" #include "autohora.h" class CTiempo : public CFecha, public CHora { public: void Despliega(){ CFecha::Despliega(); cout << " "; CHora::Despliega(); } void CambiaFecha(int ad, int am, int aa) { dd = ad; CFecha::mm = am; aa = aa; } void CambiaHora(int ah, int am, int as) { hh = ah; CHora::mm = am; ss = as; } };
174
void CambiaFecha(int ad, int am, int aa) { dd = ad; CFecha::mm = am; aa = aa; }
En este mtodo se actualizan las variables. Ntese que las clases CFecha y CHora cuentan con una variable que se llama igual por lo tanto es necesario utilizar el operador de acceso para indicarle al compilador a cual clase pertenece la variable. El siguiente programa hace uso de la clase creada en el programa anterior. Listado // testTiempo.cpp // Prueba la clase con herencia multiple CTiempo #include "tiempo.h" int main(){ CFecha fecha; fecha.Despliega(); cout << endl; CHora hora; hora.Despliega();
175
cout << endl; CTiempo tiempo; tiempo.Despliega(); tiempo.CambiaHora(12, 10, 20); cout << endl; tiempo.Despliega(); return 0; }
Salida
19/6/9 16:50:6 19/6/9 16:50:6 19/6/9 12:10:20
La utilizacin de la clase CTiempo es idntica a los dems ejemplos donde se declaran objetos, independientemente de si fue una clase sola o s uso herencia simple o herencia mltiple.
Ejercicio 1 Discuta las ventajas y desventajas del uso de la herencia mltiple 2 Investigue que problemas se pueden presentar al usar herencia mltiple
176
Capitulo 6
Polimorfismo
179
Listado
// cpolifecha.cpp #include "cpolifecha.h" #include <iostream> #include <ctime> using namespace std; CFecha::CFecha(){ time_t ahora = time(NULL); dd = tiempo->tm_mday; mm = tiempo->tm_mon + 1; aa = tiempo->tm_year - 100; } void CFecha::Fecha() { cout << dd << '/' << } void CFecha::FechaCompleta() { string meses[] = {"Enero", "febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"}; mm << '/'<< aa; // Hora y fecha de hoy struct tm *tiempo = localtime(&ahora);
180
cout << dd << " de " << meses[mm-1] << " del " << (2000 + aa); } Listado // testcpolifecha.cpp #include <iostream> #include "cpolifecha.h" using namespace std; int main() { CFecha f1(10, 1, 15); CFecha f2; f1.Fecha(); cout << "\t\t"; f1.FechaCompleta(); cout << endl; f2.Fecha(); cout << "\t\t"; f2.FechaCompleta(); cout << endl; return 0; } Salida 10/1/15 1/6/9 10 de Enero del 2015 1 de Junio del 2009
181
Se declaran dos objetos de tipo CFecha, el primer objeto recibe 3 argumentos, por lo tanto el constructor con tres argumentos es llamado. En la siguiente lnea al declarar el objeto f2 se llama el constructor sin argumentos. Ejercicio 1 Discuta si es posible sobrecargar los mtodos Fecha() y FechaCompleta(). 2 Discuta cual son las posibles ventajas y desventajas de la sobrecarga de funciones miembro.
182
virtual void Muestra() = 0; }; class CEmpleado : public CPersona { protected: float m_fSalario; public: void Agrega() { cout << "Nombre "; cin >> m_sNombre; cout << "Salario "; cin >> m_fSalario; } void Muestra() { cout << "\nNombre " << m_sNombre; cout << "\nSalario " << m_fSalario; } }; class CSocio : public CPersona { protected: int m_iAcciones; public: void Agrega() { cout << "Nombre "; cin >> m_sNombre; cout << "Acciones "; cin >> m_iAcciones; } void Muestra() { cout << "\nNombre " << m_sNombre; cout << "\nAcciones " << m_iAcciones; } };
Aqui se declaran las funciones miembro como virtuales puras, esto le indica al compilador que estas funciones van a ser implementadas por las clases derivadas. Listado // testAbstracta.cpp // prueba la clae abstracta CPersona y sus clases derivadas #include "abstracta.h" int main(){ CPersona *persona; CEmpleado empleado; CSocio socio; cout << "\nAgrega los datos del empleado " << endl; empleado.Agrega(); cout << "\nAgrega los datos del socio " << endl; socio.Agrega(); persona = &empleado; persona->Muestra(); persona = &socio; cout << endl; persona->Muestra(); cout << endl; return 0; } Salida Agrega los datos del empleado Nombre Bruce Salario 150 Agrega los datos del socio Nombre Bjarne Acciones 33
184
En este ejemplo se declaran dos objetos uno de tipo CEmpleado y otro de tipo CSocio, un puntero a la clase base CPersona. El objeto empleado y el objeto socio mandan llamar sus funciones miembro de Agrega() para asignar valores a las variables miembro por medio de la entrada de datos del usuario, como se muestra en los dos primeros prrafos de la salida. Mediante el puntero se accesa indirectamente a los objetos y se despliega el contenido las variables miembro a travs de la funcin Muestra(). Ejercicio 5 Discuta si la clase abstracta en el ejemplo anterior podra no serlo y de ser as, porque. 6 Proponga situaciones donde sea til usar una clase abstracta.
185
Para que el ligado tardo funcione, las funciones derivadas deben tener exactamente el mismo nombre, incluyendo el numero de argumentos y sus tipos de datos, de no ser as el mecanismo virtual es inoperable. Listado // virtual.h // ejemplo de clase con funciones virtuales #pragma once #include <iostream> #include <string> using namespace std; class CPersona { protected: string m_sNombre; public: virtual void Agrega() { cout << "Nombre "; cin >> m_sNombre; } virtual void Muestra() } }; class CEmpleado : public CPersona { protected: float m_fSalario; public: void Agrega() { CPersona::Agrega(); cout << "Salario "; cin >> m_fSalario; } void Muestra() { CPersona::Muestra(); cout << "\nSalario " << m_fSalario; } { cout << "\nNombre " << m_sNombre;
186
}; class CSocio : public CPersona { protected: int m_iAcciones; public: void Agrega() { CPersona::Agrega(); cout << "Acciones "; cin >> m_iAcciones; } void Muestra() { CPersona::Muestra(); cout << "\nAcciones " << m_iAcciones; } };
La clase CPersona a diferencia del ejemplo anterior no es mas una clase abstracta ya que todas los mtodos estn implementados.
Listado // testVirtual.cpp // prueba la clae abstracta CPersona y sus clases derivadas #include "virtual.h" int main(){ CPersona *persona; CPersona obj_persona; CEmpleado empleado; CSocio socio; cout << "\nAgrega los datos de una persona " << endl; obj_persona.Agrega();
187
cout << "\nAgrega los datos del empleado " << endl; empleado.Agrega(); cout << "\nAgrega los datos del socio " << endl; socio.Agrega(); // despliega los datos de obj_persona obj_persona.Muestra(); // Despliega los datos de empleado y socio mediante // el puntero persona persona = &empleado; persona->Muestra(); persona = &socio; cout << endl; persona->Muestra(); cout << endl; return 0; } Salida Agrega los datos de una persona Nombre Batman Agrega los datos del empleado Nombre Superman Salario 3500.50 Agrega los datos del socio Nombre Tarzan Acciones 33 Nombre Batman Nombre Superman
188
CPersona obj_persona;
Aqu se declara un objeto de tipo CPersona, note que la clase CPersona del programa testAbstracta.cpp no puede ser usada para instanciar objetos ya que es una clase abstracta porque tiene funciones virtuales puras, no as en este ejemplo que las funciones virtuales estn plenamente implementadas. Nota: El uso de funciones miembro virtuales permite acceder a los mtodos virtuales de las clases derivadas usando indireccion mediante un puntero de tipo clase base. Los mtodos virtuales puros garantiza el acceso a los mtodos de las clases derivadas mediante la clase base aun cuando la clase base no cuente con suficientes datos para implementar tales mtodos.
189
public: // no virtual, posible problema // ~CBase() { virtual ~CBase() { cout << " Destruyendo CBase" << endl; } virtual void Metodo() { cout << "CBase::Metodo()" << endl; } }; class CDerivada1 : public CBase { public: ~CDerivada1() { cout << "Destruyendo CDerivada1" << endl; } void Metodo() { cout << "CDerivada1::Metodo()" << endl; } }; class CDerivada2 : public CDerivada1 { public: ~CDerivada2() { cout << " Destruyendo CDerivada2" << endl; } void Metodo() { cout << "CDerivada2::Metodo()" << endl; } }; main() { // puntero clase base a objeto derivado CBase * pBase = new CDerivada2; pBase->Metodo(); delete pBase; // llamada a funcin virtual // borrar puntero a clase base
190
Salida con destructor virtual CDerivada2::Metodo() Destruyendo CDerivada2 Destruyendo CDerivada1 Destruyendo CBase
191
Capitulo 7
Archivos
Corrientes de flujo
El sistema de E/S de C++ proporciona una interfaz independiente del dispositivo real al que se accede; esto es una abstraccin entre el programador y el dispositivo que se usa. Esta abstraccin se llama corriente y el dispositivo real se llama archivo. Corrientes Almacenamiento temporal de archivo para transformar los dispositivos fsicos en un almacenamiento lgico. Esto permite que las corrientes sean independientes de los dispositivos. Existen dos tipos de corrientes: texto y binarias.
193
Entrada de texto La entrada o tambin llamada lectura es la transferencia de datos desde una secuencia a una estructura de datos, como, por ejemplo, una matriz de bytes. Salida de texto La salida, tambin llamada escritura consiste en la transferencia de informacin desde un origen de datos a una secuencia. Operaciones de bsqueda Las operaciones de bsqueda consisten en la consulta y modificacin de la posicin actual en una secuencia.
Lectura de un archivo de texto Se crea el objeto con fstream nombre_del_objeto, indicando que es de lectura con el manipulador ios::in. Ejemplo:
194
// muestra.cpp // ejemplo de lectura de un archivo de texto // muestra un archivo de texto en la salida estandard #include <iostream> #include <fstream> #include <cstdlib> using namespace std; int main() { char szArchivo[20]; char c; cout << "Archivo a mostrar? "; cin >> szArchivo; fstream entrada(szArchivo, ios::in); if (entrada.fail()) { cout << "Error al abrir archivo! Abortando"; exit(1); } while (!entrada.eof()){ entrada.get(c); cout << c; } entrada.close(); return 0; } Salida Archivo a mostrar? primer.cpp // Primer.cpp // Escribe un mensaje en pantalla #include <iostream> using namespace std; int main() // termina la ejecucion del programa
195
fstream entrada(szArchivo, ios::in); Se crea el objeto de tipo fstream, y se intenta abrir el archivo que indica la cadena de caracteres szArchivo; el manipulador ios::in indica abrirlo en modo solo lectura. La informacin sobre el xito o fracaso se almacena en basic_ios:rstate. if (entrada.fail()) {
La funcin miembro fail(), regresa el numero uno o true o bien un cero o false dependiendo de la informacin en basic_ios::rstate. En caso de error de creacin del objeto termina la ejecucin del programa al llamar la funcin exit().
while (!entrada.eof()){ eof(), regresa un cero o false si no se ha alcanzado el final del archivo y uno o trae si esta en el fin de archivo. entrada.get(c); get(), lee un carcter del flujo de entrada y lo asigna a la variable c. entrada.close(); close(), cierra el archivo.
Ejercicio
196
Escriba un programa que lea un archivo de texto y lo despliegue en maysculas en la salida estndar.
Escritura en un archivo de texto Para escribir en un archivo se crea un objeto de tipo fstream, con la sentencia fstream nombre_del_objeto, indicando que es de escritura con el manipulador ios::out. Al crear el objeto, si el archivo fsico no existe se crea fsicamente en blanco, si ya existe se sobrescribe en blanco. Ejemplo:
Si se le suma un uno a cada cdigo: 72 = H 72 + 1 = 73 73 = J Toda la cadena en ascii con cada elemento incrementado en uno:
73 49 77 65 \0
Listado
// encripta.cpp // ejemplo de lectura y de escritura a archivos de texto. // lee un archivo de entrada, desfasa el caracter leido y
197
// lo escribe en el archivo de salida #include <iostream> #include <fstream> #include <cstdlib> using namespace std; int main() { char szEntrada[20]; char szSalida[20]; char c; cout << "Archivo a encriptar? "; cin >> szEntrada; cout << "Guardar archivo encriptado en? "; cin >> szSalida; fstream entrada(szEntrada, ios::in); fstream salida(szSalida, ios::out); if (entrada.fail()) { cout << "Error al abrir archivo de entrada! Abortando"; exit(1); } if (salida.fail()) { cout << "Error al abrir archivo de salida! Abortando"; exit(1); } while (!entrada.eof()){ entrada.get(c); salida.put(c+1); } entrada.close(); salida.close(); return 0; } Salida // encripta
198
199
Nota, Listado del archivo primer.txt: 00!Qsjnfs/dqq 00!Ftdsjcf!vo!nfotbkf!fo!qboubmmb $jodmvef!=jptusfbn? vtjoh!obnftqbdf!tue< jou!nbjo)* | !!!dpvu!==!#Ftuf!ft!nj!qsjnfs!qsphsbnb!fo!D,,#< !!!sfuvso!1< ~
// encripta
entrada.close(); salida.close();
La llamada al mtodo close() cierra el archivo.
200
Ejercicios 4.Escriba un programa que lea un archivo de texto y lo escriba en un nuevo archivo de texto copie el contenido de un archivo de texto existente en otro. 5.Desarrolle un programa que lea una cadena de caracteres y la grabe en un archivo de texto. 6.Modifique el programa encripta.cpp para que desencripte un archivo.
Lectura y Escritura en un archivo de texto Para modificar un archivo (leer y escribir en un mismo archivo) se crea un objeto de tipo fstream, con la sentencia fstream nombre_del_objeto, indicando que es de lectura y escritura con los manipuladores ios::in | ios::out | ios::app. Al crear el objeto, si el archivo fsico no existe se crea fsicamente en blanco, si ya existe lo abre para lectura y escritura. Ejemplo: fstream archivo(nombre_archivo.txt, ios::in | ios::out | ios::app); Para posicionarse en una parte del archivo se usa la funcin miembro seekg Ejemplo: // se posiciona exactamente al final del archivo
archivo.seekg(0, ios::end); //se posiciona 10 caracteres despues del inicio archivo.seekg(10, ios::beg);
El siguiente programa de ejemplo ilustra la entrada y salida en un solo archivo, implementando una bitcora bsica, la idea principal es ir agregando texto en un archivo existente, para llevar un control de las actividades realizadas.
201
Listado
// bitacora.cpp // abre un archivo para E/S y agrega texto al final del mismo #include <iostream> #include <fstream> #include <cstdlib> using namespace std; int main() { char szTexto[256]; fstream esArchivo("bitacora.txt", ios::in | ios::out | if (esArchivo.fail()) { cout << "Error al abrir archivo! Abortando"; exit(1); } cout << "Texto? "; cin.getline(szTexto, 255); esArchivo.seekg(0, ios::end); esArchivo << szTexto << "\n"; esArchivo.close(); return 0; } Salida Texto? Punteros, referencias y arreglos ios::app);
Nota, Listado del archivo bitacora.txt: Fundamentos del lenguaje subprogramas Punteros, referencias y arreglos
202
cin.getline(szTexto, 255); getline(), lee toda una lnea del flujo de entrada, con un mximo de 255 caracteres esArchivo.seekg(0, ios::end); Al abrir un archivo para lectura, el indicador de lectura del flujo de entrada se posiciona al inicio del archivo. Como lo que se quiere es agregar texto al final del archivo se usa el seekg(), para mover el indicador al final del archivo. De no hacer esto, cualquier texto agregado al archivo sobrescribe el existente. esArchivo << szTexto << "\n"; Ya que esArchivo es un flujo de datos y fstream se deriva de basic_stream, se asigna la cadena con el operador <<, tal y como se hace con el objeto cout.
203
El siguiente ejemplo es la versin dos de la bitcora, usando estructuras para incluir el porciento terminado de la tarea a grabar en la bitcora, adems cuenta con un men para con las opciones de mostrar el contenido del archivo y agregar nuevos datos. Listado // bitacorax.cpp // agrega texto al final de un archivo #include <iostream> #include <fstream> #include <string> #include <cstdlib> using namespace std; const int LON = 80; struct BitacoraX { int iPorcientoTerminado; char szTexto[LON]; }; int main() { BitacoraX bitacora; int iOpcion, i; fstream esArchivo("bitacorax.dat", ios::out | ios::in | ios_base::app | ios::binary); if (esArchivo.fail()) { cout << "Error al abrir archivo! Abortando"; exit(1); } do { cout << "\n1. Muestra"; cout << "\n2. Agrega"; cout << "\n3. Termina"; cout << "\nOpcion? "; cin >> iOpcion; cin.ignore(); switch (iOpcion) {
204
case 1: cout << "Porciento\tTexto\n"; cout << "-------------------------------------------------------" << endl; esArchivo.clear(); esArchivo.seekg(0, ios::beg); esArchivo.read((char *)&bitacora, sizeof(struct BitacoraX)); while (!esArchivo.eof()){ cout << bitacora.iPorcientoTerminado << "\t\t" << bitacora.szTexto << endl; esArchivo.read((char *)&bitacora, sizeof(BitacoraX)); } break; case 2: cout << "\nTexto ? "; cin.getline(bitacora.szTexto, LON-1); cout << "Porciento terminado? "; cin >> bitacora.iPorcientoTerminado; esArchivo.clear(); esArchivo.seekg(0, ios::end); esArchivo.write((const char *)&bitacora, sizeof(BitacoraX)); esArchivo.flush(); break; case 3: break; default: cout << "Opcion no implementada"; } } while (iOpcion != 3); esArchivo.close(); return 0; }
205
Salida
1. Muestra 2. Agrega 3. Termina Opcion? 1 Porciento 100 100 70 1. Muestra 2. Agrega 3. Termina Opcion? 2 Texto ? Archivos y estructuras Porciento terminado? 60 1. Muestra 2. Agrega 3. Termina Opcion? 3 Texto Instrucciones basicas control de flujo arreglos
-------------------------------------------------------------
El programa incluye un ciclo donde se presenta un men para seleccionar si se quiere mostrar todos los elementos del archivo, agregar un elemento o terminar el ciclo y por consiguiente cerrar el archivo y terminar la ejecucin del programa. La opcin ingresada se compara en un switch, y en caso de ser la opcin 1, se ejecuta el case 1: lneas de encabezado. y despliega unas
206
esArchivo.clear(); Al abrir el archivo, su indicador se encuentra en el inicio del mismo pero al recorrerlo en modo de lectura una vez, se prende la bandera de eof y se queda as. clear(), borra la bandera puesta al alcanzar el fin de archivo -eof- para recorrerlo desde el inicio tantas veces como el usuario as lo desee. esArchivo.seekg(0, ios::beg); seekg(), posiciona el indicador de archivo al inicio, para recorrerlo mientras no sea fin de archivo. esArchivo.read((char *)&bitacora, sizeof(struct BitacoraX)); El mtodo read(), recibe dos parmetros: el primero (char *)&bitcora es el buffer donde se almacenan los datos obtenidos del archivo y el segundo parmetro sizeof(BitacoraX) indica el tamao del buffer; en este caso se invoca la funcin sizeof() la cual regresa el tamao en bytes del tipo de dato recibido como parmetro (en este ejemplo la estructura BitacoraX"). esArchivo.seekg(0, ios::end); seekg(), posiciona el indicador de archivo al final, para agregar datos. esArchivo.write((const char *)&bitacora, sizeof(struct BitacoraX)); El mtodo write(), recibe dos parmetros: el primero (const char *)&bitcora es el buffer donde estn almacenados los datos obtenidos que se van a grabar al archivo y el segundo parmetro sizeof(BitacoraX) indica el
(c) copyright 2008-2012 Rogelio Cesar Rodriguez Cervantes 207
tamao del buffer; en este caso se invoca la funcin sizeof() la cual regresa el tamao en bytes del tipo de dato recibido como parmetro (en este ejemplo la estructura BitcoraX). esArchivo.flush(); flush(), este mtodo graba el flujo del archivo al dispositivo de almacenamiento fsico, esto es para actualizar el contenido del archivo.
208
Ejercicio Realice un programa donde utilice archivos binarios, que grabe y recupere la siguiente estructura: struct SArticulo { int clave; char descripcion[40]; float precio; };
209
Bibliografa
[Stroustrup 1997] Bjarne Stroustrup: The C++ Programming Language Third Edition AddisonWesley 1997 [Stroustrup 1993] Bjarne Stroustrup: El lenguaje de programacin C++ Segunda Edicin Addison-Wesley/Diaz de Santos 1993 [Hernandez 2002] Enrique Hernndez Orallo, Jose Hernndez Orallo y Ma. Carmen Juan Lizandra: C++ Estndar Parainfo Thomson Learning 2002 [Eckel 1993] Bruce Eckel: C++ Inside & Out Osborne McGraw-Hill 1993 [Ellis 1994] Margaret A. Ellis y Bjarne Stroustrup: C++ Manual de referencia con anotaciones Addison-Wesley/Diaz de Santos 1994 [Eckel 2000] Bruce Eckel: Thinking in C++ 2nd ed. volume 1 http://www.BruceEckel.com 2000 [Deitel 1995] H.M. Deitel y P. J. Deitel: Como programar en C/C++ segunda edicin Pearson / Prentice Hall 1995 [Visual C++ 2008 Express Edition] Microsoft Corporation: Ayuda Visual C++ 2008 Express Edition Microsoft Corporation [Builder C++ 5.0] Borland, Inprise Corporation: Ayuda C++ Builder 5.0 Borland, Inprise Corporation [Kernighan 1991] Brian W. Kernighan y Dennis M. Ritchie: El lenguaje de programacin C
209
segunda edicin Prentice Hall 1991 [Schildt 1990] Herlbert Schildt: C The Complete Reference second edition Osborne McGrawHill 1990 [Rodriguez 2006] Rogelio Cesar Rodriguez Cervantes Gua para el aprendizaje sistemtico del lenguaje de programacin C++, Proyecto utilitario para presentar examen profesional de conocimientos para la Maestra en tecnologa Informtica en la Universidad Autnoma de Tamaulipas, Mxico.
210