VE IN RS CL GR IÓN D UYE AT IGITA IS L
C++ es un lenguaje orientado a objetos popular, moderno y en constante evolución. A la hora de desarrollar, es la opción más poderosa y robusta que jamás se haya creado. EN ESTE LIBRO APRENDERÁ: Introducción: programación no estructurada, procedural, modular y orientada a objetos. Análisis y diseño. Fundamentos de C++: diferencias con C, variables, palabras reservadas, constantes, booleanos, operadores, sentencias condicionales y bucles. Aspectos avanzados del lenguaje: entrada y salida, objetos cout y cin, preprocesador, array, funciones, strings y enumeradores. Clases y objetos: diagramas, constructor y destructor, modificadores de acceso, propiedades y métodos estáticos. Gestión de memoria: punteros, operadores new y delete, variables estáticas.
Esta obra es una versión revisada del libro C++, publicado en 2004 por esta misma editorial.
Herencia: métodos inline y constantes, subclases, composición vs. herencia. Polimorfismo: sobrecarga de funciones y de operadores, modificador friend, métodos y destructores virtuales.
array
NIVEL DE USUARIO
-m_iCantElementos : int -m_piArray +FijaTam() +FijaValor() +LeerValor() +Limpiar()
Juego
Básico / Intermedio
Jugador
+RegistrarJugador() +EmpezarPartida()
+RealizarMovida() 1
2
CATEGORÍA Desarrollo
pila -m_iUltimoElementos : int +Insertar() +Extraer() +Vaciar() +FijaTam()
cola -m_iPrimerElementos : int -m_iUltimoElementos : int +Insertar() +Extraer() +Vaciar() +FijaTam()
JugadorBot1
JugadorBot2
+RealizarMovida()
+RealizarMovida()
REDUSERS.com
PROFESOR EN LÍNEA
En nuestro sitio podrá encontrar noticias relacionadas y también participar de la comunidad de tecnología más importante de América Latina.
Ante cualquier consulta técnica relacionada con el libro, puede contactarse con nuestros expertos: profesor@redusers.com.
GRATIS
C++ PROGRAMACIÓN ORIENTADA A OBJETOS
El presente libro tiene el doble objetivo de introducirnos y profundizar tanto en lo que se refiere a la programación orientada a objetos, como al lenguaje C++, que será nuestra principal herramienta a la hora de implementar los conceptos teóricos que vayamos estudiando. A lo largo de los distintos capítulos, conoceremos los fundamentos del lenguaje, su sintaxis básica y la manera de escribir el primer programa en él. Además, veremos aspectos avanzados, como el preprocesador, el uso de arrays, funciones, estructuras, espacios de nombre, clases, objetos y gestión de memoria, entre otros. Analizaremos también herencia, polimorfismo, estructuras de datos dinámicas y plantillas.
VERSIÓN DIGITAL
C++ PROGRAMACIÓN ORIENTADA A OBJETOS
TIPOS DE DATOS Y OPERADORES PREPROCESADOR, ARRAYS, FUNCIONES Y STRINGS DIAGRAMAS DE CLASES Y DE OBJETOS HERENCIA Y POLIMORFISMO ESTRUCTURAS DE DATOS DINÁMICAS Y PLANTILLAS
INCLUYE INTRODUCCIÓN A UML
LENGUAJE UNIFICADO DE MODELADO
TÍTULO:
C++
AUTOR:
Diego Ruiz
COLECCIÓN:
Manuales USERS
FORMATO:
24 x 17 cm
PÁGINAS:
352
Copyright © MMXIV. Es una publicación de Fox Andina en coedición con DÁLAGA S.A. Hecho el depósito que marca la ley 11723. Todos los derechos reservados. Esta publicación no puede ser reproducida ni en todo ni en parte, por ningún medio actual o futuro sin el permiso previo y por escrito de Fox Andina S.A. Su infracción está penada por las leyes 11723 y 25446. La editorial no asume responsabilidad alguna por cualquier consecuencia derivada de la fabricación, funcionamiento y/o utilización de los servicios y productos que se describen y/o analizan. Todas las marcas mencionadas en este libro son propiedad exclusiva de sus respectivos dueños. Impreso en Argentina. Libro de edición argentina. Primera impresión realizada en Sevagraf, Costa Rica 5226, Grand Bourg, Malvinas Argentinas, Pcia. de Buenos Aires en VIII, MMXIV.
ISBN 978-987-1949-64-9
Ruiz, Diego C++ / Diego Ruiz ; coordinado por Gustavo Carballeiro. - 1a ed. - Ciudad Autónoma de Buenos Aires: Fox Andina; Buenos Aires: Dalaga, 2014. 352 p. ; 24 x 17 cm. - (Manual users; 268) ISBN 978-987-1949-64-9 1. Informática. I. Carballeiro, Gustavo, coord. II. Título CDD 005.3
4
PRELIMINARES
El libro de un vistazo En este libro abarcaremos los principales temas que componen la programación orientada a objetos con C++: cuáles son sus características y cómo se utilizan sus funciones. El contenido de cada capítulo es eminentemente teórico, acompañado siempre de diagramas y consejos prácticos.
PROGRAMACIÓN ORIENTADA A OBJETOS
GESTIÓN DE MEMORIA
En este primer capítulo abordaremos
Descubriremos cómo solicitar memoria
los conceptos principales que hacen a la
desde un programa en forma dinámica
programación orientada a objetos y haremos
por medio de los punteros, para hacer
una introducción a los sistemas complejos.
un uso más eficiente de los recursos de la computadora. Veremos también qué son las referencias.
FUNDAMENTOS DE C++ Estudiaremos los elementos que componen este lenguaje de programación y conoceremos sus principales virtudes.
HERENCIA En este capítulo abordaremos herencia y composición, dos recursos fundamentales en el momento de crear el modelo de datos de nuestro sistema. Veremos cómo trabajan los
ASPECTOS AVANZADOS DEL LENGUAJE Aprenderemos a interactuar con el usuario,
constructores y destructores y crearemos una pila y una cola utilizando un array.
veremos qué es un array y cómo emplearlo, y comenzaremos a trabajar con variables que contengan texto. Además, explicaremos cómo crear nuestras propias funciones.
POLIMORFISMO EDUCACIÓN 2.0 Estudiaremos el polimorfismo, tercer gran pilar de la programación orientada a objetos. También veremos mecanismos que
CLASE Y OBJETO
implementa el lenguaje para dar soporte
En este capítulo profundizaremos aspectos
a esta característica: la sobrecarga de
del lenguaje estudiando, básicamente, una
funciones y operadores, los métodos
de sus estructuras principales: la clase.
virtuales y las clases abstractas.
www.redusers.com
5
C++
está formado por una serie de diagramas
OBJETOS PERSISTENTES
que nos ayudarán a diseñar y documentar los
Veremos cómo trabajar con los recursos que
diferentes componentes del sistema.
ofrece C++ para la manipulación de archivos: escritura y lectura de datos binarios, y cuáles
ON WEB
son los pasos a seguir para almacenar el estado de un objeto en y desde un archivo.
ESTRUCTURAS DE DATOS DINÁMICAS Y PLANTILLAS Aprenderemos a implementar una lista enlazada, una lista doblemente enlazada,
PATRONES DE DISEÑO
una pila dinámica y una cola dinámica.
Los patrones de diseño son soluciones
Finalmente, aprenderemos a reimplementar
documentadas a problemas clásicos
todas estas estructuras, pero de modo
surgidos en la construcción de sistemas
independiente del tipo de dato que manejen,
orientados a objetos. En este capítulo,
por medio de las plantillas.
veremos algunos de los patrones más populares que podremos aplicar.
ON WEB LA LIBRERÍA DE PLANTILLAS STL La librería STL es un poderoso aliado a la LENGUAJE UNIFICADO DE MODELADO
hora de codificar sistemas que requieran
Este sistema de notaciones está destinado
estructuras de datos, como arrays, listas,
al modelado de aplicaciones que se
pilas, colas y mapas. En este capítulo
basan, principalmente, en conceptos de la
veremos cómo hacer uso de ella y, así,
programación orientada a objetos. Este sistema
mejorar nuestra productividad.
INFORMACIÓN COMPLEMENTARIA A lo largo de este manual podrá encontrar una serie de recuadros que le brindarán información complementaria: curiosidades, trucos, ideas y consejos sobre los temas tratados. Para que pueda distinguirlos en forma más sencilla, cada recuadro está identificado con diferentes iconos: CURIOSIDADES E IDEAS
ATENCIÓN
DATOS ÚTILES Y NOVEDADES
www.redusers.com
SITIOS WEB
6
PRELIMINARES
Contenido El libro de un vistazo .................................................. 4
Fundamentos de C++
Información complementaria..................................... 5
¿Qué es un lenguaje de programación? ....................30
Introducción ............................................................. 12
Proceso de compilación .........................................30 Proceso de enlace .................................................31 Entornos de programación ....................................31 Un poco de historia ...................................................32
Programación orientada a objetos
Diferencias entre C y C++ .....................................34 Nuestro primer programa en C++.............................34
Introducción ..............................................................14
Comentarios ..........................................................36
Programación no estructurada ..............................15
Variables.....................................................................38
Programación procedural ......................................15
Asignación .................................................................39
Programación modular..........................................16 Programación orientada a objetos.........................16 ¿Qué es un objeto? ................................................17
if
Más características de los objetos .........................19 Aún más características de los objetos...................20 El análisis y el diseño en la programación
( expresión
orientada a objetos....................................................23 El lenguaje unificado de modelado ........................24 Resumen ....................................................................27
) sentencia
Actividades ................................................................28 else sentencia
televisor Entrada video
televisor
Constantes .................................................................41 Tipos de datos fundamentales ..................................42 Números enteros ...................................................42 Números no enteros ..............................................43
dvd
El operador sizeof .................................................45 Caracteres .............................................................46
dvd
El tipo de dato void ...............................................47 Booleanos..............................................................47 Operadores .................................................................47 Precedencia de operadores ...................................49
www.redusers.com
7
C++
Control del flujo de ejecución...................................51 Sentencias condicionales .......................................52 Sentencia de bucle ................................................62 Resumen ....................................................................71 Actividades ................................................................72
Clase y objeto La clase ....................................................................134 Las funciones miembros o métodos .....................137 El diagrama de clases ..............................................145 El diagrama de objetos ...........................................146 El constructor..........................................................147
Aspectos avanzados del lenguaje
El destructor............................................................150
Entrada y salida en C++ ............................................74
Propiedades estáticas..............................................152
El objeto cout ........................................................77
Métodos estáticos ...................................................155
Modificadores de acceso .........................................150
El objeto cin ..........................................................80
Restricciones de los métodos estáticos ................157
El preprocesador .......................................................81
El juego de la vida ...................................................158
Array ..........................................................................84
La resolución .......................................................158
Funciones...................................................................91
La propiedad .......................................................158
Funciones que retornan valores .............................93
Los métodos ........................................................159
Funciones con parámetros .....................................94
La función main...................................................168
Arquitectura de un programa complejo ...............106
Patrones de interés .............................................170
Los string en C++ ....................................................109
Resumen ..................................................................171
Funciones para la manipulación
Actividades ..............................................................172
de cstrings ...........................................................113 Estructuras ..............................................................120 Uso de una estructura..........................................121
Class Foo {
Enumeradores..........................................................126
//…
Enumeradores sin valor numérico asociado .........128 Fijando un valor inicial ........................................130
public:
Resumen ..................................................................131
//…
Actividades ..............................................................132
1 10000 1800
unAuto2 2 30000 1500
Métodos y propiedades públicas
protected: //…
unAuto1
Métodos y propiedades privadas
Métodos y propiedades protegidas
private: //…
Otros métodos y propiedades privadas
public: //…
Otros métodos y propiedades públicas
}
www.redusers.com
8
PRELIMINARES
Gestión de memoria
Herencia
Los punteros ............................................................174
¿Qué es la herencia? ................................................220
Cómo declarar una variable
Métodos inline.....................................................221
tipo puntero .........................................................175
Métodos constantes .............................................223
El operador new..................................................178
Crear subclases ...................................................224
Operador delete...................................................178
Modificadores de acceso en la herencia...............225
Punteros a array..................................................179
El modificador de acceso protected .....................227
Cadenas de texto .................................................184
Invocación de construtores ..................................233
Aritmética de punteros ........................................186
Invocación de destructores ..................................233
Las referencias....................................................191 Los punteros como parámetros de funciones ........................................................192 Los punteros como valor de retorno ....................198 Variables estáticas ..............................................201
5. Extraigo un elemento. Extraemos el último elemento que ingresó en la pila.
Puntero a objetos ....................................................208 Las propiedades ..................................................208 Utilizar la clase Cadena.......................................212 El constructor de copia ........................................213
m_iUltimoElemento
6 3 5 4 9
[5] [4] [3] [2] [1] [0]
Array de objetos ......................................................216 El puntero this .........................................................217
4
Resumen ..................................................................217 Actividades ..............................................................218 Constructores con parámetros .............................234 Composición ............................................................237 // Declaro el puntero y solicito memoria Int * pA = new int;
Ocultar el nombre de un método ............................238
?? pA
Composición vs. Herencia .......................................240
dir. de mem. al número entero
Herencia múltiple ....................................................242 *pA = 50;
El caso del diamante ...........................................243
50 pA
dir. de mem. al número entero
Caso de estudio .......................................................246 Cómo funciona una pila .......................................246
// Libero la memoria solicitada Delete pA;
// ERROR: La siguiente línea podría causar // un error en tiempo de ejecución *pA = 60;
50 pA
50 pA
Cómo funciona una cola.......................................246
dir. de mem. al número entero
dir. de mem. al número entero
Declaración de la clase Array ..............................251 60
Definición de la clase Array .................................252 Declaración de la clase Pila ................................255 Definición de la clase Pila ...................................256 Declaración de la clase Cola ................................258
www.redusers.com
9
C++
Definición de la clase Cola...................................259 Usar la pila y la cola ............................................262 Resumen ..................................................................263
Objetos persistentes
Actividades ..............................................................264
¿Qué es un objeto persistente?...............................302 Manejo de archivos en C++.....................................302 Escritura de un archivo ...........................................303 Lectura de un archivo ..........................................308
Polimorfismo
Lectura y escritura de datos binarios ...................314
¿Qué es el polimorfismo? .......................................266
Almacenar el estado de un objeto ..........................318
Sobrecarga de funciones.........................................266
Objetos con punteros ...........................................322
Parámetros opcionales ........................................271
Resumen ..................................................................325
Sobrecarga de operadores ......................................274
Actividades ..............................................................326
El modificador friend...........................................276 Sobrecarga de operadores unarios ......................279
1. Por medio del método open abrimos el archivo existente.
Operadores que no modifican el estado del objeto .............................................282
bla bla bla ejemplo.txt (archivo) 2. Leemos cierta cantidad de caracteres del archivo y la volcamos en un buffer de memoria.
;Juego
;Jugador
bla bla bla RegistrarJugador()
ejemplo.txt (archivo) bla bla bla EmpezarPartida
szBuf (memoria) RealizarMovida
3. Cerramos el archivo.
bla bla bla
Patrones de diseño ¿Qué es un patrón de diseño? .................................328 Operadores que modifican el estado del objeto ....283
El patrón Singleton .................................................328
Sobrecarga de operadores binarios .....................286
El patrón Método Plantilla......................................330
Métodos virtuales ....................................................288
El patrón Fábrica .....................................................332
Destructores virtuales .............................................297
El patrón Estrategia ................................................334
Resumen ..................................................................299
El patrón Observador ..............................................337
Actividades ..............................................................300
Resumen ..................................................................340
www.redusers.com
10
PRELIMINARES
1. La lista, antes de realizarse alguna modificación.
primer nodo NULL
NULL último nodo
2. Creamos un puntero que apunte al primer nodo de la lista, que es el nodo a extraer.
Lenguaje Unificado de Modelado
pNodoAExtraer
Introducción ............................................................342
primer nodo NULL
NULL último nodo
3. Modificamos el puntero al primer nodo de la lista que apunte al próximo nodo a extraer.
Diagramas de Casos de uso .....................................342
pNodoAExtraer
primer nodo
Diagramas de Clases................................................343 Diagramas de Objetos .............................................344 Diagramas de Secuencia .........................................344
NULL último nodo
NULL 4. Modificamos el puntero del nuevo primero nodo, para que apunte a NULL.
pNodoAExtraer
primer nodo
Diagramas de Estados .............................................346 Diagramas de Colaboración ....................................346
NULL último nodo
NULL NULL
Diagramas de Actividad...........................................347
5. Modificamos el puntero a próximo nodo del nodo a extraer, para que apunte NULL.
pNodoAExtraer
primer nodo NULL
Diagramas de Componentes ...................................348
NULL último nodo
NULL NULL
objeto
objeto anónimo
Listas doblemente enlazadas.....................................20
; CtrlRed
S: Entidad
Pila .......................................................................35 hCom enviar (x)
creación de un objeto temporal
mensaje foco de operación
Cola.......................................................................36
callback
llamada
objeto temporal <<creates>>
; Socket
conectar()
retorno
Plantillas ....................................................................37 Listas doblemente enlazadas con plantillas............47 Pila .......................................................................54 Cola.......................................................................55
enviar(x)
Los iteradores ............................................................55
<<destroy>>
Resumen ....................................................................59
recursión
Actividades ................................................................60 destrucción del objeto
línea de vida
ON WEB
La librería de plantillas STL ON WEB
Estructuras de datos dinámicas y plantillas
La librería STL .............................................................2 Las clases contenedoras..............................................2 La clase vector ........................................................2
Listas enlazadas ...........................................................2
La clase list .............................................................5
¿Qué es una lista enlazada?.....................................2
La clase stack..........................................................8
Implementación de lista enlazada ...........................2
La clase queue ........................................................9
La clase Nodo .........................................................3
La clase map .........................................................10
La clase Lista ..........................................................6
Resumen ....................................................................12
www.redusers.com
12
PRELIMINARES
Introducción Este libro está dirigido principalmente al programador novato y de nivel medio, así como al estudiante o al autodidacta. Los primeros cuatro capítulos son introductorios, y allí explicaremos detalladamente cuáles son los fundamentos del lenguaje, su sintaxis y sus sentencias básicas. También veremos cómo realizar nuestro primer programa. Llegando a la mitad del libro profundizaremos los conceptos relacionados con la programación orientada a objetos. Descubriremos qué es un objeto cuando hablamos de C++ y cómo tratar con él. Si usted es estudiante del nivel terciario o universitario de alguna carrera relacionada con la programación, seguramente encontrará muy útil el capítulo 5, en el que se explican algunos conceptos más avanzados, como la gestión dinámica de la memoria. Por otro lado, si es un programador práctico, tal vez desee utilizar las estructuras de datos de la librería STL; en el apéndice C veremos en detalle cómo emplearla. Entre otros temas, descubriremos qué es un iterador, qué es un contenedor y cómo hacer uso de ellos. Finalizado el libro, será capaz de crear sus propias aplicaciones C++ haciendo uso de las herramientas de diseño adecuadas. Con los conceptos aprendidos y los ejemplos vistos, estará preparado para comenzar a experimentar y realizar diversos proyectos. Todos los temas son tratados a través de diagramas visuales y ejemplos prácticos, en búsqueda de facilitar la comprensión de los conceptos.
www.redusers.com
Programación orientada a objetos En este primer capítulo, comenzaremos por conocer qué es la programación orientada a objetos y en qué puede ayudarnos. También veremos sus conceptos principales, la evolución del diseño y una introducción a la construcción de sistemas complejos.
▼
Introducción ............................ 14
▼
El análisis y el diseño en la
Programación no estructurada......... 15
programación orientada
Programación procedural ................ 15
a objetos................................... 23
Programación modular .................... 16
El lenguaje unificado
Programación orientada a objetos ... 16
de modelado .................................... 24
¿Qué es un objeto? ........................... 17 Más características de los objetos .... 19
▼
Resumen................................... 27
▼
Actividades............................... 28
Aún más características de los objetos ................................... 20
Servicio de atención al lector: usershop@redusers.com
14
1. PROGRAMACIÓN ORIENTADA A OBJETOS
Introducción Queremos utilizar la computadora para resolver problemas, ¿no es así? Podría ser un problema propuesto por un profesor de la facultad en un enunciado, el cálculo del saldo de una cuenta bancaria o la programación de un juego. El asunto es que para esto deberemos trasladar dichos problemas a nuestra PC, y este proceso de traslación implica una abstracción. Tal vez el término resulte extraño, pero desde niños estamos acostumbrados a realizar abstracciones: en aquellos tiempos, a veces jugábamos a ser otra persona situada en otro mundo; incluso la bañera podía convertirse en un mar embravecido. Tales ejemplos no son otra cosa que abstracciones, modelos de una realidad llevada a otro contexto por medio de una operación intelectual. Como decíamos, para trasladar un problema a la PC, también realizamos abstracciones; o sea, debemos crear un modelo simplificado de la realidad tomando los elementos que nos parezcan pertinentes y transformándolos en variables, por ejemplo, dentro de un programa. De este modo, nuestra máquina podrá entenderlo, ejecutarlo y, finalmente, habremos obtenido el resultado que estábamos buscando.
Programa Problema
abstracción
Figura 1. Para introducir un problema en una computadora, debemos realizar un proceso de abstracción. Para esto, sin duda, deberemos acudir a un lenguaje de programación, que no es otra cosa que una herramienta. El lenguaje nos permitirá especificar, a modo de instrucciones, cuáles son los pasos que tendrá que seguir la computadora para resolver el problema. Pero cómo especifiquemos la solución dependerá del modelo de programación que utilicemos. Entonces, lo mejor será repasar un poco los modelos más comunes.
www.redusers.com
15
C++
Programación no estructurada Si no estamos empleando ningún modelo de programación en particular, probablemente estemos programando de modo no estructurado. La programación no estructurada consiste en un programa extenso desarrollado dentro de una función principal que utiliza solo variables del tipo global. Este modo de programación puede ser aceptable para la resolución de problemas triviales; sin embargo, a medida que la complejidad se incrementa, comienza a ser muy engorroso su mantenimiento y el agregado de nuevas características.
Figura 2. En la programación no estructurada es difícil seguir el flujo de ejecución.
Programación procedural La mayor parte de los lenguajes de alto nivel soportan la creación de procedimientos, que son trozos de código que realizan una tarea determinada. Un procedimiento podrá ser invocado muchas veces desde otras partes del programa con el fin de aislar la tarea en cuestión y, una vez que finaliza su ejecución, retorna al punto del programa desde donde se realizó la llamada. Además, cada procedimiento tendrá su propio conjunto de datos, aunque podrá acceder a datos globales y a los pasados como parámetros desde el programa principal o desde otros procedimientos. De esta forma, podemos dividir nuestro problema en problemas más pequeños y, así, llevar la complejidad a un nivel manejable.
programa principal
p1
p2
p3
Figura 3. Programación procedural en programas pequeños o medianos. www.redusers.com
16
1. PROGRAMACIÓN ORIENTADA A OBJETOS
programa principal
p1
p2
p3
p4
p5
Figura 4. Programación procedural en un programa de tamaño mayor.
Programación modular La programación modular va un paso más allá con la creación de procedimientos: los agrupa en módulos según su función. De esta manera, el modelo es apto para sistemas más complejos que requieren programas más extensos. Cada módulo tendrá su propio conjunto de datos, creando un nivel intermedio de datos globales, ya que el conjunto de datos del módulo será global para los procedimientos del módulo, pero inaccesible a los programa principal
procedimientos de otros módulos. De este modo, la representación de un determinado concepto del problema podrá
p1
p3
ser llevado a un módulo; por ejemplo, la manipulación de archivos, donde dentro
p2
p4
del módulo en cuestión existirán distintas funciones para su creación, destrucción, lectura, escritura, etcétera.
p3 Módulo 1
Módulo 2
Figura 5. En la programación modular, cada módulo agrupa procedimientos.
Programación orientada a objetos En la programación orientada a objetos, el concepto de módulo es profundizado y se transforma en un objeto. Además, allí, un programa
www.redusers.com
Fundamentos de C++ En este capítulo haremos una introducción al lenguaje C++, expondremos sus principales virtudes, estudiaremos los elementos que lo componen y cómo crear programas con él.
▼
¿Qué es un lenguaje de
▼
Tipos de datos
programación? ......................... 30
fundamentales........................... 42
Proceso de compilación ................... 30
Números enteros.............................. 42
Proceso de enlace............................ 31
Números no enteros ......................... 43
Entornos de programación ............... 31
El operador sizeof............................ 45 Caracteres ....................................... 46
▼
▼
Un poco de historia ................. 32
El tipo de dato void .......................... 47
Diferencias entre C y C++................ 34
Booleanos ........................................ 47
Primer programa en C++ ........ 34
▼
Comentarios .................................... 36
▼
Variables................................... 38
Operadores ............................... 47 Precedencia de operadores.............. 49
▼
Control del flujo de ejecución. 51
Las palabras
Sentencias condicionales ................. 52
reservadas de C++ ........................... 38
Sentencia de bucle ........................... 62
▼
Asignación................................ 39
▼
Resumen................................... 71
▼
Constantes ............................... 41
▼
Actividades............................... 72
Servicio de atención al lector: usershop@redusers.com
30
2. FUNDAMENTOS DE C++
¿Qué es un lenguaje de programación? Un lenguaje de programación es, principalmente, una herramienta. Por medio de él podremos indicarle a nuestra computadora qué pasos debe seguir para resolver un determinado problema. Estos lenguajes suelen ser clasificados según distintos criterios; uno de ellos es su cercanía al lenguaje realmente comprendido por la plataforma. Los lenguajes de bajo nivel son los más cercanos a la arquitectura de la computadora, y el ensamblador es uno de ellos. Por otro lado, los lenguajes de alto nivel utilizan sentencias similares al lenguaje natural empleado por los seres humanos; de este modo, son más fáciles de aprender y, además, pueden ser trasladados a distintas arquitecturas por no poseer un código íntimamente relacionado con estas. En este grupo se encuentra la mayor parte de los lenguajes, como el C, C++, Pascal, Fortran, Basic, etcétera.
Proceso de compilación Ahora bien, tal vez nos preguntemos cómo es que si programamos utilizando sentencias en lenguaje similar al natural, en cualquiera de los lenguajes conocidos, la máquina puede entenderlo. Para esto, existe un proceso mediante el cual se traduce dicho código a un lenguaje entendible por la computadora.
Proceso de compilación archivo main.cpp
archivo main.obj
archivo dos.cpp
archivo dos.obj
archivo tres.cpp
archivo tres.obj
nuestro programa
Figura 1. Un proyecto está formado por un grupo de archivos de código. Cada archivo de código se compila de manera individual. www.redusers.com
31
C++
A este proceso se lo denomina compilación, y los programas que lo realizan se denominan compiladores. Normalmente cuando nuestro programa crece en cantidad de líneas, el proyecto suele ser dividido en diversos archivos de código.
Proceso de enlace Luego, terminado el proceso de compilación, habremos obtenido una serie de archivos (con terminación obj) que deberemos juntar para crear un archivo ejecutable. El proceso que une estos archivos y les adjunta además otros componentes, según corresponda, se denomina enlace, y el programa que realiza esta función es el enlazador. El archivo de salida a este es un proceso ejecutable.
Proceso de compilación
Proceso de enlazado
archivo main.cpp
archivo main.obj
archivo dos.cpp
archivo dos.obj
archivo tres.cpp
archivo tres.obj
archivo ejecutable
librerías estáticas del sistema (lib) Compilador C++
Enlazador
Figura 2. Procesos de compilación y enlace.
Entornos de programación Parece complicado… ¿Compilar y luego enlazar? ¿Cómo hacemos todo esto? Bueno, podríamos decir que somos afortunados: en la actualidad existen muchos entornos de desarrollo donde todos estos procesos se realizan mediante un simple golpe de tecla. La elección de qué entorno utilizar se deja a criterio del lector. Sin embargo, en el Apéndice B se recomiendan algunos de los más populares para el sistema operativo Microsoft Windows.
www.redusers.com
32
2. FUNDAMENTOS DE C++
Es importante hacer la distinción entre un entorno de desarrollo y un compilador: el entorno es simplemente una aplicación –usualmente, de ventanas– que nos permite gestionar un proyecto de programación de manera sencilla; el entorno de desarrollo, por su parte, utiliza el compilador y el enlazador automáticamente. Además, los entornos modernos colorean distintas palabras de nuestro código en función de las sentencias reconocidas del lenguaje, y esto mejora muchísimo la legibilidad del código. Y existen otras tantas funciones más que realiza el entorno para facilitarnos la vida, como ser:
• Permitir la configuración de los parámetros del compilador y el enlazador de manera sencilla.
• Mostrar el nombre de las estructuras de datos empleadas en nuestro proyecto para poder acceder a ellas fácilmente.
• Facilidades de búsqueda avanzada. • Facilidades de impresión. • Muchos entornos incluyen un depurador integrador para realizar un seguimiento paso a paso de la ejecución de nuestro programa.
Un poco de historia El lenguaje C fue desarrollado en los laboratorios Bell durante la década del 70, y proviene de otro lenguaje llamado B, que a su vez desciende de otro lenguaje llamado BCPL. Inicialmente, el lenguaje C fue desarrollado como un lenguaje de programación para escribir y mantener el sistema operativo UNIX, luego fue expandido para trabajar en otros sistemas. A medida que el lenguaje creció y maduró fue tornándose más popular. Actualmente, sus principales características son:
SIMPLIFICACIÓN Esta división se realiza con la intención de disminuir la complejidad en la organización de nuestro proyecto. Cada uno de estos archivos de código es compilado de manera individual.
www.redusers.com
Aspectos avanzados del lenguaje En este capítulo, aprenderemos a interactuar con el usuario por medio de una terminal de texto. Veremos también qué es un array, comenzaremos a trabajar con variables que contengan texto y crearemos nuestras propias funciones.
▼
Entrada y salida en C++ .......... 74
Funciones para la manipulación
El objeto cout .................................. 77
de cstrings...................................... 113
El objeto cin .................................... 80 ▼ ▼
El preprocesador...................... 81
▼
Array......................................... 84
Estructuras ............................120 Uso de una estructura .................... 121
▼
Enumeradores ........................126 Enumeradores sin valor numérico
▼
Funciones ................................. 91
asociado......................................... 128
Funciones que retornan valores ....... 93
Fijando un valor inicial................... 130
Funciones con parámetros ............... 94 Arquitectura de un programa
▼
Resumen.................................131
▼
Actividades.............................132
complejo ........................................ 106
▼
Los string en C++ ..................109
Servicio de atención al lector: usershop@redusers.com
74
3. ASPECTOS AVANZADOS DEL LENGUAJE
Entrada y salida en C++ El lenguaje C++, al igual que el lenguaje C, no incluye primitivas para realizar opciones de entrada/salida. En su lugar, existen librerías estándar que nos permiten, por ejemplo, escribir en la pantalla o tomar un carácter del teclado, es decir, manipular los flujos de entrada/salida a nuestro gusto. Un flujo (en inglés, stream) no es otra cosa que una secuencia de bytes que provienen de una fuente (flujos de entrada) o van hacia algún destino (flujos de salida). Al utilizar este concepto, C++ se desvincula de cuál sea la fuente o el destino concreto; un flujo de salida podría ser la pantalla o un archivo, y todo se manejaría básicamente del mismo modo. El lenguaje C define inicialmente tres flujos estándar:
• stdin, el flujo de entrada (conectado por defecto a la entrada del teclado); • stdout, el flujo de salida (conectado por defecto a la pantalla de la terminal);
•
stderr, un flujo de error (también conectado por defecto a la pantalla de la terminal).
teclado
flujos de entrada
pantalla
programa
flujos de salida
archivo
archivo
Figura 1. Flujos de entrada/salida en un programa C++. El lenguaje C++ hereda estos conceptos de C y agrega los siguientes tres objetos globales para manipularlos:
• El objeto cin, para manipular el flujo stdin. • El objeto cout, para manipular el flujo stdout. • El objeto cerr, para manipular el flujo stderr. Para poder utilizar estos objetos, bastará con que incluyamos el archivo de cabecera iostream del siguiente modo:
www.redusers.com
75
C++
#include <iostream>
Además, estos objetos, por estar definidos dentro del conjunto de datos estándar del lenguaje, se encuentran bajo el espacio de nombre std (abreviación de “estándar”). Por lo tanto, siempre debemos anteponer std:: a su uso. Por ejemplo: std::cout << “el texto va aquí”;
En lugar de anteponer siempre std, también podemos especificarlo al comienzo del programa: using namespace std;
Pero… ¿qué es un espacio de nombre? Dentro de una librería, existe una gran cantidad de declaraciones que especifican identificadores para cada tipo de dato. ¿Qué sucedería si utilizáramos un nombre ya existente en una librería para un tipo de dato creado por nosotros? El compilador arrojaría un error, por lo que nosotros deberíamos cambiar el nombre de nuestro tipo de dato. ¿Qué sucedería si deseáramos utilizar dos librerías que declarasen dos identificadores con el mismo nombre? En ese caso, modificar el código sería muy trabajoso, ya que dicha librería no fue escrita por nosotros. A modo de analogía, el lector podría pensar qué sucedería si el sistema de archivos utilizado por su sistema operativo no permitiera la creación de subdirectorios, es decir, si todos y cada uno de los archivos que contuviera su disco duro debiesen estar en el directorio raíz. ¡Sería un problema tener que estar renombrando archivos para que no existan dos con el mismo nombre! Un espacio de nombre (en inglés, namespace) en C++ sería equivalente a un directorio en un sistema de archivos. Así como pueden existir dos archivos con el mismo nombre dentro de dos directorios distintos, pueden existir dos identificadores iguales en distintos espacios de nombres.
www.redusers.com
76
3. ASPECTOS AVANZADOS DEL LENGUAJE
Para declarar una variable dentro de un espacio de nombres en particular, simplemente debemos proceder del siguiente modo: namespace MiEspacioDeNombre { int iMiVariable, }
Por lo tanto, el siguiente listado de c贸digo es incorrecto: int iValor; int iValor;
Pero el siguiente no: namespace Ns1 { int iValor; } namespace Ns2 { int iValor; }
Ahora bien, si queremos utilizar la variable declarada dentro de nuestro programa, tendremos que especificar el espacio de nombre al cual deseamos referirnos; de otro modo, el compilador s贸lo buscar谩 el identificador en el espacio de nombres global. namespace Ns1 { int iValor; } namespace Ns2
www.redusers.com
Clase y objeto Ya hemos realizado una introducción al lenguaje; ahora comenzaremos a ver temas relacionados específicamente con la programación orientada a objetos. Profundizaremos aspectos del lenguaje C++ a partir del estudio de una de sus estructuras principales: la clase.
▼
La clase ..................................134
▼
Las funciones miembros o métodos 137
Métodos estáticos .................155 Restricciones de los métodos estáticos ........................................ 157
▼
El diagrama de clases ............145
▼
El diagrama de objetos..........146
▼
El constructor ........................147
▼
El destructor ..........................150
▼
Modificadores de acceso .......150
▼
Resumen.................................171
▼
Propiedades estáticas............152
▼
Actividades.............................172
▼
El juego de la vida .................158 La resolución ................................. 158 La propiedad.................................. 158 Los métodos................................... 159 La función main ............................. 168 Patrones de interés........................ 170
Servicio de atención al lector: usershop@redusers.com
134
4. CLASE Y OBJETO
La clase Una clase es una estructura de datos. Su objetivo, como el de la estructura (struct), es crear nuevos tipos de datos para ser utilizados en un programa. De hecho, la estructura posee muchísimas similitudes con las clases, especialmente en C++. Recordemos que una estructura nos permite manejar un conjunto de datos como un elemento único. Esto es especialmente útil cuando el conjunto de datos posee una estricta relación entre sí y conforma una entidad. En el capítulo anterior, vimos el ejemplo de un automóvil en el que colocamos unas cuantas propiedades relacionadas con él dentro de una estructura. Una clase nos permite ir un paso más allá; no solo podremos declarar tipos nuevos especificando sus propiedades, sino que podremos incluir funciones que, por supuesto, también estarán muy relacionadas con la entidad en cuestión. Supongamos que deseamos implementar una aplicación en la cual requerimos modelar el concepto de un televisor. Para ello, consideraremos algunas de sus propiedades y estableceremos como regla que las propiedades de sintonía (m_uiCanal) y nivel de sonido (m_uiVolumen) solo deberían poder modificarse cuando el televisor esté encendido (m_bEncendido en verdadero). struct Televisor { bool m_bEncendido;
// Indica si la TV está encendida
unsigned int m_uiCanal;
// Indica sintonía del televisor
unsigned int m_uiVolumen; // Indica nivel de sonido };
Ahora, si deseáramos crear un televisor en nuestra aplicación, deberíamos crear una variable a partir de él del siguiente modo: int main() { Televisor tv1;
www.redusers.com
135
C++
// … }
Siguiendo con el ejemplo, si nuestra intención es cambiar el valor de la propiedad m_uiVolumen, bastaría con escribir: tv1.m_uiVolumen = 10;
Pero esto no siempre podrá ser correcto. Habíamos establecido que el nivel del sonido podría ser modificado cuando el televisor estuviera encendido, por lo tanto, tendríamos que haber codificado: if (tv1.m_bEncendido) tv1.m_uiVolumen = 10;
Entonces, el hecho de que el programador modifique el nivel de sonido cuando el televisor esté apagado queda bajo estricta responsabilidad. ¿Es esto lo que queremos? Pues no. Lo ideal sería poder establecer ciertas reglas en una entidad y que de ningún modo otro programador pudiera alterarlas; en definitiva, algo más parecido a un televisor real, es decir, si no se puede variar el nivel de sonido cuando el aparato se encuentra apagado, como usuarios, nada podemos hacer más que obedecer las reglas. Como una alternativa encaminada a subsanar este problema, podríamos crear una función que modificara el nivel de sonido del aparato, que retorne verdadero si lo ha podido hacer y falso de otro modo:
USO DE REFERENCIAS Como se puede ver en la función ModificarVolumen, para el parámetro Televisor hemos utilizado una referencia en lugar de hacer un pase por valor. Esto es conveniente por dos motivos: - Deseamos modificar el objeto original y no una copia que será finalmente destruida. - Pasar una dirección de memoria es más eficiente que copiar las propiedades de una estructura.
www.redusers.com
136
4. CLASE Y OBJETO
bool ModificarVolumen(Televisor & tv, unsigned int uiVolumen) { if (tv.m_bEncendido) {
tv.m_uiVolumen = uiVolumen; return true;
} else return false; }
Entonces, ahora podríamos utilizar esta función de la siguiente manera: int main() { Televisor tv1; // Modifico el nivel de sonido de tv1 ModificarVolumen(tv1, 10); return 0; }
Es necesario hacer el pase de tv1, ya que podríamos poseer varios objetos del tipo Televisor, y la función requiere saber a qué estructura debe modificar sus datos. De modo análogo, podríamos crear otras funciones relacionadas con el televisor: void Encender(Televisor & tv) { Tv.m_bEncendido = tue; } void Apagar(Televisor & tv) {
www.redusers.com
Gestión de memoria En este capítulo veremos cómo solicitar memoria en forma dinámica por medio de los punteros. De esta manera podremos crear objetos dinámicamente y hacer uso más eficiente de los recursos de la computadora. Veremos también qué son las referencias y cuándo es conveniente utilizarlas sobre los punteros.
▼
Los punteros ..........................174
▼
Puntero a objetos ..................208
Cómo declarar una variable
Las propiedades ............................. 208
tipo puntero ................................... 175
Utilizar la clase Cadena ................. 212
El operador new ............................ 178
El constructor de copia .................. 213
Operador delete ............................. 178 Punteros a array ............................ 179
▼
Array de objetos ....................216
▼
El puntero this .......................217
▼
Resumen.................................217
▼
Actividades.............................218
Cadenas de texto ........................... 184 Aritmética de punteros .................. 186 Las referencias .............................. 191 Los punteros como parámetros de funciones................................... 192 Los punteros como valor de retorno ...................................... 198 Variables estáticas ......................... 201
Servicio de atención al lector: usershop@redusers.com
174
5. GESTIÓN DE MEMORIA
Los punteros Si alguna vez escuchamos hablar de punteros, seguramente la frase fue acompañada de la palabra “difícil”. Sin embargo, no tiene por qué ser así. Un puntero es básicamente una construcción del lenguaje que nos permite obtener más control de la memoria de la computadora. Con él podemos crear objetos en forma dinámica y así utilizar eficientemente los recursos del sistema. Supongamos que deseamos almacenar una cierta cantidad de notas de un alumno en alguna estructura de datos. Si bien ya estudiamos este mismo ejemplo cuando vimos los array, ahora agregaremos un condimento extra: la cantidad total de notas de un alumno sólo se conoce cuando comienza a ejecutarse el programa, y puede variar de ejecución a ejecución. Antes, bastaba con crear un array de una determinada cantidad de elementos y listo, pero ahora, ¿qué haremos? Una solución posible sería crear un array absurdamente grande para que se puedan almacenar todas las notas que sean necesarias en cualquier caso. // Creo un array de un tamaño suficientemente grande int aiNotas[1000];
Pero esta solución tiene dos defectos:
• En algunos casos, podría no existir un máximo posible, es decir, la cantidad de elementos es realmente desconocida y puede ser tanto 5 como 250 o 100 000.
• Realizamos un uso muy poco eficiente de los recursos. Si, por ejemplo, creamos un array de 1000 elementos y luego usamos tan solo 10, estaremos desaprovechando muchísima memoria. Una solución que podríamos imaginar sería crear un array de tamaño especificable durante el tiempo de ejecución. De todas maneras, el compilador exige que el valor encerrado entre corchetes en la definición de un array sea una expresión constante, es decir, un valor conocido en tiempo de compilación. Por esa razón no se permite el siguiente código:
www.redusers.com
175
C++
int iTamArray; cout << “Ingrese el tamaño del array: “; cin >> iTamArray; cout << endl; // La siguiente expresión es INVALIDA, iTamArray no es una expresión constante int aiNotas[iTamArray]; // Sigo trabajando con aiNotas // …
Como excepción a la regla, el compilador gcc 3.2 sí acepta el código anterior. Para poder crear un array en el que su tamaño sea especificado en tiempo de ejecución, tendremos que solicitar memoria en forma dinámica utilizando punteros. Un puntero es una variable que no almacena directamente un dato, sino una dirección de memoria en la que se encuentra el dato en cuestión.
Cómo declarar una variable tipo puntero Supongamos que deseamos crear una variable puntero; para ello, deberemos especificar, como con cualquier otra variable, cuál es su tipo. Veamos: // Una variable puntero a un número entero int * pA;
Notemos que la declaración es muy similar a la de una variable convencional, solo que aquí, por ser un puntero, colocamos un signo asterisco (*) entre el tipo de dato y el identificador. pA es, entonces, una variable que contendrá una dirección de memoria donde habrá un número entero. Sin embargo, ¿en qué posición estará el número entero? Aún no lo hemos determinado, simplemente existe el contenedor de la dirección, pero todavía no se estableció qué
www.redusers.com
176
5. GESTIÓN DE MEMORIA
dirección será; el puntero, como cualquier otra variable, comienza con un valor basura en su interior:
pA
???
Figura 1. Un puntero aún no inicializado. Si en nuestro programa existiese otra variable entera, podríamos establecer que nuestro puntero apuntara a ella del siguiente modo: // Declaro una variable entera convencional y la inicializo en 10 int iUnaVariableEntera = 10; // Declaro un puntero a variable entera int * pA; // Ahora le asigno la dirección de memoria donde se encuentra iUnaVariableEntera // al puntero pA pA = & iUnaVariableEntera;
iUnaVariableEntera (0xFFFF0102) pA (0xFFFF0101)
10
0xFFFF0102
Figura 2. El contenido de la variable será un valor, y el contenido del puntero será una dirección de memoria. Notemos que, para obtener la dirección de memoria que le fue asignada a la variable entera iUnaVariableEntera, utilizamos el operador & (“dirección de”). Ahora es posible modificar el valor de la variable entera no solo por medio de ella, sino también por medio del puntero. Para esto, no deberemos hacer lo siguiente:
www.redusers.com
Herencia En este capítulo abordaremos herencia y composición, recursos fundamentales en el momento de crear el modelo de datos de nuestro sistema. Veremos cómo trabajan los constructores y destructores dentro del mecanismo de herencia y crearemos una pila y una cola utilizando un array.
▼
¿Qué es la herencia? ..............220
▼
Métodos inline ............................... 221
Herencia múltiple ..................242 El caso del diamante...................... 243
Métodos constantes ....................... 223 Crear subclases .............................. 224
▼
Caso de estudio......................246
Modificadores de acceso
Cómo funciona una pila.................. 246
en la herencia ................................ 225
Cómo funciona una cola ................. 246
El modificador de acceso protected 227
Declaración de la clase Array......... 251
Invocación de constructores ........... 233
Definición de la clase Array ........... 252
Invocación de destructores............. 233
Declaración de la clase Pila ........... 255
Constructores con parámetros........ 234
Definición de la clase Pila .............. 256 Declaración de la clase Cola .......... 258
▼
Composición ..........................237
▼
Ocultar el nombre
Definición de la clase Cola ............. 259 Usar la pila y la cola ...................... 262
▼
de un método .........................238
▼
Resumen.................................263
Composición vs. herencia ......240
▼
Actividades.............................264
Servicio de atención al lector: usershop@redusers.com
06 C++.indd 219
26/08/2014 09:08:31 a.m.
220
6. HERENCIA
¿Qué es la herencia? La herencia es un concepto fundamental de la programación orientada a objetos. Por medio de esta característica podremos definir nuestras clases a partir de otras más generales y solo agregar las propiedades y métodos de la especialización. En el Capítulo 1 habíamos hecho una introducción a este tema. Habíamos comentado que el modo en que funcionaba la herencia tenía mucho que ver con la forma en que definimos elementos empleando la tradición aristotélica, es decir, nos basamos en un objeto conocido para luego especificar las diferencias que dan origen al nuevo elemento. Debido a que nuestro sistema será una colección de clases relacionadas entre sí, es muy importante ver cómo estará confirmada su estructura. Por lo general, un diseño pobre genera más problemas que soluciones, ya que los errores se propagan rápidamente y se torna más difícil su expansión y mantenimiento. Volvamos al ejemplo de los dispositivos electrónicos del Capítulo 1. Ahora veremos cómo codificar dicho ejemplo en C++. En principio, nuestro interés era incorporar al sistema la entidad Televisor y la entidad Radio, debido a que son dispositivos electrónicos que poseen características en común, y, gracias a la programación orientada a objetos, podremos realizar el modelo de un modo muy natural. Crearemos una clase llamada DispElectronico, que representará un dispositivo electrónico con características comunes a estos, de modo que luego agregaremos la clase Televisor y la clase Radio, ambas subclases de DispElectronico. Veamos: class DispElectronico { // Indica si el dispositivo se encuentra encendido o apagado bool m_bEncendido; public: // Constructor DispElectronico(); // Enciende el dispositivo void Encender() { m_bEncendido = true; } // Apaga el dispositivo
www.redusers.com
06 C++.indd 220
26/08/2014 09:09:11 a.m.
221
C++
void Apagar() { m_bEncendido = false; } // Indica si el aparato está encendido bool EstaEncendido() cons { return m_bEncendido; } };
Analicemos un poco la declaración anterior, ya que estamos haciendo algo nuevo:
• La clase solo posee una propiedad, que es privada y representa el estado del aparato.
• Además, cuenta con cuatro métodos públicos: el constructor (siempre lleva el mismo nombre que la clase y no posee tipo), un método para “encender” el dispositivo (cambia el valor de la propiedad m_bEncendido), otro para “apagarlo” y, finalmente, un método que retorna el valor de la propiedad (no es posible accederla de otro modo porque es privada).
Métodos inline Los métodos pueden ser definidos dentro de la misma clase. Hasta aquí siempre realizábamos la declaración en un archivo de cabecera (terminación .h): class X { // Declaración del método void m1(); };
Y luego, lo definíamos en un archivo de código fuente (con terminación .cpp): void X::m1() { // … }
www.redusers.com
06 C++.indd 221
26/08/2014 09:09:11 a.m.
222
6. HERENCIA
Pero veamos el siguiente caso: class X { // Declaración del método void m1() { // … } };
Ahora hemos introducido una novedad: la declaración y definición conjunta dentro del cuerpo mismo de la clase. Todo esto, en el archivo de cabecera. Por otra parte, ambas son maneras correctas de definir un método, y su modo de uso es exactamente el mismo. Entonces, ¿cuál es la diferencia? ¿Cuándo usar un modo de definición y cuándo el otro? Los métodos declarados dentro del cuerpo de la clase son del tipo inline, algo que podríamos lograr realizando una definición clásica y anteponiendo esta palabra al tipo: inline void X::m1() { // … }
¿Qué quiere decir esto? Normalmente, cuando realizamos una llamada a una función o método, el compilador debe introducir una serie de operaciones en código ensamblador para el pasaje de parámetros y para resguardar ciertos registros del CPU que serán restablecidos al retorno. Por lo tanto, si estos pasos no estuviesen y el código propio de la función fuera copiado, en lugar de realizar dicha llamada, nos estaríamos ahorrando algunas operaciones que, en cierto tipo de sistemas con funciones muy solicitadas, pueden hacer una diferencia. Las funciones o métodos inline poseen la característica de no ser funciones reales, sino que el compilador se toma el trabajo de
www.redusers.com
06 C++.indd 222
26/08/2014 09:09:12 a.m.
Polimorfismo En este capítulo estudiaremos el polimorfismo, tercer gran pilar de la programación orientada a objetos. También estudiaremos qué mecanismos implementa el lenguaje para dar soporte a esta característica: entre ellos, la sobrecarga de funciones y operadores, los métodos virtuales y las clases abstractas.
▼
¿Qué es el polimorfismo? .....266
▼
Sobrecarga de funciones .......266
Sobrecarga de operadores binarios.......................................... 286
▼
Parámetros opcionales .................. 271
▼
Métodos virtuales ..................288
Sobrecarga de operadores.....274
▼
Destructores virtuales ...........297
▼
Resumen.................................299
▼
Actividades.............................300
El modificador friend ..................... 276 Sobrecarga de operadores unarios.... 279 Operadores que no modifican el estado del objeto........................ 282 Operadores que modifican el estado del objeto........................ 283
Servicio de atención al lector: usershop@redusers.com
266
7. POLIMORFISMO
¿Qué es el polimorfismo? En pocas palabras, el polimorfismo es la habilidad que poseen los objetos para reaccionar de modo diferente ante los mismos mensajes. Un objeto del tipo Puerta, al igual que un objeto de tipo Ventana, podrá recibir el mensaje Abrir; sin embargo, cada uno de ellos reaccionará de modo diferente. En C++ el polimorfismo se encuentra íntimamente relacionado con el mecanismo de sobrecarga y los métodos virtuales. Veamos de qué se trata esto.
Sobrecarga de funciones En lenguajes como el C, es común encontrar librerías que poseen grupos de funciones que realizan la misma tarea pero con distintos tipos de datos en los parámetros. De este modo, si la tarea consiste en mostrar en pantalla el dato pasado como parámetro, podremos encontrarnos con casos como el siguiente: void ImprimirInt(int iNum); void ImprimirChar(char cCar); void ImprimirFloat(float fNum); …
El lenguaje C++ provee un mecanismo mucho más conveniente para realizar la misma tarea. Es posible crear funciones que tengan el mismo nombre y difieran en la cantidad y/o tipo de parámetros. Teniendo esto en cuenta, el ejemplo anterior podría ser reescrito como: void Imprimir(int iNum); void Imprimir(char cCar); void Imprimir(float fNum); …
www.redusers.com
267
C++
Queda claro que este procedimiento es mucho más sensato. De este modo, el programador no deberá recordar el postfijo que conforma el identificador de la función apropiada, ya que todas las funciones se llaman del mismo modo. A esta característica del lenguaje se la denomina “sobrecarga de funciones”. El compilador elige qué versión es la correcta en el momento de realizar la llamada, dependiendo de la cantidad y/o el tipo de parámetros pasados. Todo lo dicho se puede aplicar también a los métodos de una clase, que son, en definitiva, funciones miembros. Como el constructor es, además, un método, haciendo uso de la sobrecarga podríamos crear distintos tipos de constructores y dejar que el compilador seleccione cuál de ellos ejecutar en función del modo en que se crea el objeto. Veamos un ejemplo. Supongamos que deseamos construir una clase que represente un tipo de dato “fecha” (no existente como tipo de dato fundamental en C++): class Fecha { float m_fFecha; public: // … };
La clase almacenará internamente la fecha como cantidad de días desde el 30 de diciembre del año 1899 a la medianoche. Sin embargo, permitirá construir un objeto aceptando otros formatos más amigables para el programador. Veamos: El primer constructor podría no recibir parámetros y fijar la fecha en una arbitraria (por ejemplo, la actual): Fecha();
El segundo podría recibir la fecha en tres parámetros que indiquen día, mes y año respectivamente:
www.redusers.com
268
7. POLIMORFISMO
Fecha (int iDia, int iMes, iAnio);
Un tercer constructor aceptaría la fecha en día, mes, año, hora, minuto y segundo. Fecha (int iDia, int iMes, iAnio, int iHora, int iMin, int iSeg);
Por último, el constructor de copia, que recibirá una referencia constante a un objeto del mismo tipo: Fecha (const Fecha & rFec);
Por lo tanto, la declaración de la clase quedará del siguiente modo: class Fecha { float m_fFecha; public: //
Constructor estándar Fecha(); // Constructor día/mes/año Fecha (int iDia, int iMes, iAnio); // Constructor día/mes/año hh:mm:ss Fecha (int iDia, int iMes, iAnio, int iHora, int iMin, int iSeg); // Constructor de copia
TIPO DE DATO RETORNADO POR FUNCIÓN No es posible crear dos funciones con el mismo identificador que solo difieran en el tipo de dato retornado, debido a que el compilador no podrá distinguir cuándo se desea llamar a una o a otra: bool Foo(int iNum); char Foo(int iNum);
www.redusers.com
Objetos persistentes En este capítulo estudiaremos cómo almacenar y recuperar el estado de un objeto, utilizando los recursos que el lenguaje C++ nos ofrece para la manipulación de archivos.
▼
¿Qué es un objeto
▼
persistente?............................302
Almacenar el estado de un objeto ...........................318 Objetos con punteros ...................... 322
▼
Manejo de archivos en C++ .....302
▼
Escritura de un archivo .........303 Lectura de un archivo .................... 308
▼
Resumen.................................325
▼
Actividades.............................326
Lectura y escritura de datos binarios ............................ 314
Servicio de atención al lector: usershop@redusers.com
302
8. OBJETOS PERSISTENTES
¿Qué es un objeto persistente? Sabemos que el estado de un objeto está compuesto por el valor de todas sus propiedades. Es probable que, en algunos casos, deseemos conservar dicho estado una vez finalizada la ejecución del programa, para poder recuperarlo luego. Entonces, el objeto debería tener la habilidad de escribir y leer el valor de sus propiedades en un medio de almacenamiento no volátil (como por ejemplo, un archivo). Pero para poder entender mejor esto, es necesario que veamos primero cómo manipular archivos con C++.
Manejo de archivos en C++ Al igual que el lenguaje de programación C, C++ no cuenta con primitivas de entrada y salida. Sin embargo, dispone de un conjunto de clases creadas para este fin, conocidas como las clases iostream. Stream se puede traducir como “flujo”, y es en este concepto que se basa el paquete de clases iostream para trabajar con dispositivos de entrada/salida. Utilizando flujos es que el lenguaje logra independizarse en gran medida de dispositivos físicos a los cuales finalmente accede; entonces, enviar datos a un archivo, impresora o pantalla se suscita de forma muy similar. Podríamos pensar el flujo como una construcción programática a la que es posible insertarle y extraerle bytes de modo secuencial y aleatorio. Específicamente hablando de archivos, a medida que invocamos métodos de lectura/escritura, existe un cursor que avanza sobre el archivo de modo que, si en la primera operación de lectura hemos leído, por ejemplo, la primera mitad del archivo, en la segunda operación leeremos la otra mitad. Por otro lado, existen flags (banderas) que mantienen estos objetos e indican el estado en el cual se encuentra el archivo. Cuando lleguemos al final de este, se activará un flag denominado eof (por End Of File; fin de archivo), y así sabremos que no quedan más datos en el recurso. Analicemos su estructura simplificada en la Figura 1. Luego veremos cómo se efectúa la escritura y lectura en un archivo desde C++.
www.redusers.com
303
C++
ios
istream
ostream
ifstream
ofstream
iostream
fstream
Figura 1. Diagrama de clases ios simplificado.
• La clase ios es la clase base de esta estructura; si bien no es una clase abstracta, usualmente no se crean instancias de esta. Simplemente contiene propiedades comunes a flujos de entrada y salida.
• La clase istream deriva de ios y representa un flujo de entrada. • La clase ostram deriva de ios y representa un flujo de salida. • La clase ifstream deriva de istream y se especializa en la lectura de archivos.
• La clase oftream deriva de ostream y se especializa en la escritura de archivos.
• La clase iostream deriva de istream y ostream, por lo tanto, posee ambas interfaces y nos permite manipular un mismo flujo de entrada y de salida.
• La clase fstream deriva de iostream y se encuentra especializada en la manipulación de archivos en modo lectura y escritura.
Escritura de un archivo Para escribir en un archivo, haremos uso de la clase oftream o la clase fstream. De la interfaz de ifstream utilizaremos los siguientes métodos:
www.redusers.com
304
8. OBJETOS PERSISTENTES
• Abrir: el método open de la clase oftream abre un archivo en modo escritura. void open(const char* szName, int nMode = ios::out, int nProt = filebuf::openprot);
Parámetros:
• szName: nombre del archivo. • nMode: modo en que deseamos abrir el archivo. Por defecto, posee un valor asignado, que es ios::out, pero se pueden especificar otros, como ios::binary (si deseamos abrir un archivo en modo binario) ios::nocreate (si no deseamos que el archivo se cree, en caso de no existir), ios::app (si deseamos agregar datos al archivo existente, ya que, de otro modo, eliminará todo su contenido), etcétera.
• nProt: modo de protección del archivo (si se desea abrir el archivo en modo compartido, exclusivo, etcétera).
• Cerrar un archivo: el método close cierra el archivo. void close();
• Verificar si un archivo se encuentra abierto: el método is_open indica si el archivo relacionado con el objeto se encuentra abierto, es decir, si ya se ha invocado el método open de manera satisfactoria. int is_open() const;
Mediante este código, el método retorna un valor distinto de cero si el archivo se encuentra abierto, y, de no ser así, retorna cero.
• Escribir en un archivo: para escribir en un archivo, debemos utilizar métodos de clase ostream, es decir, de la clase padre a ofstream, aunque ciertamente esto es indistinto al programador, ya que un objeto del tipo oftream poseerá la interfaz de dicha clase más las interfaces que se hereden de las clases superiores.
www.redusers.com
Patrones de diseño Los patrones de diseño son soluciones documentadas a problemas clásicos surgidos en la construcción de sistemas orientados a objetos. En este capítulo, veremos algunos de los patrones más populares que podremos aplicar en nuestros programas.
▼
¿Qué es un patrón
▼
El patrón Estrategia ..............334
▼
El patrón Observador.............337
▼
Resumen.................................340
de diseño? ..............................328 ▼
El patrón Singleton ...............328
▼
El patrón Método Plantilla ....330
▼
El patrón Fábrica ...................332
Servicio de atención al lector: usershop@redusers.com
328
9. PATRONES DE DISEÑO
¿Qué es un patrón de diseño? Es común que se presenten problemas similares en diversos contextos en la construcción de sistemas informáticos, para los cuales se han propuesto soluciones que finalmente han sido aceptadas por su eficiencia, robustez y elegancia. Un patrón es exactamente eso: una buena solución documentada a un problema común. Un patrón de diseño posee un nombre, describe un problema para el cual el patrón es aplicable, describe una solución independiente del lenguaje con diagramas (UML) y pseudocódigo (también suele ejemplificar en lenguajes populares como C++, Java y Smalltalk) y, finalmente, informa de consecuencias en la aplicación del patrón (costos y beneficios, ya que, en algunos casos, ciertos patrones son mutuamente excluyentes, y la aplicación de uno u otro varía en función del contexto y el criterio del diseñador).
El patrón Singleton La intención de este patrón es asegurarse de que exista solo una instancia de clase y proveer un modo global de acceder a ella. Tradicionalmente, esto ha sido resuelto de dos maneras: la primera consiste en pasar una referencia o un puntero del objeto en cuestión a cada uno que requiera acceder a sus métodos y propiedades. Esto es muy ineficiente ya que estaremos agregando un nuevo parámetro en la llamada a, probablemente, muchos métodos. La segunda se trata de crear una variable global que pueda ser vista desde todo el programa. Esto es poco elegante y hasta perjudicial en muchos casos, ya que,
LA DESTRUCCIÓN DEL SINGLETON En nuestra implementación, no existe manera de hacerlo; será eliminado por el sistema cuando el programa finalice. Es posible crear un método llamado LiberarInstancia y un contador –también estático–, de modo que se incremente en cada TomarInstancia y se decremente en cada LiberarInstancia (en este último método, cuando el contador es cero, el objeto es destruido).
www.redusers.com
329
C++
por ejemplo, nadie evita que se cree otra instancia del objeto en cuestión y, así, existe la posibilidad de ocasionar un error en la ejecución del programa o de que este no se
Singleton
comporte del modo esperado.
Figura 1. Diagrama de clases del patrón Singleton.
-m_pInstancia -… +TomarInstacion() #Singleton()
El patrón Singleton se basa en una clase que posee, básicamente, un método estático y una propiedad estática. // Archivo .h ----- class Singleton { public: static Singleton * TomarInstancia(); protected: // Constructor Singleton() {} private: static Singleton * m_pInstancia; }; // Archivo .cpp ----- // Inicializo el valor de la propiedad estática Singleton * Singleton::m_pInstancia = 0; // …
La clase cuenta con un método estático que, como tal, puede ser invocado sin la necesidad de crear ningún objeto. Este método, llamado TomarInstancia, debe ser invocado para obtener un puntero al objeto único. Veamos de qué forma implementarlo: Singleton * Singleton::TomarInstancia() {
www.redusers.com
330
9. PATRONES DE DISEÑO
if (!Singleton::m_pInstancia) Singleton::m_pInstancia = new Singleton(): return Singleton::m_pInstancia; }
Como se puede apreciar, existe una sentencia condicional if que verifica el valor de la propiedad estática m_pInstancia. Si su valor es NULL (0), entonces se crea un nuevo objeto y se retorna el valor del punteo a este. Notemos que esta condición solo será cierta en la primera llamada al método, y luego, en toda ocasión, se retornará el mismo puntero. El haber colocado el constructor de la clase como protegido evita que el objeto sea instanciado del modo tradicional; en pocas palabras, el siguiente código causará un error de compilación: Singleton obj;
Para poder acceder a los métodos del Singleton, deberemos codificar: Singleton * pObj = Singleton::TomarInstancia();
El patrón Método Plantilla La tarea de este patrón consiste en definir el esqueleto de un algoritmo en una operación retardando partes de su implementación a subclases. Método Plantilla permite que las subclases redefinan ciertos métodos sin cambiar la estructura del algoritmo. Este patrón puede ser utilizado cuando:
• Ciertos pasos de un algoritmo deben implementarse en subclases. • Cierto código común debe ser colocado en superclases para evitar la duplicación. El código sube a una superclase, y se sobrescriben los métodos que correspondan.
www.redusers.com
Puedes comprar este libro impreso o en formato eBook u optar por algunos de los títulos que conforman la biblioteca USERS. En USERSHOP encontrarás las últimas novedades y material informativo de cada título, que te ayudará a decidir la compra.
¡Recibe promociones semanales exclusivas en tu casilla de correo!
usershop.redusers.com + 54 (011) 4110-8700
usershop@redusers.com
VE IN RS CL GR IÓN D UYE AT IGITA IS L
C++ es un lenguaje orientado a objetos popular, moderno y en constante evolución. A la hora de desarrollar, es la opción más poderosa y robusta que jamás se haya creado. EN ESTE LIBRO APRENDERÁ: Introducción: programación no estructurada, procedural, modular y orientada a objetos. Análisis y diseño. Fundamentos de C++: diferencias con C, variables, palabras reservadas, constantes, booleanos, operadores, sentencias condicionales y bucles. Aspectos avanzados del lenguaje: entrada y salida, objetos cout y cin, preprocesador, array, funciones, strings y enumeradores. Clases y objetos: diagramas, constructor y destructor, modificadores de acceso, propiedades y métodos estáticos. Gestión de memoria: punteros, operadores new y delete, variables estáticas.
Esta obra es una versión revisada del libro C++, publicado en 2004 por esta misma editorial.
Herencia: métodos inline y constantes, subclases, composición vs. herencia. Polimorfismo: sobrecarga de funciones y de operadores, modificador friend, métodos y destructores virtuales.
array
NIVEL DE USUARIO
-m_iCantElementos : int -m_piArray +FijaTam() +FijaValor() +LeerValor() +Limpiar()
Juego
Básico / Intermedio
Jugador
+RegistrarJugador() +EmpezarPartida()
+RealizarMovida() 1
2
CATEGORÍA Desarrollo
pila -m_iUltimoElementos : int +Insertar() +Extraer() +Vaciar() +FijaTam()
cola -m_iPrimerElementos : int -m_iUltimoElementos : int +Insertar() +Extraer() +Vaciar() +FijaTam()
JugadorBot1
JugadorBot2
+RealizarMovida()
+RealizarMovida()
REDUSERS.com
PROFESOR EN LÍNEA
En nuestro sitio podrá encontrar noticias relacionadas y también participar de la comunidad de tecnología más importante de América Latina.
Ante cualquier consulta técnica relacionada con el libro, puede contactarse con nuestros expertos: profesor@redusers.com.
GRATIS
C++ PROGRAMACIÓN ORIENTADA A OBJETOS
El presente libro tiene el doble objetivo de introducirnos y profundizar tanto en lo que se refiere a la programación orientada a objetos, como al lenguaje C++, que será nuestra principal herramienta a la hora de implementar los conceptos teóricos que vayamos estudiando. A lo largo de los distintos capítulos, conoceremos los fundamentos del lenguaje, su sintaxis básica y la manera de escribir el primer programa en él. Además, veremos aspectos avanzados, como el preprocesador, el uso de arrays, funciones, estructuras, espacios de nombre, clases, objetos y gestión de memoria, entre otros. Analizaremos también herencia, polimorfismo, estructuras de datos dinámicas y plantillas.
VERSIÓN DIGITAL
C++ PROGRAMACIÓN ORIENTADA A OBJETOS
TIPOS DE DATOS Y OPERADORES PREPROCESADOR, ARRAYS, FUNCIONES Y STRINGS DIAGRAMAS DE CLASES Y DE OBJETOS HERENCIA Y POLIMORFISMO ESTRUCTURAS DE DATOS DINÁMICAS Y PLANTILLAS
INCLUYE INTRODUCCIÓN A UML
LENGUAJE UNIFICADO DE MODELADO