Sei sulla pagina 1di 123

c++

leer
ndice general

1 Introduccin 1
1.1 Conceptos Bsicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
[1]
1.1.1 Qu es un Lenguaje de Programacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Historia de C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2.1 Qu es C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.3 Herramientas Necesarias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.4 Consejos iniciales antes de programar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.5 Ejemplos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.6 Referencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

2 Lo ms bsico 4
2.1 Estructura de un programa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.1.1 Directivas de preprocesador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.1.2 Declaraciones globales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.1.3 Declaracin de funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.2 Proceso de desarrollo de un programa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2.1 Comentarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.2.2 Utilizacin de la consola o terminal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.3 Sintaxis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.3.1 El punto y coma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.3.2 Espacios y tabuladores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.4 Tipos primitivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.4.1 Espacio que ocupan la variables (en mquinas x86) . . . . . . . . . . . . . . . . . . . . . . . 12
2.5 Tipos enumerados (enum) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.6 Tipos denidos por el usuario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.7 Variables y constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

3 Iteraciones y decisiones 15
3.1 Sentencias de decisin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.2 Sentencia if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

i
ii NDICE GENERAL

3.2.1 Sentencia switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18


3.2.2 Operador condicional ternario ?: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.3 Sentencias de iteracin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.3.1 Sentencias For . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.3.2 Sentencia while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.3.3 Sentencia do - while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.4 Sentencias break y continue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.4.1 break . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.4.2 continue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

4 Estructuras 22

5 Estructuras de datos 23
5.1 Estructuras bsicas en C, C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
5.1.1 variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
5.2 Matrices o Arreglos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
5.2.1 Matrices estticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
5.2.2 Acceso a los miembros de una matriz de datos: . . . . . . . . . . . . . . . . . . . . . . . 25
5.2.3 Matrices dinmicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
5.3 Estructuras compuestas (struct, union, class) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
5.3.1 Acceso a los miembros de una estructura . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
5.3.2 Estructuras anidadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
5.3.3 Herencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
5.3.4 Estructura de campos de bits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
5.4 union: Sintaxis general . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
5.5 class: sintaxis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
5.6 struct vs. class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

6 Funciones 31

7 Funciones 32
7.1 Deniendo una funcin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
7.2 Parmetros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
7.3 Llamar a una funcin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
7.4 Funciones void . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
7.5 Funciones anidadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
7.6 Funciones de tipo puntero (*) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
7.7 Variables estticas y automticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
7.8 Parmetros constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
7.9 Parmetros con valor por defecto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
NDICE GENERAL iii

7.10 Parmetros de tipo puntero . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35


7.11 Parmetros estructurados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
7.12 Funciones sobrecargadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
7.13 Nmero variable de parmetros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

8 Streams 38

9 Streams, entrada y salida de datos 39


9.1 La iostream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
9.2 Streams automticos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
9.3 Operadores de direccionamiento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
9.4 Banderas de I/O . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
9.4.1 Banderas de formato: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
9.4.2 Manipuladores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

10 Streams para archivos o cheros 42


10.1 Abrir y cerrar archivo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42

11 Arrays y cadenas de texto 44


11.1 Arrays y cadenas de texto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
11.2 Indices de un array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
11.3 Dimensiones de un array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
11.3.1 Array unidimensional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
11.3.2 Array bidimensional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
11.4 Declaracin de arrays en C, C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
11.4.1 Iteraciones dentro de un array (vector) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
11.4.2 Iteraciones dentro de un array (matriz) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
11.5 Cadenas de caracteres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
11.5.1 La biblioteca string . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
11.6 Cadenas en C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
11.7 Arrays en C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49

12 Desarrollo Orientado a Objetos 50


12.1 Desarrollo Orientado a Objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
12.2 Caractersticas de la programacin orientada a objetos . . . . . . . . . . . . . . . . . . . . . . . . . . 50

13 Objetos y Clases 52
13.1 Clases y Objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
13.2 Fundamentos de Clases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
13.2.1 Resolucin de mbito . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
13.2.2 Acceso a las funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
iv NDICE GENERAL

13.2.3 Ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
13.3 Miembros de una clase ( mtodos y atributos ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
13.4 Visibilidad de los miembros de una clase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
13.5 Subclases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
13.6 Herencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
13.6.1 Herencia por extensin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
13.6.2 Agregacion o composicin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
13.7 Constructores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
13.8 Destructores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57

14 Sobrecarga de Operadores 59
14.1 Sobrecarga de operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
14.2 Mi primer sobrecarga . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
14.3 Sintaxis general . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
14.4 Sobrecarga permitida de operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
14.5 Sobrecarga del operador << ( iostream ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
14.6 Sobrecarga del operador >> ( istream ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
14.7 Operadores amigos ( friend ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
14.8 Sobrecarga de operadores dentro de una clase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62

15 Herencia 63
15.1 Introduccin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
15.2 INTRODUCIENDO LA HERENCIA. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
15.3 CONTROL DE ACCESO DE LA CLASE BASE. . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
15.3.1 USANDO MIEMBROS PROTEGIDOS. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
15.3.2 USANDO PROTECTED PARA LA HERENCIA DE UNA CLASE BASE. . . . . . . . . . . 68
15.3.3 REVISANDO public, protected, y private . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
15.3.4 HEREDANDO MULTIPLES CLASES BASE . . . . . . . . . . . . . . . . . . . . . . . . . 68
15.3.5 CONSTRUCTORES, DESTRUCTORES, Y HERENCIA . . . . . . . . . . . . . . . . . . . 69
15.3.6 PASANDO PARAMETROS A LOS CONSTRUCTORES DE LA CLASE BASE . . . . . . . 70
15.3.7 GARANTIZANDO ACCESO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
15.3.8 LEYENDO GRAFICOS DE HERENCIA EN c++ . . . . . . . . . . . . . . . . . . . . . . . 72
15.3.9 CLASES BASE VIRTUALES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72

16 Funciones virtuales 74
16.1 Introduccin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
16.2 PUNTEROS A TIPOS DERIVADOS. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
16.2.1 REFERENCIAS A TIPOS DERIVADOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
16.2.2 FUNCIONES VIRTUALES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
NDICE GENERAL v

16.2.3 LAS FUNCIONES VIRTUALES SON HEREDADAS . . . . . . . . . . . . . . . . . . . . . 77


16.2.4 PORQUE FUNCIONES VIRTUALES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
16.2.5 UNA SIMPLE APLICACION DE LAS FUNCIONES VIRTUALES . . . . . . . . . . . . . 78
16.2.6 FUNCIONES VIRTUALES PURAS Y CLASES ABSTRACTAS . . . . . . . . . . . . . . . 79
16.2.7 ENLACE TEMPRANO VS TARDIO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
16.2.8 POLIMORFISMO Y EL PURISTA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81

17 Punteros 82
17.1 Punteros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
17.1.1 Sintaxis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
17.1.2 Operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
17.1.3 Punteros y vectores (arrays) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
17.1.4 Aritmtica de Punteros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
17.1.5 Matrices de punteros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
17.1.6 Formas de pasar un valor por referencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
17.1.7 Punteros a funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
17.1.8 Ordenamiento burbuja . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
17.1.9 Proyecto de colas para un banco . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85

18 Estructuras II 87

19 Introduccin 88
19.1 Pilas o Stacks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
19.1.1 Pila en arreglo esttico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
19.1.2 Pila dinmica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
19.2 Colas o Queues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
19.2.1 Cola en un arreglo esttico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
19.3 Colas de doble enlace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92

20 Plantillas 94

21 Plantillas 95
21.1 Introduccin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
21.2 Un paso hacia adelante . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
21.3 Una mejor solucin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
21.4 La clase vector desde la perspectiva de la POO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
21.5 Una plantilla para la clase vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98

22 Excepciones 99

23 Excepciones. Motivacin histrica 100


vi NDICE GENERAL

24 Conformacin de los bloques try y catch 101

25 Control de excepciones 102


25.1 Excepciones genricas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
25.2 Excepciones de clases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
25.3 La clase exception . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103

26 Librera Estndar de Plantillas 105


26.1 Biblioteca estndar de plantillas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
26.2 Iteradores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
26.3 Iteradores reversos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106

27 Biblioteca Estndar de Plantillas 108


27.1 Biblioteca estndar de plantillas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
27.2 Iteradores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
27.3 Iteradores reversos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109

28 Vectores 111
28.1 C++ vector estndar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
28.2 Colas de doble n ( deque ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
28.3 Tabla de mtodos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112

29 Pilas 113

30 Colas 114
30.1 Origen del texto y las imgenes, colaboradores y licencias . . . . . . . . . . . . . . . . . . . . . . . . 115
30.1.1 Texto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
30.1.2 Imgenes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
30.1.3 Licencia del contenido . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
Captulo 1

Introduccin

1.1 Conceptos Bsicos

1.1.1 Qu es un Lenguaje de Programacin[1]


Antes de hablar de C++, es necesario explicar que un lenguaje de programacin es una herramienta que nos permite
comunicarnos e instruir a la computadora para que realice una tarea especca. Cada lenguaje de programacin posee
una sintaxis y un lxico particular, es decir, forma de escribirse que es diferente en cada uno por la forma que fue creado
y por la forma que trabaja su compilador para revisar, acomodar y reservar el mismo programa en memoria.
Existen muchos lenguajes de programacin de entre los que se destacan los siguientes:

1. C

2. C++

3. Basic

4. Ada

5. Java

6. Pascal

7. Python

8. Fortran

9. Smalltalk

1.2 Historia de C++


C++ es un lenguaje de programacin creado por Bjarne Stroustrup en los laboratorios de At&T en 1983. Stroustrup tom
como base el lenguaje de programacin ms[referencia] popular en aquella poca el cual era C.
El C++ es un derivado del mtico lenguaje C, el cual fue creado en la dcada de los 70 por la mano del nado Dennis
Ritchie para la programacin del sistema operativo (un sistema parecido a Unix es GNU/Linux), el cual surgi como
un lenguaje orientado a la programacin de sistemas (System Programming) y de herramientas (Utilities) recomendado
sobre todo para programadores expertos, y que no llevaba implementadas muchas funciones [cmo cules?] que hacen
a un lenguaje ms comprensible.

1
2 CAPTULO 1. INTRODUCCIN

Sin embargo, aunque esto en un inicio se puede convertir en un problema, en la prctica es su mayor virtud, ya que permite
al programador un mayor control sobre lo que est haciendo. Aos ms tarde, un programador llamado Bjarne Stroustrup,
creo lo que se conoce como C++.
Necesitaba ciertas facilidades de programacin, incluidas en otros lenguajes pero que C no soportaba, al menos direc-
tamente, como son las llamadas clases y objetos, principios usados en la programacin actual. Para ello redise C,
ampliando sus posibilidades pero manteniendo su mayor cualidad, la de permitir al programador en todo momento tener
controlado lo que est haciendo, consiguiendo as una mayor rapidez que no se conseguira en otros lenguajes.
C++ pretende llevar a C a un nuevo paradigma de clases y objetos con los que se realiza una comprensin ms humana
basndose en la construccin de objetos, con caractersticas propias solo de ellos, agrupados en clases. Es decir, si yo
quisiera hacer un programa sobre animales, creara una clase llamada animales, en la cual cada animal, por ejemplo un
pato, sera un objeto, de tal manera que se ve el intento de esta forma de programar por ser un el reejo de cmo los
humanos (en teora) manejamos la realidad[referencia].
Se dice que nuestro cerebro trabaja de forma relacional[referencia] (relacionando hechos), es por ello que cada vez que
recuerdas algo, (cuentas un hecho), termina siendo diferente (se agregan u omiten partes).

1.2.1 Qu es C++
C++ es un lenguaje de programacin orientado a objetos que toma la base del lenguaje C y le agrega la capacidad de
abstraer tipos como en Smalltalk.
La intencin de su creacin fue el extender al exitoso lenguaje de programacin C con mecanismos que permitieran la
manipulacin de objetos. En ese sentido, desde el punto de vista de los lenguajes orientados a objetos, el C++ es un
lenguaje hbrido [por? fusionar con el siguiente].
Posteriormente se aadieron facilidades de programacin genrica, que se sum a los otros dos paradigmas que ya estaban
admitidos (programacin estructurada y la programacin orientada a objetos). Por esto se suele decir que el C++ es un
lenguaje de programacin multiparadigma.

1.3 Herramientas Necesarias


Las principales herramientas necesarias para escribir un programa en C++ son las siguientes:

1. Un equipo ejecutando un sistema operativo.


2. Un compilador de C++
(a) Windows MingW (GCC para Windows) o MSVC (compilador de microsoft con versin gratuita)
(b) Linux (u otros UNIX): g++
(c) Mac (con el compilador Xcode)
3. Un editor cualquiera de texto, o mejor un entorno de desarrollo (IDE)
(a) Windows:
i. Microsoft Visual C++ (conocido por sus siglas MSVC). Incluye compilador y posee una versin gratuita
(versin express)
ii. Bloc de notas (no recomendado)
iii. Editor Notepad++
iv. DevCpp (incluye MingW - en desuso, no recomendado, incluye tambin un compilador)
v. Code::Blocks
(b) Linux (o re-compilacin en UNIX):
i. Gedit
1.4. CONSEJOS INICIALES ANTES DE PROGRAMAR 3

ii. Kate
iii. KDevelop
iv. Code::Blocks
v. SciTE
vi. GVim
(c) Mac:
i. Xcode (con el compilador trae una IDE para poder programar)

4. Tiempo para practicar


5. Paciencia

Adicional

1. Ingls (Recomendado)

2. Estar familiarizado con C u otro lenguaje derivado (PHP, Python, etc).

Es recomendable tener conocimientos de C, debido a que C++ es una mejora de C, tener los conocimientos sobre este te
permitira avanzar mas rapido y comprender aun mas. Tambien, hay que recordar que C++, admite C, por lo que se puede
programar (reutilizar), funciones de C que se puedan usar en C++.
Aunque No es obligacion aprender C, es recomendable tener nociones sobre la programacin orientada a objetos en el
caso de no tener conocimientos previos de programacin estructurada. Asimismo, muchos programadores recomiendan
no saber C para saber C++, por ser el primero de ellos un lenguaje imperativo o procedimental y el segundo un lenguaje
de programacin orientado a objetos.

1.4 Consejos iniciales antes de programar


Con la prctica, se puede observar que se puede confundir a otros programadores con el cdigo que se haga. Antes de
siquiera hacer una lnea de cdigo, si se trabaja con otros programadores, ha de tenerse en cuenta que todos deben escribir
de una forma similar el cdigo, para que de forma global puedan corregir el cdigo en el caso de que hubieran errores o
rastrearlos en el caso de haberlos.
Tambin es muy recomendable hacer uso de comentarios (comenta todo lo que puedas, hay veces que lo que parece obvio
para ti, no lo es para los dems) y tratar de hacer un cdigo limpio y comprensible, especicando detalles y haciendo
tabulaciones, aunque te tome un poco mas de tiempo, es posible que mas adelante lo agradezcas tu mismo.

1.5 Ejemplos
/* Programa que nos permite saber si el nmero ingresado es par o no */ #include <stdio.h> #include <conio.h> int main
() { int numero; printf (Ingrese el valor de numero: "); scanf ("%d, &numero); if(numero%2 == 0) { printf ("\n***El
numero es par\n); } else { printf ("\n***El numero es impar\n); } getch(); }

1.6 Referencias
[1] Lenguaje de Programacion Denicion de Wikipedia
Captulo 2

Lo ms bsico

2.1 Estructura de un programa


En general, los programas escritos en C++ tienen una estructura (bsica) compuesta por tres secciones:

1. Directivas de preprocesador

2. declaraciones globales

3. declaracin de funciones

2.1.1 Directivas de preprocesador

Los compiladores de C++ proporcionan bibliotecas de funciones, y cada biblioteca de funciones tiene asociada un archivo
de denicin que se denomina cabecera. Para utilizar algo de una biblioteca en un programa (por ejemplo, una funcin),
hay que colocar al principio del programa una directiva de preprocesamiento seguida de la cabecera de la biblioteca entre
signos de menor que y mayor que (<>).
A continuacin se muestra un tpico ejemplo de una directiva de preprocesador:
#include <iostream>

En el ejemplo anterior, la directiva invocada es include y la cabecera iostream, e indican al preprocesador que debe incluir
la libreria iostream a nuestro programa. Cabe sealar que todas las directivas comienzan con el simbolo numeral #. Dentro
de las directivas de preprocesdor se encuentran:

1. Macros de preprocesador (#dene y #undef)

2. Inclusiones condicionales (#ifdef, #ifndef, #if, #endif, #else and #elif)

3. Control (#line)

4. Error (#error)

5. Inclusin de chero (#include)

6. Pragma (#pragma)

4
2.1. ESTRUCTURA DE UN PROGRAMA 5

Macros de preprocesador

Para denir macros de preprocesador, usamos la directiva #dene. La sintaxis es:


#dene identicador reemplazo

Cuando el procesador encuentra esta directiva, este reemplaza todas las ocurrencias del identicador y son sustituidas por
reemplazo. Cuando se requiere desactivar una macro, a partir de cierta parte del cdigo, se utiliza la directiva #undef. La
sintaxis es:
#undef nombre_macro

Inclusiones condicionales

Estas directivas permiten incluir o descartar partes de codigo, si se cumplen algunas condiciones.

#ifdef: Permite que una seccion del programa sea compilada solo si la macro especicada como parametro ha sido
denida, sin importar el valor de esta. Por ejemplo:

#ifdef TABLE_SIZE int table[TABLE_SIZE]; #endif

El ejemplo anterior indica que, si la macro TABLE_SIZE se encuentra denida, entonces procede a la creacin de una
matriz de enteros de tamao TABLE_SIZE. Cabe sealar que el n para esta directiva es la directiva #endif

#ifndef: Permite realizar exactamente todo lo contrario a #ifdef. La lineas de cdigo que se encuentren entre #ifndef
y #endif, sern ejecutadas siempre y cuando la macro sealada como parmetro de #ifndef no se encuentre denida
aun. Por ejemplo:

#ifndef TABLE_SIZE #dene TABLE_SIZE 100 #endif int table[TABLE_SIZE];

En el ejemplo anterior, se indica que si la macro TABLE_SIZE no est denida an, entonces se dene. Cabe sealar que
el n para la directiva #ifndef es, al igual que #ifdef, #endif.

#if, #else y #elif (o else if): Estas directivas tienen el mismo signicado que los comandos condicionales de cualquier
lenguaje de programacion. Por ejemplo:

#if TABLE_SIZE>200 #undef TABLE_SIZE #dene TABLE_SIZE 200 #elif TABLE_SIZE<50 #undef TABLE_SIZE
#dene TABLE_SIZE 50 #else #undef TABLE_SIZE #dene TABLE_SIZE 100 #endif int table[TABLE_SIZE];

En el ejemplo anterior, se muestra el uso de todas las directivas condicionales del preprocesador.

Control

Cuando compilamos un programa y un error ocurre durante el proceso de compilacin, el compilador muestra un mensaje
de error con las referencias al nombre del archivo donde ocurri el error y un nmero de lnea, por lo que es ms fcil
encontrar el cdigo que genera el error. La directiva #line nos permite controlar ambas cosas, los nmeros de lnea en los
archivos de cdigo, as como el nombre del archivo que queremos que aparece cuando un error ocurre. Su formato es:
#line numero nombre_de_archivo
6 CAPTULO 2. LO MS BSICO

Donde nmero es el nuevo nmero de lnea que se asignar a la siguiente lnea de cdigo. Los nmeros de lnea de las lneas
sucesivas se incrementarn uno por uno desde este punto en adelante."nombre_de_archivo es un parmetro opcional que
permite redenir el nombre del archivo que se mostrar. Por ejemplo:
#line 20 asignacion de variable int a?;

Este cdigo generar un error que se mostrar como un error en el archivo de asignacin de variable, lnea 20.

Error

Esta directiva cancela la compilacin cuando es encontrada, generando un error de compilacin que puede ser especicado
segn un parmetro de un ejercicio. Por ejemplo:
#ifndefcplusplus #error A Se requiere compilador de C++ #endif

En el ejemplo anterior, la compilacin es cancelada si la macro __cplusplus no est denida (Esta macro es denida por
defecto en todos los los compiladores de C++).

Inclusin de chero

Cuando el preprocesador encuentra la directiva #include, este reemplaza todas las ocurrencias de ella por el archivo o
cabecera especicada. Existen dos formas de utilizar #include:

#include <cabecera>: Es usado para incluir las cabeceras proporcionadas por defecto, por ejemplo, la librera
estndar (string, iostream, etc.).

#include archivo : Es usado para incluir archivos.

Pragma

la directiva #pragma es utilizada para especicar las distintas opciones del compilador. Estas opciones son especicadas
dependiendo del compilador que se utilice. Si el compilador no permite un argumento para #pragma, esto es ignorado y
no genera un error de sintaxis.

2.1.2 Declaraciones globales


En esta seccion se declaran todas variables y cabeceras de funciones que seran vistas de manera global, es decir, que su
alcance es total en el programa. Por ejemplo:
#include <iostream> #dene PI 3.1415 std::string autor: Wikibooks"; int adicion(int, int);

En el ejemplo anterior, se denen (ademas de las directivas de preprocesador, vistas en la seccin anterior) una variable y
una funcin. La variable autor podr ser utilizada por todas las funciones del programa. Mientras que la funcin adicion
solo fue declarada de la forma inline. Una funcin se dice declarada inline cuando solo se seala su nombre y sus
tipos de entrada y salida. Su denicin completa se reserva para mas adelante.

2.1.3 Declaracin de funciones


La ultima seccin del programa es la declaracin de funciones. La primera funcin que se debe declarar es la funcin
principal o main. La funcin main es la mas importante, pues es la que es invocada cuando el programa se ejecuta. Toda
instruccin que no sea declarada dentro de esta, simplemente no sera considerada. A continuacin de la funcin main,
2.2. PROCESO DE DESARROLLO DE UN PROGRAMA 7

se permite la denicin completa de las funciones declaradas en la seccin de declaraciones globales. En el siguiente
ejemplo, mostraremos la estructura denitiva de un programa y la declaracin completa de sus funciones:
#include <iostream> #dene PI 3.1415 std::string autor: Wikibooks"; int adicion(int, int); int main(int argc, char **argv)
{ std::cout<<"El resultado de la suma de 1 y 2 es "<<adicion(1,2)<<std::endl; return 0; } int adicion(int a, int b) { return
a+b; }

Los parmetros de entrada de la funcin main es algo que se abordar mas adelante.

2.2 Proceso de desarrollo de un programa


Si se desea escribir un programa en C++ se debe ejecutar como mnimo los siguientes pasos:

1. Escribir con un editor de texto plano un programa sintcticamente vlido o usar un entorno de desarrollo (IDE)
apropiado para tal n

2. Compilar el programa y asegurarse de que no han habido errores de compilacin

3. Ejecutar el programa y comprobar que no hay errores de ejecucin

Este ltimo paso es el ms costoso, por que en programas grandes, averiguar si hay o no un fallo prcticamente puede ser
una tarea totmica.
Como ejemplo, si se desea escribir un archivo con el nombre hola.cpp y en l escribir un programa con emacs, por ejemplo,
que es un programa de edicin de textos, se puede, en GNU, ejecutar el siguiente comando:
$emacs hola.cpp &
Para otros sistemas operativos u otros entornos de desarrollo, no necesariamente se sigue este paso.
A continuacin se escribe el siguiente cdigo en C++:
Ejemplo

// Aqu generalmente se suele indicar qu se quiere con el programa a hacer // Programa que muestra 'Hola mundo' por
pantalla y naliza // Aqu se sitan todas las librerias que se vayan a usar con #include, // que se ver posteriormente
// Las librerias del sistema son las siguientes #include <iostream> // Funcin main // Recibe: void // Devuelve: int //
Funcin principal, encargada de mostrar Hola Mundo,por pantalla int main(void) { // Este tipo de lneas de cdigo que
comienzan por '//' son comentarios // El compilador los omite, y sirven para ayudar a otros programadores o // a uno
mismo en caso de volver a revisar el cdigo // Es una prctica sana poner comentarios donde se necesiten, std::cout <<
Hola Mundo << std::endl; // Mostrar por std::cout el mensaje Hola Mundo y comienza una nueva lnea return 0; // se
devuelve un 0. //que en este caso quiere decir que la salida se ha efectuado con xito. }

Mediante simple inspeccin, el cdigo parece enorme, pero el compilador lo nico que leer para la creacin del progra-
ma es lo siguiente:
Ejemplo

#include <iostream> int main(void){ std::cout << Hola Mundo << std::endl; return 0; }

Como se puede observar, este cdigo y el original no dieren en mucho salvo en los saltos de lnea y que los comentarios,
de los que se detallan posteriormente, estn omitidos y tan slo ha quedado el esqueleto del cdigo legible para el
compilador. Para el compilador, todo lo dems, sobra.
8 CAPTULO 2. LO MS BSICO

O este otro, que es, en parte, como el lenguaje C, en su versin C99, es:
Ejemplo

#include <stdio.h> #include <stdlib.h> int main(void) { printf( Hola Mundo\n ); return EXIT_SUCCESS; // 'EXIT_SUCCESS'
es una denicin que est dentro de 'stdlib.h' // tambien funciona return 0 }

Nota: si se usa Windows, el cdigo es el mismo, pero debemos agregar un metodo mas para que el programa se mantenga
abierto y no se cierre la consola, cosa que en GNU, no es necesaria por que la consola ya esta abierta (al mandar a ejecutar).
Para esto podemos usar cin.get() que nos permitira leer del teclado, por lo que el programa no nalizara, hasta que el
usuario pulse enter.
Ejemplo

#include <iostream> int main(void) { std::cout << Hola Mundo << std::endl; std::cin.get(); //con 'std::cin.get();' lo que
se hace es esperar hasta que el usuario pulse enter. return 0; }

Los pasos siguientes son para una compilacin en GNU o sistema operativo Unix. En Windows tampoco es aplicable.
Con ctrl-x ctrl-s se guarda el archivo. Ahora para generar el ejecutable del programa se compila con g++ de la siguiente
forma:
$ g++ hola.cpp -o hola
Para poder ver los resultados del programa en accin, se ejecuta el programa de la siguiente forma:
$./hola
Y a continuacin se debe mostrar algo como lo siguiente:
Hola Mundo

2.2.1 Comentarios

Cuando se escriben programas es muy til agregar comentarios que ayuden a explicar lo que realiza un programa. En
C++ se pueden utilizar tres tipos de comentarios: al estilo C, al estilo C++ y usando preprocesador.
Los comentarios al estilo C se caracterizan por lo siguiente: comenzar el bloque de comentarios con /* y terminar dicho
bloque de comentarios con */
Ej:
/* Este es un comentario al estilo C. Todo lo escrito dentro de las etiquetas de apertura y cierre es un comentario. A estos
comentarios se le llaman multilinea, logicamente por el hecho de permitir varias lineas de comentarios. */

Si se usan este tipo de etiquetas de comentarios, hay que tener cuidado con el cierre (*/), por que el compilador puede
tomar todo el texto como comentario, o cerrar antes de lo deseado.
Usando el estilo de cdigo de C++ slo pueden ocupar una lnea como en el siguiente cdigo:
// Este es un comentario al estilo C++

Una buena prctica de programacin es pensar que se programa a sabiendas de que otro programador, tal vez el lector
mismo en un futuro, tenga que desencriptar qu quiso hacer ah y por qu.
Otra posible forma de comentar cdigo es usando el preprocesador. Esto se detallar ms adelante en profundidad, por
ahora la parte til del preprocesador que interesa es la siguiente:
#if 0 Comentarios sobre el programa /parte del programa. Pueden ocupar mltiples lneas. Ms complicado de visualizar
2.2. PROCESO DE DESARROLLO DE UN PROGRAMA 9

que los comentarios C/C++ #endif

Este tipo de comentarios se usan rara vez. Generalmente son difciles de localizar, incluso para programadores experi-
mentados que trabajan en papel, y son fciles de distinguir en casi cualquier IDE. Es recomendable indicar que se tratan
de comentarios, o directamente no usarlos, salvo si son grandes cantidades de comentarios. Se ver ms adelante que
tambin puede tener otros usos.

2.2.2 Utilizacin de la consola o terminal

En los ejemplos anteriores se utilizaron 'std::cout'. 'std::cout' es un objeto que permite escribir en la consola (la terminal
en GNU/Unix/MacOSX), solo se puede utilizar gracias a que se ha incluido deniciones de su uso con la lnea de cdigo
'#include <iostream>'.
std::cout << ALGO;

Donde ALGO puede ser lo que sea que 'std::cout' sea capaz de mostrar por consola. Ms adelante se trata ms sobre ello
en detalle y aclarando posibles dudas que ahora puedan surgir. Tambin se utiliz 'std::endl', esto permite que el texto se
escriba en una nueva lnea.
Un ejemplo ms completo sobre la escritura en consola es el siguiente. Ha de tenerse en cuenta que se han eliminado
algunos comentarios superuos del primer programa debido a que ahora se est tratando con ms detalle acerca del uso
de imprimir texto en la consola:
Ejemplo

// Programa que muestra diversos textos por consola // Las libreras del sistema usadas son las siguientes #include <ios-
tream> // Funcin: main // Recibe: void // Devuelve: int // Es la funcin principal encargada de mostrar por consola
diferentes textos int main(void) { // Ejemplo con una nica lnea, se muestra el uso de std::cout y std::endl std::cout <<
Bienvenido. Soy un programa. Estoy en una linea de codigo. << std::endl; // Ejemplo con una nica lnea de cdigo que
se puede fraccionar mediante el uso de '<<' std::cout << Ahora " << estoy fraccionado en el codigo, pero en la consola
me muestro como una unica frase. << std::endl; // Uso de un cdigo largo, que cuesta leer para un programador, y que se
ejecutar sin problemas. // *** No se recomienda hacer lneas de esta manera, esta forma de programar no es apropiada
*** std::cout << Un gran texto puede ocupar muchas lineas. << std::endl << Pero eso no frena al programador a que
todo se pueda poner en una unica linea de codigo y que << std::endl << el programa, al ejecutarse, lo situe como el
programador quiso << std::endl; return 0; // Y se termina con xito. }

Se reta a compilar este cdigo y a observar sus resultados. En este momento el lector est capacitado para escribir pro-
gramas que impriman por pantalla el mensaje que se quiera.
Atencin: Se hace hincapi en la posibilidad de que las palabras acentuadas no se puedan mostrar en la consola. Depende
completamente del compilador que se pueda ver lo siguiente:
std::cout << programacin";

Con algunos compiladores, ver 'programacin', pero con otros puede ver incluso 'programaci n'.
Advertencia: cout puede ser utilizado sin tener std:: de forma previa porque se puede introducir una directiva, denomi-
nada 'using', que acomoda todos los cout. De otro modo habra un error de compilador. Este tema se trata en detalle ms
adelante.
using namespace std;
10 CAPTULO 2. LO MS BSICO

2.3 Sintaxis
Sintaxis es la forma correcta en que se deben escribir las instrucciones para el computador en un lenguaje de programacin
especco. C++ hereda la sintaxis de C estndar, es decir, la mayora de programas escritos para el C estndar pueden ser
compilados en C++.

2.3.1 El punto y coma

El punto y coma es uno de los simblos ms usados en C, C++; y se usa con el n de indicar el nal de una lnea de
instruccin. El punto y coma es de uso obligatorio.
ejemplo

clrscr(); //Limpiar pantalla, funciona solo con la librera conio de Borland C++ x = a + b; string IP = 127.0.0.1"; //
Variable IP tipo string cout << IP << endl; // Devuelve 127.0.0.1 char Saludo[5] = Hola"; // Variable Saludo tipo char
cout << Saludo[0] << endl; // Igual a H cout << Saludo[1] << endl; // Igual a o cout << Saludo[2] << endl; // Igual a l
cout << Saludo[3] << endl; // Igual a a

El punto y coma se usa tambin para separar contadores, condicionales e incrementadores dentro de una sentencia for
ejemplo

for (i=0; i < 10; i++) cout << i;

2.3.2 Espacios y tabuladores

Usar caracteres extras de espaciado o tabuladores ( caracteres tab ) es un mecanismo que nos permite ordenar de manera
ms clara el cdigo del programa que estemos escribiendo, sin embargo, el uso de estos es opcional ya que el compilador
ignora la presencia de los mismos. Por ejemplo, el segundo de los ejemplos anteriores se podra escribir como:
for (int i=0; i < 10; i++) { cout << i * x; x++; }

y el compilador no pondra ningn reparo.

2.4 Tipos primitivos


En un lenguaje de programacin es indispensable poder almacenar informacin, para esto en C++ estn disponibles los
siguientes tipos que permiten almacenar informacin numrica de tipo entero o real:

Los valores dependen de la arquitectura utilizada. Los mostrados son los que generalmente se encuentran en una
mquina tpica de arquitectura 32 bits.

El modicador long

El modicador long le indica al compilador que el tipo debe utilizar ms bits que los normalmente utilizados por ejemplo
si tenemos en una maquina de 32 bits como un Pentium de Intel, normalmente de un int ocupara 32 bits, pero si al declarar
un entero le antecedemos long, este entero ocupa 64 bits, el siguiente cdigo muestra como utilizar este modicador:
int corto; // Entero de 32 bits long int largo; // Entero de 64 bits
2.4. TIPOS PRIMITIVOS 11

El Modicador short

Similar al anterior, pero indica que se deben utilizar menos bits. Por ejemplo, en un computador de 32 bits, un short int
ocupa 16 bits.

El Modicador unsigned

El modicador unsigned es utilizado nicamente con los enteros, su utilizacin permite utilizar en los enteros nicamente
la parte positiva,
int a; // Almacena valores entre 32768 y 32767 unsigned int a; // Almacena valores entre 0 y 65535

El Modicador register

Este modicador sobre una variable le indica al compilador que la variable debe almacenarse en un registro en el compi-
lador, que para el caso de los IA32, es un registro real de la propia CPU, y por tanto el tiempo de acceso es ms rpido
respecto a la memoria RAM. Hoy en da apenas se utiliza este modicador, ya que los compiladores son capaces de
determinar de manera ptima la asignacin de registros a variables del programa.

El Modicador volatile

Al contrario que el modicador registrer, volatile obliga al compilador a forzar el cdigo resultante de manera que la
variable modicada con este modicador, sea almacenada siempre en la memoria. El efecto que tiene es que cuando la
variable se modica con otro valor, dicha variable se almacena directamente en memoria y no queda localizado el valor
slo en el registro de la CPU como pasaba en el caso de register o en condiciones normales. Un uso muy comn en el que
se suele emplear este modicador, es para acceder a variables que estn siendo utilizadas por drivers o por perifricos, ya
que si no declarsemos esta propiedad, podra darse el caso que la CPU usase el valor de la variable, por lo que la cach
guarda el valor, y poco despus la CPU usase de nuevo dicha variable, pero como sta est en cache, la CPU coge el valor
que existe en la cach, que puede ser bien distinta al real puesto que un posible perifrico puede haber modicado su
valor.

El Modicador static

Dependiendo del entorno donde se declare la variable que la modiquemos como static, puede signicar dos cosas muy
distintas:

1. Si declaramos una variable static dentro del cuerpo de una funcin, lo que estamos indicndole al compilador es
que dicha variable sea inicializada solo una vez (la primera vez que se llama a la funcin), y el resto de veces que
se llame a la funcin, la variable contendr el ltimo valor asignado. Esta variable slo podr ser visible desde la
funcin que declara dicha variable. Por ejemplo:

void mifuncion(){ static int i=0; cout<<"En la entrada i vale "<<i<<endl; for(int j=0;j<10;j++) i++; cout<<"En la salida
i vale "<<i<<endl; }

1. Si declaramos una variable static fuera del cuerpo de una funcin, lo que le estamos indicando al compilador, es
que dicha variable es privada para el modulo donde se implementa el cdigo del contexto de la variable, es decir,
que otro chero objeto binario, no podr tener acceso a dicha variable utilizando el modicador extern. Ejemplo:

#import prueba.h //variable privada para prueba.cpp static int i=0; void mifuncion(){ cout<<"En la entrada i vale
"<<i<<endl; for(int j=0;j<10;j++) i++; cout<<"En la salida i vale "<<i<<endl; }
12 CAPTULO 2. LO MS BSICO

Tenga en cuenta que para este ltimo caso, usted podr acceder a la variable y desde cualquier funcin que dena dentro
de prueba.cpp, pero no tendr acceso desde cualquier chero objeto o fuente que no sea prueba.cpp

2.4.1 Espacio que ocupan la variables (en mquinas x86)

El espacio en bits que ocupan en la computadora una variable de este tipo se puede ver en la siguiente tabla:

Rango de los Tipos Primitivos

El rango que puede almacenar los tipos primitivos en C++ es muy importante, para poder saber cual es el rango de
valores que puede almacenar un tipo es necesario conocer el nmero de bits del tipo. El caso para enteros y para otantes
es distinto. Para enteros se debe saber si el tipo es con signo o sin signo. si es sin signo el rango de valores que puede
almacenar es el siguiente:
[0, 2Bits 1]
Si el tipo es con signo el rango es el siguiente
[2Bits1 , 2Bits1 1]
Para ilustrar lo anterior supongamos que tenemos un entero de 16 bits sin signo, entonces el rango de valores que puede
almacenar es el siguiente:
[0, 216 1] = [0, 65535]
Para obtener el rango de un entero de 32 bits con signo se puede realizar el siguiente calculo:
[231 , 231 1] = [2147483648, 2147483647]
El caso de los nmeros otantes es distinto y depende en gran manera del compilador y el procesador que este utili-
zando. Sin embargo hoy en da la mayora de los compiladores y los procesadores utilizan en estndar de la IEEE para
representacin en coma otante. Para saber mas al respecto ver IEEE oating-point standard.

2.5 Tipos enumerados (enum)


Los tipos enumerados son un mecanismo usado en C y C++ con el objetivo de agrupar de alguna manera constantes
simblicas. Para denir tipos enumerados se usa la palabra reservada enum.
Ejemplo 1
enum dias { domingo, lunes, martes, miercoles, jueves, viernes, sabado };

En el ejemplo anterior se dene por medio de enum el tipo enumerado dias, en el mismo ejemplo se debe observar que
dentro de la construccin se denen las constantes simblicas: domingo, lunes, ... sabado; y que a las mismas el compilador
les asignar respectivamente y por defecto los valores: 0, 1, 2, 3, 4, 5, 6. Es decir, las constantes mencionadas pueden
usarse dentro del programa y este sabr a que valor hace referencia cada una de las mismas. Por ejemplo, la instruccin:
cout << domingo; desplegar 0 en la pantalla. El siguiente ejemplo muestra como usar las constantes enumeradas en un
ciclo for.
for (int d = domingo; d <= sabado; d++) cout << d;

En el siguiente ejemplo se dene por medio de enum el tipo enumerado colores, en el mismo se debe de observar la
elinacin del comportamiento por defecto ya que a la primera constante (gris) se le asigna en forma especca el valor de
7, de tal manera que la siguiente constante (grisoscuro) tendr un valor igual a 8 y la constante amarillo ser igual a 14.
Ejemplo 2
2.6. TIPOS DEFINIDOS POR EL USUARIO 13

enum colores { gris = 7, grisoscuro, amarillo = 14 };

2.6 Tipos denidos por el usuario


En muchas ocasiones descubriremos que los tipos primitivos no bastan para llevar a cabo ciertas tareas, debido a esto el
lenguaje C, C++ da el soporte necesario para que el programador dena sus propios tipos. Para denir tipos se usa la
palabra reservada typedef. Por ejemplo, si deseamos denir un tipo de dato llamado entero podemos usar la sintaxis:
typedef int entero;

Luego, podremos declarar variables, constantes y funciones del tipo entero. Por ejemplo,
entero edad = 33;
Un uso ms til y comn es el empleo de typedef para denir datos estructurados. Por ejemplo, supongamos que deseamos
denir un tipo estructurado llamado persona y que contenga nombre, edad y sexo. Entonces podemos denir persona
como:
typedef struct persona { char nombre[32]; int edad; char sexo; };

Una vez que un tipo ya se ha denido, el mismo puede emplearse para declarar variables y constantes de este. Por ejemplo,
con la instruccin:
persona hombre;

se est declarando la variable hombre del tipo persona. Luego, para acceder a cada elemento de la variable hombre
usaremos un mecanismo conocido como direccionamiento directo por medio del carcter de punto ( . ). Por ejemplo,
la edad de hombre se debe indicar como:
hombre.edad

2.7 Variables y constantes


Una variable, como su nombre lo indica, es un determinado objeto cuyo valor puede cambiar durante el proceso de
una tarea especca. Contrario a una variable, una constante es un determinado objeto cuyo valor no puede ser alterado
durante el proceso de una tarea especca. En C, C++ para declarar variables no existe una palabra especial, es decir, las
variables se declarn escribiendo el tipo seguido de uno o ms identicadores o nombres de variables. Por otro lado, para
declarar constantes existe la palabra reservada const, as como la directiva #dene. A continuacin se muestran ejemplos
de declaracin de variables y constantes.
Notas:

A diferencia de las constantes declaradas con la palabra const los smbolos denidos con #dene no
ocupan espacio en la memoria del cdigo ejecutable resultante.

El tipo de la variable o constante puede ser cualquiera de los listados en Tipos primitivos, o bien de un
tipo denido por el usuario.

Las constantes son usadas a menudo con un doble propsito, el primero es con el n de hacer ms legible el cdigo
del programa, es decir, si se tiene (por ejemplo) la constante numerica 3.1416 y esta representa al nmero pi, entonces
podemos hacer declaraciones tales como:
14 CAPTULO 2. LO MS BSICO

#dene pi 3.1416
En este caso podremos usar la palabra pi en cualquier parte del programa y el compilador se encargar de cambiar dicho
simbolo por 3.1416.
o bien,
const pi = 3.1416;
En este otro caso podremos usar la palabra pi en cualquier parte del programa y el compilador se encargar de cambiar
dicho smbolo por una referencia a la constante pi guardada en la memoria.
Captulo 3

Iteraciones y decisiones

3.1 Sentencias de decisin


DEFINICIN
Las sentencias de decisin o tambin llamadas de CONTROL DE FLUJO son estructuras de control que realizan una
pregunta la cual retorna verdadero o falso (evala una condicion) y selecciona la siguiente instruccin a ejecutar depen-
diendo la respuesta o resultado.

En algn momento dentro de nuestros algoritmos, es preciso cambiar el ujo de ejecucin de las instrucciones, es decir,
el orden en que las instrucciones son ejecutadas. Muchas de las veces tenemos que tomar una decisin en cuanto a que se
debe ejecutar basndonos en una respuesta de verdadero o falso (condicion).
La ejecucin de las instrucciones incluyendo una estructura de control como el condicional funcionan de esta manera:

Las instrucciones comienzan a ejecutarse de forma secuencial (en orden) y cuando se llega a una estructura condi-
cional, la cual esta asociada a una condicion, se decide que camino tomar dependiendo siempre del resultado de la
condicion siendo esta falsa o verdadera.

Cuando se termina de ejecutar este bloque de instrucciones se reanuda la ejecucin en la instruccin siguiente a la
de la condicional.

3.2 Sentencia if
La instruccin if es, por excelencia, la ms utilizada para construir estructuras de control de ujo.
SINTAXIS
Primera Forma
Ahora bin, la sintaxis utilizada en la programacin de C++ es la siguiente:
if (condicion) { Set de instrucciones }
siendo condicion el lugar donde se pondr la condicin que se tiene que cumplir para que sea verdadera la sentencia y
as proceder a realizar el set de instrucciones o cdigo contenido dentro de la sentencia.
Segunda Forma
Ahora veremos la misma sintaxis pero ahora le aadiremos la parte Falsa de la sentencia:
if (condicion) { Set de instrucciones //PARTE VERDADERA } else { Set de instrucciones 2 //Parte FALSA }

15
16 CAPTULO 3. ITERACIONES Y DECISIONES

Estructura de control IF

La forma mostrada anteriormente muestra la union de la parte VERDADERA con la nueva secuencia la cual es la parte
FALSA de la sentencia de decision IF en la cual esta compuesta por el:
else { Set de instrucciones 2 //Parte FALSA }
la palabra else o De lo contrario indica al lenguaje que de lo contrario al no ser verdadera o no se cumpla la parte
verdadera entonces realizara el set de instrucciones 2.
EJEMPLOS DE SENTENCIAS IF...
Ejemplo 1:
if(numero == 0) //La condicion indica que tiene que ser igual a Cero { cout<<"El Numero Ingresado es Igual a Cero"; }
Ejemplo 2:
if(numero > 0) // la condicion indica que tiene que ser mayor a Cero { cout<<"El Numero Ingresado es Mayor a Cero"; }
3.2. SENTENCIA IF 17

Crystal_Clear_app_kedit.png

Ejemplo 3:
if(numero < 0) // la condicion indica que tiene que ser menor a Cero { cout<<"El Numero Ingresado es Menor a Cero";
}
Ahora uniremos todos estos ejemplos para formar un solo programa mediante la utilizacin de la sentencia Else e
introduciremos el hecho de que se puede escribir en este espacio una sentencia if ya que podemos ingresar cualquier tipo
de cdigo dentro de la sentencia escrita despus de un Else.
Ejemplo 4:
if(numero == 0) //La condicion indica que tiene que ser igual a Cero { cout<<"El Numero Ingresado es Igual a Cero"; }
else { if(numero > 0) // la condicion indica que tiene que ser mayor a Cero { cout<<"El Numero Ingresado es Mayor a
Cero"; } else { if(numero < 0) // la condicion indica que tiene que ser menor a Cero { cout<<"El Numero Ingresado es
Menor a Cero"; } } }
18 CAPTULO 3. ITERACIONES Y DECISIONES

3.2.1 Sentencia switch

switch es otra de las instrucciones que permiten la construccin de estructuras de control. A diferencia de if, para controlar
el ujo por medio de una sentencia switch se debe de combinar con el uso de las sentencias case y break.

Notas: cualquier nmero de casos a evaluar por switch as como la sentencia default son opcionales. La
sentencia switch es muy til en los casos de presentacin de menus.

Sintaxis:
switch (condicin) { case primer_caso: bloque de instrucciones 1 break; case segundo_caso: bloque de instrucciones 2
break; case caso_n: bloque de instrucciones n break; default: bloque de instrucciones por defecto }
Ejemplo 1
switch (numero) { case 0: cout << numero es cero"; }

Ejemplo 2
switch (opcion) { case 0: cout << Su opcion es cero"; break; case 1: cout << Su opcion es uno"; break; case 2: cout <<
Su opcion es dos"; }

Ejemplo 3
switch (opcion) { case 1: cout << Su opcion es 1"; break; case 2: cout << Su opcion es 2"; break; case 3: cout << Su
opcion es 3"; break; default: cout << Elija una opcion entre 1 y 3"; }

3.2.2 Operador condicional ternario ?:

En C/C++, existe el operador condicional ( ?: ) el cual es conocido por su estructura como ternario. El comportamiento
de dicho operador es el mismo que una estructura if - then - else del lenguaje BASIC (y de la funcin IIf de Visual
Basic). El operador condicional ?: es til para evaluar situaciones tales como:
Si se cumple tal condicin entonces haz esto, de lo contrario haz esto otro.
Sintaxis:
( (condicion) ? proceso1 : proceso2 )
En donde, condicion es la expresin que se evalua, proceso1 es la tarea a realizar en el caso de que la evaluacin resulte
verdadera, y proceso2 es la tarea a realizar en el caso de que la evaluacin resulte falsa.
Ejemplo 1
int edad; cout << Cual es tu edad: "; cin >> edad; cout << ( (edad < 18) ? Eres joven aun : Ya tienes la mayora de
edad );

El ejemplo anterior podra escribirse de la siguiente manera:


int edad; cout << Cual es tu edad: "; cin >> edad; if (edad < 18) cout << Eres joven aun"; else cout << Ya tienes la
mayora de edad";

Ejemplo 2
Vamos a suponer que deseamos escribir una funcin que opere sobre dos valores numricos y que la misma ha de regresar
1 (true) en caso de que el primer valor pasado sea igual al segundo valor; en caso contrario la funcin debe retornar 0
(false).
3.3. SENTENCIAS DE ITERACIN 19

int es_igual( int a, int b) { return ( (a == b) ? 1 : 0 ) }

3.3 Sentencias de iteracin


DEFINICIN
Las Sentencias de Iteracin o Ciclos son estructuras de control que repiten la ejecucin de un grupo de instrucciones.
Bsicamente, una sentencia de iteracin es una estructura de control condicional, ya que dentro de la misma se repite la
ejecucin de una o ms instrucciones mientras que una a condicin especica se cumpla. Muchas veces tenemos que
repetir un nmero denido o indenido de veces un grupo de instrucciones por lo que en estos casos utilizamos este tipo
de sentencias. en C++ los ciclos o bucles se construyen por medio de las sentencias for, while y do - while. La sentencia
for es til para los casos en donde se conoce de antemano el nmero de veces que una o ms sentencias han de repetirse.
Por otro lado, la sentencia while es til en aquellos casos en donde no se conoce de antemano el nmero de veces que una
o ms sentencias se tienen que repetir.

3.3.1 Sentencias For

for(contador; nal; incremento) { Codigo a Repetir; }

donde:

1. contador es una variable numrica

2. nal es la condicin que se evalua para nalizar el ciclo (puede ser independiente del contador)

3. incremento es el valor que se suma o resta al contador

Hay que tener en cuenta que el for evalua la condicin de nalizacin igual que el while, es decir, mientras esta se
cumpla continuaran las repeticiones.
Ejemplo 1:
for(int i=1; i<=10; i++) { cout<<"Hola Mundo"; }

Esto indica que el contador i inicia desde 1 y continuar iterando mientras i sea menor o igual a 10 ( en este caso llegar
hasta 10) e i++" realiza la sumatoria por unidad lo que hace que el for y el contador se sumen. repitiendo 10 veces
HOLA MUNDO en pantalla.
Ejemplo 2:
for(int i=10; i>=0; i--) { cout<<"Hola Mundo"; }

Este ejemplo hace lo mismo que el primero, salvo que el contador se inicializa a 10 en lugar de 1; y por ello cambia la
condicin que se evalua as como que el contador se decrementa en lugar de ser incrementado.
La condicin tambin puede ser independiente del contador:
Ejemplo 3:
int j = 20; for(int i=0; j>0; i++){ cout<<"Hola"<<i<<" - "<<j<<endl; j--; }

En este ejemplo las iteraciones continuaran mientras j sea mayor que 0, sin tener en cuenta el valor que pueda tener i.
20 CAPTULO 3. ITERACIONES Y DECISIONES

3.3.2 Sentencia while


while(condicion) { cdigo a Repetir }

donde:

1. condicion es la expresin a evaluar

Ejemplo 1:
int contador = 0; while(contador<=10) { contador=contador+1; cout<<"Hola Mundo"; }

El contador Indica que hasta que este llegue a el total de 10 entonces se detendr y ya no se realizar el cdigo contenido
dentro de la sentencia while, de lo contrario mientras el contador sea menor o igual a 10 entonces el cdigo contenido
se ejecutar desplegando hasta 11 veces Hola Mundo en pantalla.

3.3.3 Sentencia do - while


La sentencia do es usada generalmente en cooperacin con while para garantizar que una o ms instrucciones se ejecuten
al menos una vez. Por ejemplo, en la siguiente construccin no se ejecuta nada dentro del ciclo while, el hecho es que el
contador inicialmente vale cero y la condicin para que se ejecute lo que est dentro del while es mientras el contador
sea mayor que diez. Es evidente que a la primera evaluacin hecha por while la condicin deja de cumplirse.
int contador = 0; while(contador > 10) { contador ++; cout<<"Hola Mundo"; }

Al modicar el segmento de cdigo anterior usando do tenemos:


int contador = 0; do { contador ++; cout<<"Hola Mundo"; } while(contador > 10);

Observe cmo en el caso de do la condicin es evaluada al nal en lugar de al principio del bloque de instrucciones y, por
lo tanto, el cdigo que le sigue al do se ejecuta al menos la primera vez.

3.4 Sentencias break y continue


En la seccin (Sentencia switch) vimos que la sentencia break es utilizada con el propsito de forzar un salto dentro
del bloque switch hacia el nal del mismo. En esta seccin volveremos a ver el uso de break, salvo que esta ocasin la
usaremos junto con las sentecias for y la sentencia while. Adems, veremos el uso de la sentencia continue.

3.4.1 break
La sentencia break se usa para forzar un salto hacia el nal de un ciclo controlado por for o por while.
Ejemplo:
En el siguiente fragmento de cdigo la sentencia break cierra el ciclo for cuando la variable ( i ) es igual a 5. La salida
para el mismo ser:

01234

for (int i=0; i<10; i++) { if (i == 5) break; cout << i << " "; }
3.4. SENTENCIAS BREAK Y CONTINUE 21

3.4.2 continue
La sentencia continue se usa para ignorar una iteracin dentro de un ciclo controlado por for o por while.
Ejemplo:
En el siguiente fragmento de cdigo la sentencia continue ignora la iteracin cuando la variable ( i ) es igual a 5. La salida
para el mismo ser:

012346789

for (int i=0; i<10; i++) { if (i == 5) continue; cout << i << " "; }

Uso de break y continue junto con while


Los dos ejemplos anteriores se presentan en seguida, salvo que en lugar de for se hace uso de while.

Nota: no deje de observar que la construccin del ciclo while para el caso de la sentencia continue es
diferente, esto para garantizar que el ciclo no vaya a caer en una iteracin innita.

break
int i = 0; while (i<10) { if (i == 5) break; cout << i << " "; i++; }

continue
int i = 1; while (i<10) { i++; if (i == 5) continue; cout << i << " "; }
Captulo 4

Estructuras

22
Captulo 5

Estructuras de datos

Las estructuras de datos se emplean con el objetivo principal de organizar los datos contenidos dentro de la memoria
del ordenador. As, nuestra primera experiencia con estructuras comienza desde el momento mismo en que usamos en
nuestros programas variables de tipos primitivos (char, short, int, oat, etc). A la memoria del ordenador se le puede
considerar como un gran bloque compuesto por una serie de BYTES dispuestos secuencialmente uno detrs de otro. por
ejemplo, si un ordenador posee una memoria de 128MB (128 megas) entonces se le puede leer o escribir desde el BYTE
0 hasta el BYTE 128MB - 1 ( 0000000H .. 7FFFFFFH ).
La idea de ver la memoria como un serie de bytes es buena, sin embargo no es suciente ya que en la misma podemos
guardar nmeros, cadenas de caracteres, funciones, objetos, etc. de tal manera que surge la necesidad de establecer los
mecanismos adecuados para dar cuenta de la forma, tamao y objetivo de los datos almacenados. Segn el tipo de mi-
croprocesador, estos tienen la capacidad para manipular o direccionar estructuras compuestas por uno, dos, cuatro, etc,
bytes; de donde se derivan los tipos que comunmente se conocen como: BYTE, WORD, DWORD, QWORD y TWORD.
La estructura mnima de informacin manipulable en un sistema de computacin es el BIT el cual se agrupa normalmente
en bloques de 8 para formar un BYTE. Cabe mencionar que los BITS no son direccionables directamente, sino a travs
de compuertas AND, OR, NOT, XOR, las cuales en C y C++ se escriben como &, |, ~ y ^, conocidos como Bitwise
operators u Operadores de manipulacin de bits.
En C,C++ existe una serie de estructuras bsicas o tipos primitivos, los cuales pueden ser usados por el programador
para declarar variables, y tambin son el fundamento sobre el cual se crean estructuras complejas. El tamao de los tipos
primitivos no es estndar ya que los mismos dependen de factores tales como:

Tipo del microprocesador

El compilador

Sin embargo, en la actualidad, la mayoria de compiladores de C y C++ soportan los siguientes tipos con la longitud
indicada:

5.1 Estructuras bsicas en C, C++


Nota: en el lenguaje C,C++ existe el operador sizeof(), con el cual se puede obtener el tamao (nmero de bytes) ocupados
por un tipo especco. Por ejemplo, sizeof(int) regresa el nmero de bytes ocupados por los datos de tipo int.

5.1.1 variables

En C, C++ la sintaxis para declarar variables es:

23
24 CAPTULO 5. ESTRUCTURAS DE DATOS

tipo id1 [, id2 ...] ;


donde, tipo se reere a uno de los tipos mostrados en la tabla anterior; id1 es el nombre con el cual se identicar a la
variable. Observe que si se quiere declarar ms de una variable en una lnea de instruccin, las mismas deben separarse
por medio de una coma.
Ejemplos:
char c; // La variable c es una variable char int i; // La variable i es una variable int oat f; // La variable f es una variable
oat int x,y,z; // Las variables x,y,z son todas variables int, y declaradas a la vez

De acuerdo con la tabla anterior y segn las instrucciones anteriores, con la primera, o sea ( char c;), se le est indicando
al ordenador que reserve en la memoria un espacio de tipo char (8 bits) y que el mismo ser identicado bajo el nombre
de c. La segunda instruccin ( int i;) le indica al ordenador que reserve en la memoria un espacio de tipo int (16 bits) y
que el mismo ser identicado bajo el nombre de i. Luego, la instruccin ( oat f;) le indica al ordenador que reserve en
la memoria un espacio de tipo oat (32 bits) y que el mismo ser identicado bajo el nombre de f. Por ltimo, se le indica
al compilador que reserve espacio para otras tres variables enteras identicadas como: x, y, z. As, se puede dar cuenta
cmo los tipos primitivos sirven con el propsito de estructurar los datos dentro de la memoria y con la idea de referirnos
a los mismos mediante nombres usamos identicadores de variables.

5.2 Matrices o Arreglos


Una Matriz (en ingls, array, tambin denominado arreglo) es una estructura usada para agrupar bajo un mismo nombre
listas de datos de un mismo tipo.
El tipo de matriz puede ser cualquiera, sin embargo cada componente tiene que ser del mismo tipo. En C estndar
solamente da soporte para matrices estticas, mientras que con C++ se pueden crear matrices dinmicas pudiendo usar
la librera estndar de plantillas (STL).

5.2.1 Matrices estticas


Una matriz esttica es una estructura cuyo tamao es determinado en tiempo de compilacin, es decir, una vez establecido
el tamao de la matriz sta no podr cambiarse durante el tiempo de ejecucin. En C, C++ para declarar un arreglo esttico
de datos se emplea la sintaxis:
tipo identicador[ [tamao] ] [ = { lista de inicializacin } ] ;
donde,

tipo se reere al tipo de datos que contendr la matriz. El tipo puede ser cualquiera de los tipos estndar (char, int,
oat, etc.) o un tipo denido por el usuario. Es ms, el tipo de la matriz puede ser de una estructura creada con:
struct, union y class.

identicador se reere al nombre que se le dar a la matriz.

tamao es opcional e indica el nmero de elementos que contendr la matriz. Si una matriz se declara sin
tamao, la misma no podr contener elemento alguno a menos que en la declaracin se emplee una lista de
inicializacin.

lista de inicializacin es opcional y se usa para establecer valores para cada uno de los componentes de la
matriz. Si la matriz es declarada con un tamao especifco, el nmero de valores inicializados no podr ser
mayor a dicho tamao.
5.3. ESTRUCTURAS COMPUESTAS (STRUCT, UNION, CLASS) 25

Ejemplos:
int intA[5]; long longA[5] = { 1, 2, 3, 4, 5 }; char charA[] = { 'a', 'b', 'c' };

5.2.2 Acceso a los miembros de una matriz de datos:


En orden de acceder a los miembros de una matriz se debe indicar el nombre de la matriz seguido de dos corchetes,
dentro de los cuales se debe especicar el ndice del elemento deseado. Se debe aclarar que los ndices son nmeros
o expresiones enteras y que en C, C++ estos tienen un rango permitido de 0 a T-1 ( T = tamao de la matriz ).
Ejemplos: dadas las matrices intA, charA, longA ( ejemplo anterior )
intA[0] = 100; // establece el valor del elemento 0 de intA a 100. charA[3] = 'O'; // establece el valor del elemento
3 de charA a 'O'. cout << longA[0]; // muestra por pantalla el elemento 0 de longA, que es longA[0].

5.2.3 Matrices dinmicas


Una matriz dinmica es una estructura compleja y, ya que C estndar no da el soporte para operar con estos tipos
de estructuras, le corresponde al programador crear los algoritmos necesarios para su implementacin. Crear
lista dinmicas de datos en C estndar no es una tarea para programadores inexpertos, ya que para lograr tal
objetivo se necesita tener conocimentos solidos acerca de los punteros y el comportamiento de los mismos. Los
usuarios de C++ pueden auxiliarse de la librera estndar de plantillas, conocidas por sus siglas en ingles como
STL.

5.3 Estructuras compuestas (struct, union, class)


Con base en los tipos bsicos mostrados arriba, se pueden crear nuevos tipos con estructuras compuestas por uno
o ms de uno de los tipos mencionados. En C, C++ en orden de crear nuevas estructuras se emplean la palabras
reservadas struct, union y class.

struct: esta orden se emplea para agrupar variables de tipos iguales o diferentes en un solo registro, con
la misma se pueden crear estructuras annimas, estructuras con nombre, y un tipo especial de estructura
conocida como bit-elds ( banderas o campos de bits ).

union: una union es similar a una struct, salvo que en una estructura creada con union los campos o
variables comparten una direccin de almacenamiento comn.

class: una clase es una estructura en donde se agrupan variables y funciones, la misma es usada en Pro-
gramacin Orientada al Objeto. Las clases no son soportadas por el C estndar.

Nota: tanto las estructuras como las uniones y las clases pueden ser annimas, pero lo ms recomendable es
darle a las mismas un nombre. Si una estructura, union o clase posee nombre, esta pueden ser empleadas para
declarar variables de la misma y, lo ms importante, puede ser usada para el paso de parmetros a funciones.
Sintaxis general: struct
struct [ <nombre tipo de estructura > ] { [ <tipo> <nombre-variable[, nombre-variable, ...]> ] ; [ <tipo> <nombre-
variable[, nombre-variable, ...]> ] ; ... } [ <variables de estructura> ] ;

Nota: recuerde que lo que se muestra entre corchetes es opcional.


struct: Sintaxis ( variacin uno, estructura annima )
26 CAPTULO 5. ESTRUCTURAS DE DATOS

De acuerdo con la sintaxis general de la orden struct es posible crear estructuras de datos annimas. Solamente
hay que tener en cuenta que en una declaracin annima se debe denir al menos una variable al nal de la
declaracin. Por ejemplo, con el siguiente fragmento de cdigo:
struct { int a, b; } p1;

se declara y dene la variable estructurada p1, misma que se compone por los miembros a y b; ambos del tipo int.
Ahora bien, la sintaxis mostrada arriba no es tan comn ni conveniente, ya que con la misma solamente se esta
creando una variable estructurada pero no un nuevo tipo. Es decir, si desearamos tener otra variable que tuviera
las mismas caracteristicas que posee la variable p1, necesitariamos escribir exactamente la misma instruccin,
salvo que cambiando el nombre de la variable. Por ejemplo:
struct { int a, b; } p2;

Por supuesto, en una misma lnea de instruccin podemos denir ms de una variable. Ejemplo:
struct { int a, b; } p1, p2;

Entonces, para crear nuevos tipos con struct deberemos de modicar la sintaxis mostrada en los ejemplos ante-
riores.
Sintaxis: struct ( variacin dos, estructura con nombre )
Observe que, la sintaxis para declarar estructuras con nombre es bastante parecida a la sintaxis para declarar
estructuras annimas; salvo que en una declaracin de estructura con nombre se debe especicar el nombre
deseado para la misma. Adems, en una declaracin de estructura con nombre la o las variables denidas al nal
de la misma son opcionales.
Ejemplos:
struct pareja { int a, b; } p1;

En el fragmento de cdigo anterior se declara la estructura identicada como pareja, misma que se compone
de los miembros a y b, ambos de tipo int. En el mismo ejemplo, se dene la variable p1; la cual es una variable
estructurada de tipo pareja.
Una vez que una estructura con nombre ha sido creada, la misma puede ser usada para declarar cualquier nmero
de variables. Por ejemplo, en el siguiente fragmento de cdigo se crea la estructura tiempo compuesta por los
miembros hora, minuto y segundo; todos del tipo int. En el mismo ejemplo, se declaran las variables t1 y t2.
/* declaracin de estructura tiempo */ struct tiempo { int hora, minuto, segundo; }; /* declaracin de variables
de tipo tiempo */ struct tiempo t1, t2;

Nota: en C++ puede obviarse la palabra struct a la hora de declarar variables. As, en C++ la lnea de instrucin
struct tiempo t1, t2; ( del ejemplo anterior) puede escibirse como: tiempo t1, t2;

5.3.1 Acceso a los miembros de una estructura

En orden de poder leer o escribir uno de los miembros de una variable estructurada, se debe usar el operador de
acceso ( . ); o sea, el nombre de la variable seguida por un punto seguido por el nombre del miembro o componente
deseado de la estructura. Por ejemplo, para acceder a los miembros de la variable t1 (mostrada arriba) podemos
hacerlo de la siguiente manera:
t1.hora = 12; t1.minuto = 0; t1.segundo = 0; printf ("%i\n, t1.hora); cout << t1.minuto << endl;
5.3. ESTRUCTURAS COMPUESTAS (STRUCT, UNION, CLASS) 27

5.3.2 Estructuras anidadas

Los miembros de una estructura pueden ser ellos mismos, otra estructura previamente identicada, o bien una
estructura annima. Por ejemplo, en el siguiente fragmento de cdigo, se crean las estructuras pareja y pareja2.
Obsrvese cmo dentro de los miembros de pareja2, se declara el miembro X, que a su vez es una estructura del
tipo pareja. Luego, las variables declaradas a raz de la estructura pareja2 poseern los miembros variables a y
b heredados de pareja, y c.
struct pareja { int a, b ; }; struct pareja2 { struct pareja X; int c; } P3;

Ahora bien, para acceder a los miembros de una estructura dentro de otra estructura se emplea el mismo meca-
nismo de acceso (el punto). Por ejemplo, para desplegar el miembro a de la variable P3 declarada en el ejemplo
anterior, lo haremos ms o menos as:
printf( "%i\n, P3.X.a );

5.3.3 Herencia

El trmino herencia se usa con gran frecuencia en Programacin Orientada a Objetos, y se le relaciona principal-
mente con las clases. Sin embargo, la herencia est presente siempre y cuando una estructura struct, union
o class posea a otra estructura. En ese sentido, en C++ se presentan dos tipos de herencia:

herencia por agregacin o composicin.

herencia por extensin.

Por ejemplo, en la denicin de las estructuras pareja y pareja2 del ejemplo anterior, se dice que pareja2 hereda
por composicin todos los miembros de pareja. Ahora, en el siguiente ejemplo se usa la sintaxis para que la
estructura pareja2 herede por extensin los miembros de pareja:
// solo C++ struct pareja { int a, b ; }; struct pareja2 : pareja { int c; } P3;

Con esta forma de herencia, la estructura pareja2 hereda de pareja los miembros a y b, y adems agrega un
miembro c. Y a diferencia del ejemplo anterior, para acceder a alguno de sus miembros heredados, basta con
utilizar el mecanismo de acceso (el punto).
// solo C++ cout << P3.a << P3.b ;

5.3.4 Estructura de campos de bits

Un campo de bit es un elemento de una estructura denido en terminos de bits. Usando un tipo
especial de denicin de struct, se pueden declarar elementos de estructura con rangos de 1 a 16 de
largo (dependiendo de la arquitectura de la PC y del compilador, el rango para una estructura de
campos de bits puede ser de 1 a 16, 1 a 32, 1 a 64).

Antes de ver un ejemplo del uso de struct para crear estructuras de campos de bits, consideremos el caso en
donde se tiene una variable del tipo short (16 bits) y que para la misma se desea que los bits tengan signicados
especcos. Digamos que el primer bit servir para controlar alguna condicin; los siguientes cuatro bits, o sea
del segundo al quinto bit, controlarn otra condicin; el bit 6 tendr otra funcin; y el resto, o sea del sptimo al
decimosexto bit se emplearn para controlar otra condicin. De tal manera que si queremos, por ejemplo, saber
si el primer bit de la variable tiene almacenado un 1 o un 0, podemos emplear la siguiente sintaxis:
28 CAPTULO 5. ESTRUCTURAS DE DATOS

int X = 123; int r = X & 1;

la cosa parece sencilla, pero ahora consideremos el caso en el cual deseamos saber cual es el valor contenido por
el grupo de bits ( segundo al quinto ), entonces nos daremos cuenta que no basta con una prueba mediante AND
( X & 1 ), sino que hay que realizar otros pasos.
Precisamente, para problemas como el planteado arriba es por los que los lenguajes C y C++ soportan las es-
tructuras de campos de bits. Por ejemplo, la estructura
struct campo_de_bit { int bit_1 : 1; int bits_2_a_5 : 4; int bit_6 : 1; int bits_7_a_16 : 10; } bit_var;

corresponde a la siguiente coleccin de campos bits:

El mecanismo de estructuras de campos de bits soportado por C,C++ es una herramienta til y poderosa, sobre
todo en programacin de bajo nivel; ya que mediante el mismo es posible aislar y dar nombres a todos y cada
uno de los bits de un dato, y tambin crear en un mismo campo grupos de bits (como se mostr arriba). Ahora
bien, no hay que olvidar que la estructura mnima de informacin en un sistema de memoria de un PC es el bit, y
que para aislar a cada uno de estos se puede emplear el operador AND ( &, en C ), pero mediante el mecanismo
mencionado el compilador genera los algoritmos necesarios para llevar a cabo el aislamiento de los bits. Por
ejemplo, para escribir y leer bit identicado como bit_1 de la variable bit_var del ejemplo anterior, podemos
emplear las siguientes instrucciones:
bit_var.bit_1 = 1; printf("%i\n, bit_var.bit_1 );

Nota: acerca de las estructuras de campos de bits hay que aclarar que, aunque cada uno de los campos de la
estructura pueden declararse como enteros con signo o enteros sin signo, la misma no tendr una longitud mayor
a un entero largo.

5.4 union: Sintaxis general


union [ <nombre tipo de union > ] { <tipo> <lista de variables>; } [ <variables de union> ] ;

De la misma manera que con la orden struct, con la orden union se pueden crear estructuras con nombre
y estructuras sin nombre.

El mecanismo de acceso a los miembros de una union es igual al mecanismo de acceso a los miembros de
una struct.

Los miembros de una union comparten un espacio de almacenamiento comn.

En una union, el compilador reserva el espacio de almacenamiento para la misma de acuerdo con el tipo
de la variable de mayor tamao.
5.5. CLASS: SINTAXIS 29

Ejemplo: union annima


union { short a; long b; } u1;

En el ejemplo anterior se declara la variable u1, la cual es una estructura tipo union. El espacio de almacenamiento
para la variable a es compartido por la variable b, en consecuencia, al escribir sobre cualquiera de estas dos
variables se altera el contenido de ambas.
Ejemplo: union con nombre
union ux { short a; long b; } u1;

En el ejemplo anterior se declara la variable u1, la cual es una estructura tipo union. El espacio de almacenamiento
para la variable a es compartido por la variable b. Es decir, el compilador reservar espacio en la memoria para la
variable de mayor tamao (que para ste caso es b ). Ahora bin, suponiendo que en su equipo el tipo long ocupa
32 bits y que el tipo short ocupa 16 bits, entonces la variable a ocupar solamente los 16 bits menos signicativos,
mientras que la variable b ocupar todo el espacio, o sea los 32 bits; Observe que en la sintaxis se ha especicado
el nombre ux, mismo que puede ser empleado para declarar cualquier nmero de variables de la union. Por
ejemplo, a continuacin se declaran las variables u2 y u3 del tipo union ux creado en el ejemplo anterior.
union ux u2, u3;

5.5 class: sintaxis


<classkey> <classname> [<:baselist>] { <member list> } [lista de variables] ;

<classkey> es una de las palabras class, struct, o union.


<baselist> lista de clas(es) base de donde se deriva esta clase. <baselist> es opcional.
<member list> declara los datos miembros y funciones miembros de la clase.
[lista de variables] esta entrada es opcional y se usa para instanciar variables u objetos de esta clase.

Nota: Dentro de una clase,

los datos son llamados datos miembros

las funciones son llamadas funciones miembros.

El mecanismo para acceder a los miembros de una class es igual que aquel utilizado para acceder a los
miembros de una struct

Las clases son algo as como super estructuras capaces de agrupar no solo datos miembros sino tambin fun-
ciones miembros. En el lenguaje comn a los datos miembros de una clase se les conoce como atributos; mientras
que a las funciones miembros de una clase se les llama mtodos. Normalmente, los mtodos se emplean para leer
o escribir los atributos. Es decir, la norma general es no permitir el acceso directo a los atributos de una clase,
con la idea de aumentar la seguridad de los datos.
En seguida se mostrar el cdigo para crear la clase Pareja, misma que poseer los atributos a y b, y los mtodos
setA(), setB(); getA(), getB(), y mostrar();
class Pareja { int a, b; public: void setA(int n) { a = n; } void setB(int n) { b = n; } int getA() { return a; } int
getB() { return b; } void mostrar() { cout << a = " << a << "; b = " << b << endl; } } p1;
30 CAPTULO 5. ESTRUCTURAS DE DATOS

Nota: por omisin, los miembros de una clase son privados, lo cual signica que los objetos instanciados de dicha
clase no tendrn acceso a los mismos. As, en el ejemplo anterior se est creando la clase Pareja, y al mismo
tiempo el objeto p1. Luego, para leer o escribir los atributos de p1 se debe hacer a traves de los mtodos denidos
con tal objetivo. Por ejemplo, con el siguiente fragmento de cdigo se establecen respectivamente a 100 y a 200
los atributos a y b; y posteriormente se despliegan por medio del mtodo mostrar().
p1.setA(100); p1.setB(200); p1.mostrar();

5.6 struct vs. class


Esta seccin no pretende ensear que el uso de la palabra struct es lo mismo que usar la palabra class. Sin
embargo, como ya se mencion en una seccin anterior a esta, las estructuras (struct) pueden extenderse de otras
y heredar todos los miembros de la estructura base. Otro punto que se demostrar aqu es el hecho de que las
estructuras en C++ (no en C estndar) pueden contener miembros funciones. Por supuesto, el compilador trata
a una struct de una forma diferente que a una class. Entre algunas de las diferencias que se pueden mencionar
respecto a las estructuras contra las clases son:

Los miembros de una struct son pblicos por defecto, mientras que los miembros de una class son privados
por defecto.
Los parmetros-argumentos struct se pasan normalmente por copia, los parmetros-argumentos class se
pasan normalmente por referencia.

// Este programa ha sido probado en Dev-C++, Borland C++ y Code::Blocks #include <iostream> using names-
pace std; // estructura tipo clase base struct Par { int a, b; // constructor base Par() { a = b = 0; } // destructor
base ~Par() { cout << hecho... << endl; } void setA(int n) { a = n; } void setB(int n) { b = n; } void mostrar()
{ cout << a = " << a << ", b = " << b << "; suma = " << a+b << endl; } }; // estructura tipo clase hija //
ParHijo es una extensin de Par, y por lo tanto hereda los miembros de Par struct ParHijo : Par { // constructor
del hijo ParHijo(int a, int b) { this->a = a; this->b = b; } }; // prueba void test00() { ParHijo p1(100, 200); //
p1 es instancia de ParHijo p1.mostrar(); // se enva mensaje al mtodo mostrar() de p1 } // funcion principal int
main() { test00(); cin.get(); return 0; }
Captulo 6

Funciones

31
Captulo 7

Funciones

7.1 Deniendo una funcin

Modelo de programacin estructurada

Una funcin es un conjunto de lneas de cdigo que realizan una tarea especca y puede retornar un valor. Las funciones
pueden tomar parmetros que modiquen su funcionamiento. Las funciones son utilizadas para descomponer grandes
problemas en tareas simples y para implementar operaciones que son comnmente utilizadas durante un programa y de
esta manera reducir la cantidad de cdigo. Cuando una funcin es invocada se le pasa el control a la misma, una vez que
esta naliz con su tarea el control es devuelto al punto desde el cual la funcin fue llamada.
<tipo> [clase::] <nombre> ( [Parmetros] ) { cuerpo; }
Ejemplo de una funcin

32
7.2. PARMETROS 33

Para comenzar, vamos a considerar el caso en el cual se desea crear la funcin cuadrado(), que deber devolver el cuadrado
de un nmero real (de punto otante), es decir, cuadrado() aceptar nmeros de punto otante y regresar una respuesta
como nmero otante.
Nota: aunque para la funcin que veremos el tipo de retorno coincide con el tipo de parmetro pasado, algunas veces las
cosas pueden cambiar, es decir, no es obligatorio que una funcin reciba un parmetro de un tipo y que tenga que regresar
una respuesta de dicho tipo.
// regresar el cuadrado de un nmero double Cuadrado(double n) { return n*n; }

7.2 Parmetros
Normalmente, las funciones operan sobre ciertos valores pasados a las mismas ya sea como constantes literales o como
variables, aunque se pueden denir funciones que no reciban parmetros. Existen dos formas en C++ de pasar parmetros
a una funcin; por referencia o por valor. El hecho es que si en una declaracin de funcin se declaran parmetros
por referencia, a los mismos no se les podr pasar valores literales ya que las referencias apuntan a objetos (variables o
funciones) residentes en la memoria; por otro lado, si un parmetro es declarado para ser pasado por valor, el mismo puede
pasarse como una constante literal o como una variable. Los parmetros pasados por referencia pueden ser alterados por
la funcin que los reciba, mientras que los parmetros pasados por valor o copia no pueden ser alterados por la funcin
que los recibe, es decir, la funcin puede manipular a su antojo al parmetro, pero ningn cambio hecho sobre este se
reejar en el parmetro original.
Parmetros por valor
La funcin cuadrado() (ver arriba) es un clsico ejemplo que muestra el paso de parmetros por valor, en ese sentido la
funcin cuadrado() recibe una copia del parmetro n. En la misma funcin se puede observar que se realiza un calculo
( n*n ), sin embargo el parmetro original no sufrir cambio alguno, esto seguir siendo cierto an cuando dentro de la
funcin hubiera una instruccin parecida a n = n * n; o n*=n;.
Parmetros por referencia
Para mostrar un ejemplo del paso de parmetros por referencia, vamos a retomar el caso de la funcin cuadrado, salvo
que en esta ocasin cambiaremos ligeramente la sintaxis para denir la misma. Veamos:
// regresar el cuadrado de un nmero double cuadrado2(double &n) { n *= n; return n; }

Al poner a prueba las funciones cuadrado() y cuadrado2() se podr vericar que la primera de estas no cambia el valor
del parmetro original, mientras que la segunda s lo hace.

7.3 Llamar a una funcin


para llamar a la funcin cuadrado() vista anteriormente, podemos emplear:
cout << cuadrado(25); cout << cuadrado(X); R = cuadrado(X); // guardar en R el cuadrado de X

7.4 Funciones void


Bajo ciertas circunstancias se desear escribir funciones que no regresen valor alguno (esto sera algo parecido a escribir
procedures en Pascal) y para ello podemos declarar a la funcin como void. La palabra reservada void es utilizada para
declarar funciones sin valor de retorno y tambin para indicar que una funcin especca no requiere de parmetros. Por
ejemplo, la funcin pausa() que se ver en seguida, no devolver valor alguno y la misma no requiere de parmetros.
34 CAPTULO 7. FUNCIONES

// esta funcin requiere de la librera iostream void pausa(void) { cout << Por favor presione <Enter> HOLA..."; cin.get();
cin.ignore(255, '\n'); // rechazar caracteres introducidos antes de <Enter> }

;
Notas: se debe de aclarar que el uso de la palabra void dentro de los parentesis es opcional al momento de declarar
una funcin. As, la funcin pausa() podra haberse declarado como void pausa(), y la misma puede invocarse como:
pausa();.

7.5 Funciones anidadas


A diferencia de Pascal, el lenguaje C, C++ no permite anidar funciones, sin embargo, dentro de una funcon puede
existir la llamada a una o ms funciones declaradas previamente, que determinara en cierto punto un resultado para que
sea ejecutado cuando el programador lo desee.

7.6 Funciones de tipo puntero (*)


En muchas ocasiones se desea que ciertas funciones regresen una referencia o puntero hacia un tipo (sea este estructurado o
no) especco de dato en lugar de un valor especco. En tales casos, la funcin se deber declarar como para que regrese un
puntero. Por ejemplo, supongamos que deseamos crear una funcin para convertir un nmero entero en notacin decimal
a una cadena de caracteres en forma de nmeros binarios, luego, la funcin mencionada podra escribirse para que reciba
el nmero entero como parmetro y regrese un puntero a una cadena de caracteres conteniendo la conversin. Para ser
ms puntuales, vamos a escribir un programa en donde se ver la funcin binstr(), y cuyo objetivo ser precisamente
convertir nmeros decimales en cadenas binarias.
Nota: observe que en la sintaxis para declarar funciones tipo puntero se debe de poner el smbolo * despues del tipo y
antes del nombre de la funcin que se est declarando. Esto se puede ver en el programa, ya que la funcin binstr se
declara como: char *binstr(unsigned int);
#include <iostream> #include <string.h> using namespace std; // declaracin de prototipo char *binstr(unsigned int); //
punto de prueba int main() { int n = 128; cout << decimal = " << n << ", binario = " << binstr(n) << endl; cin.get(); }
// denicin de funcin binstr() // nota: esta funcion requiere de la librera estndar string char *binstr(unsigned int n) {
static char buer[65]; int i = 0; strcpy(buer, 0); if (n > 0) { while (n > 0) { buer[i] = ( n & 1 ) + '0'; i++; n >>= 1; }
buer[i] = '\0'; strrev(buer); } // n (n > 0) return buer; }

7.7 Variables estticas y automticas


Dentro de una funcin, las variables y/o constantes pueden ser declaradas como: auto (por omisin) o como static. Si
una variable dentro de una funcin es declarada como esttica signica que la misma retendr su valor entre las llamadas
a la funcin. Por otro lado, la variables automticas pierden su valor entre las llamadas. En el programa anterior puede
verse que el arreglo de caracteres (buer[65]) se ha declarado como esttico para garantizar que dicho buer retenga los
datos an despues de que la funcin termine. En el mismo ejemplo, si el buer no se declara como esttico, el contenido
del mismo podra ser destruido al salir de la funcin y los resultados obtenidos podran ser no deseados.

7.8 Parmetros constantes


Los parmetros usados por una funcin pueden declararse como constantes ( const ) al momento de la declaracin de
la funcin. Un parmetro que ha sido declarado como constante signica que la funcin no podr cambiar el valor del
mismo ( sin importar si dicho parmetro se recibe por valor o por referencia ).
7.9. PARMETROS CON VALOR POR DEFECTO 35

Ejemplo:
int funcionX( const int n ); void printstr( const char *str );

7.9 Parmetros con valor por defecto


Los parmetros usados por una funcin pueden declararse con un valor por defecto. Un parmetro que ha sido declarado
con valor por defecto es opcional a la hora de hacer la llamada a la funcin. Ejemplo: Dada la funcin:
void saludo( char* mensaje = Hola Copa America 2015 );
la misma puede ser invocada como:
saludo(); // sin parmetro saludo(Sea usted bienvenido a C++"); // con parmetro
Para ver un ejemplo ms, vamos a considerar el caso de la funcin binstr() del programa funciones01. Ahora, vamos
modicar dicha funcin, salvo que esta ocasin nos interesa que la misma sirva para convertir nmeros decimales en
cadenas numricas y cuya base de conversin sea pasada como parmetro. Es decir, la funcin de la que estamos hablando
podr convertir nmeros decimales a: binario, octal, decimal, hexadecimal, etc.; y la nica condicin ser que la base
indicada est entre el 2 y el 36, inclusive.
Nota: Ya que la funcin servir para convertir nmeros a cualquier representacin la nombraremos como numstr() en
lugar de binstr(). Si la funcin es invocada sin el parmetro base regresar una cadena de digitos decimales.
#include <iostream> #include <stdlib.h> using namespace std; // declaracin de prototipo char *numstr(unsigned int,
const int base = 10); // punto de prueba int main() { int n = 128; cout << decimal = " << n << ", binario = " << numstr(n,
2) << endl; cout << decimal = " << n << ", octal.. = " << numstr(n, 8) << endl; cin.get(); } // denicin de funcin
numstr() // nota: esta funcion requiere de la librera stdlib.h char *numstr(unsigned int n, const int base) { static char
buer[65]; itoa(n, buer, base); return buer; }

7.10 Parmetros de tipo puntero


Anteriormente se mencion que en C++ los parmetros a una funcin pueden pasarse por valor o por referencia, al
respecto, podemos agregar que los parmetros tambin pueden pasarse como punteros. El paso de parmetros de punteros
es bastante parecido al paso de parmetros por referencia, salvo que el proceso de los datos dentro de la funcin es
diferente. Por ejemplo, las funciones:
void referencia( int &X ) { X = 100; } void puntero( int *X ) { *X = 100; }

ambas reciben un puntero o referencia a un objeto de tipo entero, por lo tanto cualquiera de las funciones del ejemplo
puede cambiar el valor de la variable entera apuntada por X, la diferencia radica en la forma en que cada una de las mismas
lleva cabo la tarea. Si en la funcin puntero() en lugar de usar *X = 100; se usara X = 100; se le asignara 100 al puntero
X, ms no al objeto apuntado por X, y esto podra ser la causa de que el programa se terminara de manera abrupta.

7.11 Parmetros estructurados


Al igual que cualquier otro tipo los parmetros de tipo estruturado pueden pasarse por valor o por referencia, sin embargo,
podra ser que si una estructura es pasada por valor el compilador mostrara una advertencia ( warning ) indicando que se
pasado por valor una estructura, puesto que el paso de estructuras por valor es permitido usted puede ignorar la advertencia,
pero lo mejor es pasar estructuras por referencia. Si una estructura es pasada por valor y si esta es muy grande podria ser
que se agotara la memoria en el segmento de pila ( Stack Segment ), aparte de que la llamada a la funcin sera ms lenta.
Para ver un ejemplo, consideremos el caso del siguiente tipo estructurado:
36 CAPTULO 7. FUNCIONES

struct empleado { char nombre[32]; int edad; char sexo; };

Ahora, pensemos que deseamos escribir una funcin para imprimir variables del tipo empleado. As, la funcin puede
escribirse de las tres maneras siguientes:
void ImprimeEmpleadoV( empleado e) { cout << Nombre: " << e.nombre << endl; cout << Edad: " << e.edad << endl;
cout << Sexo: " << e.sexo << endl; } // Parametro empleado pasado por referencia void ImprimeEmpleadoR( empleado
&e ) { cout << Nombre: " << e.nombre << endl; cout << Edad: " << e.edad << endl; cout << Sexo: " << e.sexo <<
endl; } // Parametro empleado pasado como puntero void ImprimeEmpleadoP( empleado *e ) { cout << Nombre: " <<
e->nombre << endl; cout << Edad: " << e->edad << endl; cout << Sexo: " << e->sexo << endl; }

7.12 Funciones sobrecargadas


C++, a diferencia del C estndar, permite declarar funciones con el mismo nombre y a esto se conoce como sobrecarga
de funciones. Las funciones sobrecargadas pueden coincidir en tipo, pero al menos uno de sus parmetros tiene que ser
diferente. En todo caso, si usted trata de declarar funciones sobrecargadas que coincidan en tipo y nmero de parmetros
el compilador no se lo permitir. Para poner un ejemplo vamos a considerar el caso de dos funciones cuyo nombre ser
divide, ambas regresarn el cociente de dos nmeros, salvo que una de ellas operar sobre nmeros enteros y la otra lo
har sobre nmeros reales ( de punto otante ).
Nota: cuando en los programas se hace una llamada a una funcin sobrecargada, el compilador determina a cual de las
funciones invocar en base al tipo y nmero de parmetros pasados a la funcin.
#include <iostream.h> #include <stdlib.h> using namespace std; // divide enteros int divide(int a, int b) { cout << divi-
sin entera << endl; return ( (b != 0) ? a/b : 0); } // divide reales double divide(double a, double b) { cout << divisin
real << endl; return ( (b != 0) ? a/b : 0); } // punto de prueba int main() { cout << divide(10, 3) << endl; cout <<
divide(10.0, 3.0) << endl; cin.get(); }

7.13 Nmero variable de parmetros


En C,C++ se pueden crear funciones que operen sobre una lista variable de parmetros, es decir, en donde el nmero de
parmetros es indeterminado. En esta seccin se mostrar un ejemplo de la manera en que podemos crear funciones para
manejar tales asuntos, y para ello haremos uso de tres macros soportadas por C++:

1. va_list puntero de argumentos


2. va_start inicializar puntero de argumentos
3. va_end liberar puntero de argumentos

La sintaxis que usaremos para declarar funciones con lista de parmetros variables es:
1) tipo nombrefuncion(...) 2) tipo nombrefuncion(int num, ...)
donde:

1. tipo es el tipo regresado por la funcin


2. nombrefuncion es el nombre de la funcin
3. int num es el nmero de parmetros que la funcin procesar
4. ... esta notacin se emplea para indicar que el nmero de parmetros es variable
7.13. NMERO VARIABLE DE PARMETROS 37

Nota: observe que la primera forma de declaracin es realmente variable el nmero de parmetros a procesar y en estos
casos se debe establecer el mecanismo para determinar cuando se ha procesado el ltimo de los argumentos, en el segundo
tipo de declaracin el nmero total de parmetros a procesar es igual al valor del parmetro num.
En el siguiente programa, por ejemplo, se dene una funcin ( printstr ) que despliega una lista variable de cadenas de
caracteres.
#include <iostream.h> #include <stdarg.h> // despliega una lista de cadenas, la ultima debe ser NULL void printstr(...)
{ va_list ap; char *arg; va_start(ap, 0); while ( (arg = va_arg(ap, char*) ) != NULL) { cout << arg; } va_end(ap); } int
main() { printstr(Hola, ", Esta es\n, una prueba\n, NULL); cin.get(); return 0; }

En el programa que se listar en seguida, se dene la funcin suma(), misma que operar sobre listas de nmeros enteros,
la funcin devolver la suma de dichos nmeros.
#include <iostream>//entrada y salida #include <stdarg.h> using namespace std; // Esta funcin opera sobre una lista
variable de nmeros enteros int suma( int num, ... ) { int total = 0; va_list argptr; va_start( argptr, num ); while( num > 0
) { total += va_arg( argptr, int ); num--; } va_end( argptr ); return( total ); } int main() { cout << suma(4, 100, 200, 300,
400) << endl; cin.get(); return 0; }
Captulo 8

Streams

38
Captulo 9

Streams, entrada y salida de datos

En este captulo abordaremos el tema de la manipulacin de datos a travs de los dispositivos de entrada y salida estndar
por medio de ciertos componentes lgicos conocidos como: streams. A manera de denicin, un stream es una especie
de canal a travs del cual uyen los datos. Tcnicamente, un stream es el enlace lgico utilizado por el programador en
C, C++ para leer o escribir datos desde y hacia los dispositivos estndar conectados a la PC. Normalmente, el dispositivo
estndar para manipular entradas es el teclado y este, en C++, est asociado al objeto cin; el dispositivo estndar de salida
est asociado (generalmente) con la pantalla o monitor de despliegue de la PC y, el mismo, en C++ se puede acceder por
medio del objeto cout. El dispositivo estndar para mensajes de error es el cerr, y el mismo est asociado por defecto con
la pantalla o monitor de despliegue. Otro dispositivo estndar es la impresora, pero este ltimo no es soportado por los
objetos de la iostream. Los programadores que hayan tenido alguna experiencia al programar en C estndar, notarn que
los tres objetos mencionados coinciden con los dispositivos: stdin, stdout y stderr. La tabla que se muestra en seguida
puede servirnos de base.

9.1 La iostream

La iostream es la biblioteca estndar en C++ para poder tener acceso a los dispositivos estndar de entrada y/o salida.
En sus programas, si usted desea hacer uso de los objetos cin, cout, cerr y clog tendr que incluir ( por medio de la
directiva #include ) el uso de la biblioteca iostream. En la iostream se encuentran denidas las clases ios ( misma que
es la base para las clases que implementen operaciones de entrada y/o salida de datos ), istream ( para operaciones de
entrada ) y ostream ( para operaciones de salida ). Aparte de las clases mencionadas, en la iostream se encuentra una
lista de variables y constantes ( atributos ) que son accesibles por el usuario a travs del operador de mbito ( :: ).

9.2 Streams automticos

Si usted usa la directiva #include <iostream.h> o #include <iostream> en sus programas, automticamente la iostream
pone a su disposicin los objetos cin, cout, clog y cerr en el mbito estndar (std), de tal manera que usted puede comenzar
a enviar o recibir informacin a travs de los mismos sin siquiera preocuparse de su creacin. Asi, un sencillo ejemplo del
uso de los objetos mencionados se muestra en seguida.
// De nuevo con el hola mundo... #include <iostream.h> int main() { std::cout << Hola mundo"; // imprimir mensaje
(en la pantalla) std::cin.get(); // lectura ( entrada del teclado ) return 0; }

39
40 CAPTULO 9. STREAMS, ENTRADA Y SALIDA DE DATOS

9.3 Operadores de direccionamiento


Los operadores de direccionamiento son los encargados de manipular el ujo de datos desde o hacia el dispositivo refe-
renciado por un stream especco. El operador de direccionamiento para salidas es una pareja de smbolos de menor
que <<, y el operador de direccionamiento para entradas es una pareja de smbolos de mayor que >>. Los operadores
de direccionamiento se colocan entre dos operandos, el primero es el Stream y el segundo es una variable o constante que
proporciona o recibe los datos de la operacin. Por ejemplo, en el siguiente programa y en la instruccin cout << Entre
su nombre: "; la constante Entre su nombre: " es la fuente o quien proporciona los datos para el objeto cout. Mientras
que en la instruccin cin >> nombre la variable nombre es el destino o quien recibe los datos provenientes del objeto cin.
// De nuevo con el hola mundo... #include <iostream.h> int main() { char nombre[80]; cout << Entre su nombre: "; cin
>> nombre; cout << Hola, << nombre; cin.get(); return 0; }

Observe que si en una misma lnea de comando se desea leer o escribir sobre varios campos a la vez, no es necesario
nombrar ms de una vez al stream. Ejemplos:
cout << Hola, << nombre; cin >> A >> B >> C;

9.4 Banderas de I/O


En esta seccin abordaremos de manera ms directa el tema sobre el control de formato para los stream de C++. Espec-
camente, veremos las tres diferentes formas que existen en C++ para manipular las banderas relacionadas a los stream
y que nos permitirn gobernar de una manera ms precisa la forma para representar datos de salida y/o entrada. En ese
sentido, veremos que la primera de las forma que nos permitir el formateo sera a travs de las funciones ags(), setf() y
unsetf(), la segunda y la tercera forma las encontraremos en ciertos manipuladores directos denidos en las bibliotecas
<iostream> y <iomanip>.

9.4.1 Banderas de formato:

C++ dene algunas banderas de formato para entradas y salidas estndar, las cuales pueden ser manipuladas a travs de
la funciones (mtodos) ags(), setf(), y unsetf(). Por ejemplo,
cout.setf(ios::left);
activa la justicacin a la izquierda para todas las salidas dirigidas hacia cout.
A continuacin se muestra una tabla de referencia de las banderas de I/O.
Manipulando la lista de banderas de I/O de C++ (mostrada arriba) se pueden controlar los aspectos relacionados a la
forma con la cual se desean presentar los datos en la salida. Por ejemplo, el programa que se muestra en seguida, activa
la bandera boolalpha para mostrar los resultados de operaciones booleanas como true o false en lugar de 0 o 1
como es lo normal.
// Programacin con C++ // programa Banderas01.cpp; // probado en Dev-Cpp Versin 4.9.9.2 #include <iostream>
using namespace std; int main(int argc, char *argv[]) { cout <<"\n0 > 1 ? "<<"\t"<<(0>1)<< endl; cout <<"\n5 > 1 ?
"<<"\t"<<(5>1)<< endl; cout.setf(ios::boolalpha); // activar bandera cout <<"\n0 > 1 ? "<<"\t"<<(0>1)<< endl; cout
<<"\n5 > 1 ? "<<"\t"<<(5>1)<< endl; cin.get(); return 0; }

9.4.2 Manipuladores

Las banderas tambin pueden manipularse directamente usando los siguientes manipuladores. Seguramente usted ya
estar familiarizado con el manipulador endl, el mismo puede darle una idea de cmo son usados los manipuladores. Por
ejemplo, usted puede establecer la bandera de nmeros decimales usando el comando:
9.4. BANDERAS DE I/O 41

cout << dec;


La tabla que se muestra en seguida corresponde a los manipuladores denidos en <iostream>.
El programa que se muestra en seguida es un ejemplo de como emplear manipuladores directos para formatear salidas
al stream estndar de salida ( cout ). En el mismo, se emplea el manipulador boolalpha para que los resultados de la
operaciones logicas sean textuales, o sea, true o false y los manipuladores dec, hex y oct.
// Programacin con C++ // programa Banderas02.cpp; // probado en Dev-Cpp Versin 4.9.9.2 #include <iostream>
using namespace std; int main(int argc, char *argv[]) { cout << boolalpha; cout << 0 > 1 ? " << '\t' << (0 > 1) << endl;
cout << 5 > 1 ? " << '\t' << (5 > 1) << endl; cout << dec << 2048 << endl; cout << hex << 2048 << endl; cout << oct
<< 2048 << dec << endl; cin.get(); return 0; }

Por ltimo, y para terminar esta seccin, hablaremos de los manipuladores denidos en la biblioteca <iomanip>. Estos
operan directamente igual que los que se viern anteriormente, salvo que son parametrizados, es decir, operan en lnea
de salida mediante el operador <<, pero los mismos operan a manera de funciones, o sea con parmetros.
Atendiendo a las tablas de banderas, as como a las tablas de manipuladores para las mismas mostradas arriba, usted puede
(con practica y perceverancia) lograr salidas con muy buena presentacin hacia los dispositivos estndar. Por ejemplo, el
programa que se mostrar a continuacin muestra una de las formas de emplear manipuladores para formatear nmeros
de punto otante, en el programa se despliega una lista de valores numricos y se establece el campo de salida a una
longitud de 12 caracteres y dos decimales.
// Programacin con C++ // programa Banderas03.cpp; // probado en Dev-Cpp Versin 4.9.9.2 #include <iostream> #in-
clude <iomanip> using namespace std; int main() { double data[] = { 347.25, 45.75, 124.50, 456.80, 1500.90 }; double
total = 0; int ancho = 12; cout.precision(2); cout.setf(ios::xed); for (int c = 0; c < 5; c++) { cout << setw(ancho) <<
data[c] << endl; total += data[c]; } cout.ll('-'); cout << setw(ancho) << "" << endl; cout.ll(' '); cout << setw(ancho) <<
total << endl; cout << Por favor oprime Enter..."; cin.get(); return 0; }
Captulo 10

Streams para archivos o cheros

Por denicin, un archivo es una coleccin de datos almacenados en algn lugar. En C++, el programador puede considerar
que un archivo es un stream, de tal manera que los objetos vistos en la seccin anterior ( cin, cout, cerr y clog ) son una
especie de archivo manipulados por streams. Este punto es de suma importancia, ya que todo lo que se ha mencionado
acerca de los atributos, banderas y manipuladores; que son miembros de los objetos ( stream ) para entrada y salida
estndar, son aplicables a los streams que usemos para trabajar con archivos en disco y/o cualquier otro dispositivo de
almacenamiento. Hasta este momento, solamente se ha mencionado que los streams poseen banderas y que las mismas
pueden alterarse a traves de los manipuladores, sin embargo, hace falta hablar acerca de los mtodos o funciones que son
miembros de dichos streams. As, antes de ver un ejemplo para mostrar el uso de archivos en disco listaremos una tabla
ms, o sea, la de los mtodos aplicables a los streams.

10.1 Abrir y cerrar archivo


A diferencia de los streams para dispositivos estndar, los cuales son creados y abiertos de manera automtica, para
trabajar con archivos en discos se debe primeramente abrir el archivo, y luego de haber terminado de leer o escribir
datos en el mismo, se debe cerrar el archivo. En C++, en orden de trabajar con archivos en disco, podemos emplear las
clases fstream, ifstream, y ofstream. Si usted desea abrir un archivo especco en modo de lectura y escritura use un
objeto de la clase fstream, por el contrario, si desea abrir un archivo solo para lectura o solo para escritura use objetos
de la clase ifstream y ofstream, respectivamente.
La sintaxis para crear objetos de las tres clase mencionadas es:
'''streams para lectura y escritura''' fstream(); fstream(const char*, int, int = lebuf::openprot); fstream(int); fstream(int
_f, char*, int); '''streams solo para lectura''' ifstream(); ifstream(const char*, int, int = lebuf::openprot); ifstream(int);
ifstream(int _f, char*, int); '''streams solo para escritura''' ofstream(); ofstream(const char*, int, int = lebuf::openprot);
ofstream(int); ofstream(int _f, char*, int);

Observe que la sintaxis para crear objetos de las tres clases mostradas arriba es la misma. Es decir,

1. El primer mtodo constructor crea un objeto que no est (an) asociado a un archivo en disco, en estos casos, se
tendr que usar el mtodo stream.open(nombre de archivo); para establecer la conexin entre el stream y el
archivo en disco.
2. El segundo mtodo constructor crea un objeto asociado a un archivo en disco, en estos casos, el archivo en el disco
queda abierto y asociado al stream. En este, el parmetro char * apunta a una cadena de caracteres con el nombre
del archivo.
3. El tecer mtodo crea un stream a raiz de un identicador de archivo.
4. El cuarto mtodo crea un stream a raiz de un identicador de archivo, un buer y tamao de buer especcos.

42
10.1. ABRIR Y CERRAR ARCHIVO 43

Para nuestro primer ejemplo usaremos el segundo de los constructores mencionados. As, el programa que se muestra en
seguida realiza las siguientes tareas:

1. Crea y escribe sobre el archivo de texto test.$$$


2. Abre y lee los datos del archivo test.$$
3. Cierra el archivo test.$$

Para el caso que se presenta se debe prestar atencin a los metodos:

1. bad(), para vericar el estado de error del stream


2. get(), para leer caracteres del stream
3. put(), para escribir caracteres en el stream
4. close(), para cerrar el archivo.

// Ejemplo de ifstream y ofstream // En este programa se demuestra la forma de crear un archivo // en disco por medio
de objeto ofstream, y la manera abrir y // leer un archivo por medio de un objeto ifstream. #include <iostream> #include
<fstream> #include <string.h> using namespace std; char *lename = test.$$$"; char *data = Esta lnea de texto se
guardar en el archivo test.$$$"; // crear un archivo en disco cuyo nombre es dado por lename int crearArchivo(char
*lename) { ofstream chero(lename); // crear o rescribir archivo // vericar la creacin del archivo if ( chero.bad()
) { cout << Error al tratar de abrir archivo"; cin.get(); return 1; } // escribir datos al archivo for (unsigned int t = 0; t <
strlen(data); t++ ) chero.put(data[t] ); chero.close(); cout << archivo creado exitosamente << endl; return 0; } // abrir
un archivo en disco cuyo nombre es dado por lename int leerArchivo(char *lename) { ifstream chero(lename); //
abrir archivo para lectura // vericar la apertura del archivo if ( chero.bad() ) { cout << Error al tratar de abrir archivo";
cin.get(); return 1; } // lectura de datos while ( ! chero.eof() ) cout << (char)chero.get(); chero.close(); cout << endl
<< archivo leido exitosamente << endl; return 0; } int main() { crearArchivo(lename); leerArchivo(lename); cout
<< endl << Presione <Enter>..."; cin.get(); return 0; }

Usando operadores de redireccin ( <<, >> )


sobre streams asociados con archivos en disco.
No hay que olvidar que si un archivo es asociado a un stream las operaciones de entrada y/o salida para dicho archivo
se hacen a travez del stream y, por lo tanto, se puede usar, no solo los operadores de redireccin, sino tambin todos los
metodos, manipuladores y atributos que se ha mencionado hasta este momento. As, el objetivo del siguiente programa
es demostrar como hacer uso del operador << sobre un archivo asociado a un stream de salida, y del operador >> sobre
un archivo asociado a un stream de entrada. El ejemplo que se mostrar no es realmente til, sin embargo cumple con su
cometido, es decir, el punto de interes del mismo est en las lneas de cdigo:
while ( ! n.eof() ) { n >> temp; fout << temp ; }

Encargadas de leer datos del archivo de entrada y de escribir los mismos en el archivo de salida.
#include <iostream.h> #include <fstream.h> char *lename = test.$$$"; // abre y lee datos de un archivo en disco cuyo
nombre es dado por lename int leerArchivo(char *lename) { ifstream chero(lename); // abrir archivo para lectura
// vericar la apertura del archivo if ( chero.bad() ) { cout << Error al tratar de abrir archivo"; cin.get(); return 1; } //
lectura de datos while ( ! chero.eof() ) cout << (char)chero.get(); chero.close(); cout << endl << archivo leido exito-
samente << endl; return 0; } // crea una copia del archivo test.$$$ hacia test.bak // Nota: ningn carcter de espaciado
en el origen le es tranferido al destino int backup(char *lename) { char newname[80]; int i = 0; // copia el nombre del
archivo original y remplaza la extensin por BAK. while (lename[i] != '.' ) newname[i] = lename[i++]; newname[i]
= '\0'; strcat(newname, ".BAK); ifstream n( lename ); // abrir archivo de entrada ofstream fout( newname, ios::trunc
); // abrir archivo de salida char temp; while ( ! n.eof() ) { n >> temp; fout << temp ; } n.close(); fout.close(); return
0; } int main() { backup(lename); leerArchivo(test.bak); cout << endl << Presione <Enter>..."; cin.get(); return 0; }
Captulo 11

Arrays y cadenas de texto

11.1 Arrays y cadenas de texto


Los arrays son usados extensamente por los programadores para contener listas de datos en la memoria, por ejemplo, los
datos almacenados en un disco suelen leerse y ponerse dentro de un array con el objetivo de facilitar la manipulacin de
dichos datos, ya que los datos en memoria pueden ser modicados, clasicados, marcados para su eliminacion, etc. para
luego ser reescritos al disco. Otro ejemplo podra ser el de un men de opciones que se desplegarn dentro de una ventana
para que el usuario pueda elegir una de stas, en tales casos y cuando las opciones son numerosas, solamente se ponen
unas cuantas de ellas dentro de la ventana pero se le da al usuario la oportunidad de poder subir y bajar a su antojo para
ver el resto de opciones que, aunque no se vean en la ventana, forman parte del men o array de opciones.
Array:
Un array es un conjunto de datos del mismo tipo ordenados de forman lneal uno despus de otro. Los componentes de
un array se han de referenciar por medio del nombre del array y un ndice de desplazamiento para indicar el componente
deseado.

11.2 Indices de un array


Los ndices son nmeros que se utilizan para identicar a cada uno de los componentes de un array. A modo de ejemplo,
podemos pensar que los ndices son como los nmeros de habitaciones de un hotel, es decir, para poder dirigirnos a un
hotel especco es necesario saber el nombre del mismo, luego, si queremos llegar a una habitacin especca de dicho
hotel necesitaremos, adems del nombre del hotel, el nmero de habitacin deseado.

11.3 Dimensiones de un array


De acuerdo a la forma en que se construye o declara un array, ste puede ser clasicado como: unidimensional, bidimen-
sional y multidimensional. Los arrays que se emplean con mucha ms frecuencia son los estructurados a manera de vector
( array unidimensional ) y los estructurados a manera de matriz ( array bidimensional ), as, aunque en C++ se pueden
crear estructuras multidimensionales, en este captulo solo trataremos con vectores y matrices.

11.3.1 Array unidimensional


Una array uni-dimensional es aquel en donde los componentes son accesibles por medio de uno y solamente un ndice
que apunte al componente requerido. Los arrays de este tipo son conocidos tambin con el nombre de vectores. Concep-
tualmente, podemos pensar en un array unidimensional como una lista compuesta de datos, donde para referirnos a uno

44
11.4. DECLARACIN DE ARRAYS EN C, C++ 45

de ellos emplearemos un nmero para indicar la posicin del mismo dentro de la lista. Por ejemplo, consideremos el caso
de la tabla o array VentaSemanal, la cual est pensada para registrar las ventas de cada uno de los das de la semana. De
manera conceptual podemos ver el array como se muestra a continuacin:

Nota: en C++ los arrays estn basados en 0 ( cero ), es decir, el primer elemento de un array se indexa
mediante el 0, y el ndice para el ltimo de los elementos es igual al nmero de componentes menos uno.

array: VentaSemanal +------+ | dato | <-- componente 0, ( la 0 ) |------| | dato | <-- componente 1, ( la 1 ) |------| | dato |
... |------| | dato | ... |------| | dato | ... |------| | dato | ... |------| | dato | <-- componente 6, ( la 6 ) |------|
Si en el array VentaSemanal queremos que el elemento 4 ( por ejemplo ) contenga el valor de 8987 lo podemos lograr
con la instruccin: VentaSemanal[4] = 8987; y el estado del array sera:
array: VentaSemanal +------+ | dato | |------| | dato | |------| | dato | |------| | dato | |------| | 8987 | <--- componente 4 |------|
| dato | |------| | dato | |------|

11.3.2 Array bidimensional


Un array bi-dimensional es aquel en donde los componentes son accesibles por medio de una pareja de ndices que apunten
a la la y a la columna del componente requerido. Los arrays de este tipo son conocidos tambin con el nombre de matrices.
Conceptualmente, podemos pensar en un array bidimensional como en una lista compuesta de las y columnas, en donde
para referirnos a una de ellas emplearemos un nmero para indicar la posicin de la y otro nmero para indicar la
posicin de la columna del componente deseado. Por ejemplo, consideremos el caso de la tabla o array VentaSemanaQ,
la cual est pensada para registrar las ventas de cada uno de los das de la semana por cuatro semanas, o sea, una tabla de
7 x 4 elementos. De manera conceptual podemos ver el array como se muestra a continuacin:
array: VentaSemanaQ C O L U M N A S +--- componente ( 0, 0 ) | +------+------+------+------+ | dato | dato | dato | dato |
|------|------|------|------| F | dato | dato | dato | dato | |------|------|------|------| I | dato | dato | dato | dato | |------|------|------|--
----| L | dato | dato | dato | dato | |------|------|------|------| A | dato | dato | dato | dato | |------|------|------|------| S | dato | dato
| dato | dato | |------|------|------|------| | dato | dato | dato | dato | +------+------+------+------+ | +---- componente ( 6, 3 )
Si en el array VentaSemanaQ queremos que el elemento de la la 4, columna 3 ( por ejemplo ) contenga el valor de 5000
lo podemos lograr con la instruccin: VentaSemanaQ[4][3] = 5000; y el estado del array sera:
array: VentaSemanaQ +--- componente ( 0, 0 ) | +------+------+------+------+ | dato | dato | dato | dato | |------|------|------
|------| | dato | dato | dato | dato | |------|------|------|------| | dato | dato | dato | dato | |------|------|------|------| | dato | dato |
dato | dato | |------|------|------|------| | dato | dato | dato | 5000 | <-- componente ( 4, 3 ) |------|------|------|------| | dato | dato
| dato | dato | |------|------|------|------| | dato | dato | dato | dato | +------+------+------+------+ | +---- componente ( 6, 3 )

11.4 Declaracin de arrays en C, C++


En C, C++ para declarar un array se emplea la sintaxis:
tipo identicador [tamao] = { lista de inicializacin } ;
donde,

tipo se reere al tipo de datos que contendr el array. El tipo puede ser cualquiera de los tipos estndar (char, int,
oat, etc.) o un tipo denido por el usuario. Es ms, el tipo del array puede ser de una estructura creada con: struct,
union y class.

identicador se reere al nombre que le daremos al array.

tamao es opcional e indica el nmero de elementos que contendr el array. Si un array se declara sin tamao, el
mismo no podr contener elemento alguno a menos que en la declaracin se emplee una lista de inicializacin.
46 CAPTULO 11. ARRAYS Y CADENAS DE TEXTO

lista de inicializacin es opcional y se usa para establecer valores para cada uno de los componentes del array. Si el
array es declarado con un tamao especco, el nmero de valores inicializados no podr ser mayor a dicho tamao.

Ejemplos:
int intA[5]; long longA[5] = { 1, 2, 3, 4, 5 }; char charA[3] = { 'a', 'b', 'c' };

11.4.1 Iteraciones dentro de un array (vector)


El termino Iterar se reere al hecho de acceder (con el n de leer o escribir) sobre cada uno de los componentes de un
array. As, para poner un ejemplo reconsideremos el caso de la tabla VentaSemanal (vista en una seccin anterior), y
que dicho sea de paso es un array de 7 elementos de tipo double. Luego, vamos a mostrar como ejemplo un programa
completo en el cual se declara el array mencionado con valores inicializados, que sern mostrados en pantalla y al nal
la suma de estos. Observe que la variable i usada para iterar dentro del array va desde 0 hasta FILAS - 1 ( FILAS es el
tamao del array ).

Nota: por motivos de simplicacin el programa est escrito al estilo de C estndar. Sin embargo puede
ser compilado y ejecutado en un compilador de C++.

#include <stdio.h> #include <stdlib.h> #dene FILAS 7 int main() { oat ventas[FILAS] = { 123.50, 234.60, 345.45,
321.40, 345.00, 456.65, 0.0 }; oat total = 0; int i; puts(Ventas de la semana); puts("-------------------"); for (i=0;
i<FILAS; i++) { total += ventas[i]; printf( "%8.2f\n, ventas[i] ); } puts("--------"); printf("%8.2f\n, total ); system(pause);
return 0; } Esta es la salida del programa: Ventas de la semana ------------------- 123.50 234.60 345.45 321.40 345.00
456.65 0.00 -------- 1826.60

11.4.2 Iteraciones dentro de un array (matriz)


Con el n de leer o escribir sobre cada uno de los componentes de una matriz se deben crear dos ciclos de iteracin. As,
para poner un ejemplo reconsideremos el caso de la tabla VentaSemanaQ (vista en una seccin anterior), y que dicho
sea de paso es un array de 4 x 4 elementos de tipo double. Luego, vamos a mostrar como ejemplo un programa completo
en el cual se declara el array mencionado con valores inicializados, que sern mostrados en pantalla y al nal la suma de
estos. Observe que en este caso se utilizan dos variables, una para iterar sobre las las y otra para iterar sobre las columnas
de la matriz.
#include <stdio.h> #include <stdlib.h> #dene FILAS 7 #dene COLS 4 int main() { oat VentaSemanaQ[FILAS][COLS]
= { 123.50, 234.60, 345.45, 321.40, 345.00, 456.65, 123.50, 234.60, 345.45, 321.40, 345.00, 456.65, 123.50, 234.60,
345.45, 321.40, 345.00, 456.65, 123.50, 234.60, 345.45, 321.40, 345.00, 456.65, 0.0, 0.0, 0.0, 0.0 }; oat totales[COLS]
= { 0.0, 0.0, 0.0, 0.0 }; oat grantotal = 0; int f, c, t = 0 ; /* indices para las, columnas y totales */ puts(Ventas de cuatro
semanas); puts("------------------------"); for (f=0; f<FILAS; f++) { for (c=0; c<COLS; c++) { totales[c] += VentaSe-
manaQ[f][c]; printf("%8.2f ", VentaSemanaQ[f][c] ); } puts(""); } puts("--------------------------------------"); for (t=0;
t<COLS; t++) { printf("%8.2f ", totales[t] ); grantotal += totales[t]; } printf("\n\nGran total: %10.2f\n, grantotal);
system(pause); return 0; } Salida del programa: Ventas de cuatro semanas ------------------------ 123.50 234.60 345.45
321.40 345.00 456.65 123.50 234.60 345.45 321.40 345.00 456.65 123.50 234.60 345.45 321.40 345.00 456.65 123.50
234.60 345.45 321.40 345.00 456.65 0.00 0.00 0.00 0.00 -------------------------------------- 1627.90 2025.30 1627.90
2025.30 Gran total: 7306.40

11.5 Cadenas de caracteres


En C, C++ las cadenas de caracteres no son ms que arrays de caracteres, salvo que a este tipo de arrays el compilador
les da un tratamiento especial. Usted puede manipular las cadenas de caracteres de la misma manera en que manipula
11.5. CADENAS DE CARACTERES 47

cualquier otro tipo de array, sin embargo, es preferible hacer uso de una librera estndar especialmente escrita para
manipulacion de cadenas de caracteres, me reero a la librera <string.h> y que viene incluida con todo compilador de
C, C++.
Para comenzar y antes de ver algunas de las funciones de la mencionada librera, tenemos los siguientes ejemplos:
1. char nombre[] = Oscar"; 2. char nombre2[] = { 'O', 's, 'c', 'a', 'r', '\0' };

En el ejemplo 1 se est declarando la variable nombre como una cadena de caracteres y cuyo contenido inicial es
Oscar.

En el ejemplo 2 se est declarando la variable nombre2 como una cadena de caracteres y cuyo contenido inicial es
{ 'O', 's, 'c', 'a', 'r', '\0' };.

En ambos casos el resultado es el mismo, es decir, al nal se obtiene la misma cadena, pero usted debe poner atencin al
hecho de que toda cadena de caracteres en C, C++ debe terminar con el carcter NULL, que normalmente es igual a cero
y se puede escribir como '\0'. Ahora bien, cuando usted usa la sintaxis mostrada en el ejemplo 1 no tiene que preocuparse
por agregar el caracter NULL, ya que esto lo hace el compilador automticamente.

11.5.1 La biblioteca string

Los compiladores de C, C++ dan soporte a la biblioteca de funciones <string.h>, a la que accede por medio de la directiva
#include <string.h>. No veremos en detalle todas las funciones contenidas en dicha biblioteca, y nos limitaremos a
mostrar algunos ejemplos de ciertas funciones importantes.
strlen(): Obtener longitud de cadenas

Sintaxis: size_t strlen(const char *s);

Comentarios: La funcin strlen() devuelve la longitud de la cadena s.

Ejemplo:
char *nombre = Oscar E. Palacios"; cout << strlen(nombre) << endl;

strcpy(): Copiar cadenas

Sintaxis: char *stpcpy(char *dest, const char *src);

Comentarios: stpcpy copia la cadena src hacia dest, la funcin termina hasta haber encontrado en
src el carcter de terminacin null.

Ejemplo:
char *nombre = Oscar E. Palacios"; char copia[80]; strcpy(copia, nombre); cout << copia << endl;

strcat(): Concatenar cadenas

Sintaxis: char *strcat(char *dest, const char *src);

Comentarios: strcat agrega la cadena src a dest, la funcin termina hasta haber encontrado en src
el carcter de terminacin null.
48 CAPTULO 11. ARRAYS Y CADENAS DE TEXTO

Ejemplo:
char nombre[] = Oscar E."; char copia[80] = " Palacios"; strcat(copia, nombre); cout << copia << endl;

strlwr(): Convertir a minsculas.

Sintaxis: char *strlwr(char *dest);

Comentarios: strlwr convierte todos los caracteres alfabticos ( 'A' .. 'Z' ) en dest a sus corres-
pondientes caracteres alfabticos ( 'a' .. 'z' ).

Ejemplo:
char nombre[] = Oscar E. Palacios"; strlwr(nombre); cout << nombre << endl;

strupr(): Convertir a maysculas.

Sintaxis: char *strupr(char *dest);

Comentarios: strupr convierte todos los caracteres alfabticos ( 'a' .. 'z' ) en dest a sus correspon-
dientes caracteres alfabticos ( 'A' .. 'Z' ).

strchr(): Buscar carcter ( hacia adelante )

Sintaxis: char *strchr(char *s, int c);

Comentarios: strchr busca en s el caracter c. La busqueda se lleva a cabo desde el inicio hasta el
nal de s.

Regreso: si la operacin es exitosa strchr regresa un puntero hacia la primera ocurrencia de c en


s, en caso contrario strchr regresa null.

Ejemplo:
char nombre[] = Oscar E. Palacios"; char *p; p = strchr(nombre, 'E'); if (p) { cout << nombre contiene a E << endl;
cout << indice = " << (p - nombre) << endl; } else cout << E no est en nombre << endl;

strrchr(): Buscar carcter ( hacia atras )

Sintaxis: char *strrchr(char *s, int c);

Comentarios: strchr busca en s el caracter c. La busqueda se lleva a cabo desde el nal hasta el
inicio de s.

Regreso: si la operacin es exitosa strchr regresa un puntero hacia la ltima ocurrencia de c en s,


en caso contrario strchr regresa null.

Ejemplo:
char nombre[] = Oscar E. Palacios"; char *p; p = strrchr(nombre, 'E'); if (p) { cout << nombre contiene a E << endl;
cout << indice = " << (p - nombre) << endl; } else cout << E no est en nombre << endl;

strstr(): Buscar subcadena


11.6. CADENAS EN C++ 49

Sintaxis: char *strstr(const char *s1, char *s2);

Comentarios: strstr busca en s1 la subcadena s2. La bsqueda se lleva a cabo desde el inicio hasta
el nal de s1.

Regreso: si la operacin es exitosa strstr regresa un puntero hacia la primera ocurrencia de s2 en


s1, en caso contrario strstr regresa null.

Ejemplo:
char s[] = Un barco de tristeza"; char *p; p = strstr(s, barco); if (p) { cout << barco est en s << endl; cout << indice
= " << (p - s) << endl; } else cout << barco no est en s << endl;

11.6 Cadenas en C++


En la seccin anterior descubrimos algunas funciones para trabajar con cadenas de caracteres al estilo de C estndar,
si bien no est de ms tener tal conocimiento, tambin es cierto que C++ es un lenguaje de programacn orientado a
objetos, de tal manera que ciertos compiladores ( como el gcc, utilzado por Bloodshed Dev-C++ y otros tantos entornos
de desarrolo ) dan soporte a la clase cstring, que no debe confundirse con la <string.h>.

Nota: Bloodshed Dev-C++ es un IDE (Editor con Depurador Integrado) para programar en C++
en un ambiente grco para Windows, distibuido gratuitamente bajo licencia GPL GNU y usted
puede encontrarlo aqu: www.bloodshed.net. Actualmente (febrero de 2008) se recomienda bajar
la versin Dev-C++ 4.9.9.2.

Nota:Como el Dev-c++ ya esta descontinuado, es recomendable usar su extensin Wx Dev-C++


que esta actualmente activa y es recomendable para muchos proyectos bajo el lenguaje C++, este
programa tambien es licencia GPL, podras descargarlo desde=wxdsgn.sourceforge.net, y encon-
traras informacion de este aqui=es.wikipedia.org/wiki/WxDev-C%2B%2B.

Una de las ventajas que ofrece la clase cstring es que, a diferencia de las cadenas estndar, sta posee la capacidad
de crecer o disminuir su tamao en tiempo de ejecucin. Adems, entre otras caracteristicas destacables, la clase string
soporta operaciones de asignacin tales como: =, +, +=, etc.; y de comparacin tales como: ==, <=, etc.
Para tener una idea bsica sobre las cadenas en C++ veamos el siguiente programa:

Nota: en el programa se debe de observar el uso del operador de asignacin +=, algo que no es
posible hacer con las cadenas estndar.

Un estudio exhaustivo sobre la clase string requiere de un captulo completo, ya que la misma, segn el manual de refe-
rencia de C++, posee aproximadamente 33 mtodos y unos 7 constructores; adems de los atributos.

11.7 Arrays en C++


As como C++ da aternativas elegantes para la manipulacin de cadenas de caracteres, tambin da el soporte para la
manipulacon de arrays dinmicos. Este tema ser ampliado en el captulo Libreria de Plantillas Estndar STL, sin
embargo para tener una idea de lo que vendr mostraremos aqu un ejemplo sencillo en donde se usar la clase plantilla
vector.
Captulo 12

Desarrollo Orientado a Objetos

12.1 Desarrollo Orientado a Objetos


Antes de empezar a programar con objetos, es necesario entender ciertos conceptos bsicos.
Terminologa
El paradigma de Programacin Orientada a Objetos utiliza algunos trminos bsicos que se explicarn a continuacin.

Objeto. Es una entidad que tiene atributos o propiedades y que presenta un comportamiento. Puede ser un objeto
del mundo real, o una abstraccin al mundo informtico. Ejemplo, un automvil, una persona, un animal, etc.

Clase. Los humanos percibimos el mundo como un entorno compuesto de cosas, objetos, les llamamos. Interactua-
mos con tantos, que lo natural es agruparlos de acuerdo a sus caractersticas ms relevantes o generales. En otras
palabras, los clasicamos en clases. Todos los objetos que pertenecen a una clase, contiene todos los elementos
comunes a todos los objeto y no particulariza en los elementos que distinguen a los objetos entre s; ejemplo, la
clase automvil, comprende todos los automviles que circulan en este momento por las calles. Incluira elemen-
tos que asociamos a los automviles, como llantas, puertas, asientos, volante, etctera, pero no particularizara en
cuestiones como color, nmero de registro o caractersticas individuales a cada auto.

Propiedad. Una caracterstica asociada a una clase. Ejemplo, todos los autos tienen un color, o un registro aunque
esta propiedad vare entre objetos.

Mtodo. Posible accin que los objetos de una clase pueden realizar. En trminos informticos, un algoritmo que
pueden ejecutar. Ejemplo: todos los autos pueden acelerar, frenar, etc.

12.2 Caractersticas de la programacin orientada a objetos


Encapsulamiento. Cuando conducimos un auto, no es necesario que nos enteremos de como hace el motor para
girar las ruedas del vehculo. Esos pormenores quedan ocultos al usuario o encapsulados en la estructura del la clase
auto. Cada objeto presenta una interfaz al usuario de mtodos y propiedades con las cuales se interacta con l.
As se protege el objeto y todos los miembros de su clase, de alteraciones directas a sus mtodos y propiedades.

Polimorsmo. Una vez que aprendemos a conducir un auto, podemos manejar prcticamente cualquier auto. Esto
se debe a que la interfaz que presentan los objetos auto a los usuarios, es ms o menos la misma para cada auto.

50
12.2. CARACTERSTICAS DE LA PROGRAMACIN ORIENTADA A OBJETOS 51

Todos tiene mtodos para acelerar, y si bien, posiblemente los pormenores de como acelera cada auto, sean sustan-
cialmente diferentes; el usuario hace referencia a un mismo mtodo en todos los autos. En trminos informticos,
objetos de clases diferentes; incluso en la misma clase; pueden tener mtodos homnimos, que hagan cosas equi-
valentes aunque los detalles del como, sean diferentes.

Herencia. La clase auto es una clase muy general; se puede hacer un desglose ms pormenorizado, por ejemplo, si
hacemos subclases de autos deportivos, utilitarios, compactos, manuales, automticos, de lujo, etc. Cada subclase
toma de su clase base (o superclase, en la jerga informtica) las propiedades y mtodos ms generales y agrega
las propias. As, como todos los autos, tienen color y registro, as que stas estaran contenidas en la superclase
auto. Propiedades como Capacidad en toneladas, sistema estreo o cantidad de velocidades, estaran delegadas a
las subclases ms particulares.
Captulo 13

Objetos y Clases

13.1 Clases y Objetos


Este capitulo introduce a las clases en C++. La clase es la fundacin de C++ para el soporte de la programacin orientada
a objetos, y se encuentra en el ncleo de muchas de sus ms avanzadas caractersticas. La clase es la unidad bsica de
C++ de la encapsulacin y esta provee el mecanismo por el cual los objetos son creados.

13.2 Fundamentos de Clases


Vamos a comenzar deniendo los trminos de clase y objeto. Una clase dene un nuevo tipo de dato que especica la
forma de un objeto. Una clase incluye los datos y el cdigo que operar sobre esos datos. Adems, una clase enlaza datos y
cdigo. C++ usa una especicacin de una clase para construir objetos. Los objetos son instancias de una clase. Adems,
una clase es esencialmente una serie de planes que especican cmo construir un objeto. Es importante tener claro esto:
Una clase es una abstraccin lgica.
No es sino hasta que un objeto de esa clase sea creado que la representacin fsica de la clase existe en la memoria.
Cuando se dene una clase, se declaran los datos que sta contiene y el cdigo que opera en esos datos. Aunque clases
muy simples pueden contener slo cdigo o slo datos, la mayora de las clases contienen ambos. En conjunto, los datos
se almacenan en las variables y el cdigo en las funciones. Colectivamente, las funciones y variables que constituyen una
clase son llamados 'miembros de la clase. Una variable declarada dentro de una clase es llamada 'variable miembro', y
una funcin declarada en una clase es llamada 'funcin miembro'. En ocasiones el trmino 'variable de instancia' es usado
en lugar de variable miembro.
Una clase es creada con la palabra clave class. La declaracin de una clase es similar sintcticamente a una estructura (
y tienen muchsimo que ver ). Aqu tenemos un ejemplo. La siguente clase dene un tipo llamado CRender, el cual es
usado para implementar operaciones de renderizado en este caso.
// Esto dene la clase CRender class CRender { char buer[256]; public: void m_Renderizar(); };
Veamos ms de cerca esta declaracin de la clase.
Todos los miembros de CRender son declarados dentro de la declaracin 'class. La variables miembro de CRender es
buer. La funcin miembro es m_Renderizar.
NOTA: Por defecto los miembros de una clase son privados.
Una clase puede contener tanto miembros privados como pblicos. Por defecto, todos los elementos denidos en una clase
son privados. Por ejemplo la variable buer es privada. Esto signica que slo pueden acceder a ella otros miembros de
la clase CRender, cosa que no podr hacer ninguna otra parte del programa. Es una forma de lograr la encapsulacin: se
puede controlar el acceso a ciertos elementos de datos mantenindolos privados. Aunque no hay ninguna en este ejemplo,
se pueden denir funciones privadas, las cuales pueden ser llamadas solamente por otros miembros de la clase.

52
13.2. FUNDAMENTOS DE CLASES 53

Para hacer pblica una parte de la clase ( accesible a otras partes del programa ), se debe declarar con la palabra clave
public. Todas las variables o funciones denidas despus de la declaracin pblica son accesibles por todas las dems
funciones en el programa. En nuestra clase CRender, la funcin m_Renderizar() es pblica. Tpicamente, su programa
acceder a los miembros privados de una clase a travs de sus funciones pblicas. Note que la palabra clave public es
seguida con : . Mantenga en mente que un objeto forma una relacin entre cdigo y datos. Una funcin miembro tiene
acceso a los elementos privados de su clase. Esto signica que m_Renderizar tiene acceso a buer en nuestro ejemplo.
Para aadir una funcin miembro a la clase, debe especicar su prototipo en la denicin de la misma.
Una vez que se ha denido una clase, se puede crear un objeto de ese tipo usando el nombre de la clase. El nombre de la
clase se convierte en un especicador del nuevo tipo. Por ejemplo la siguiente declaracin crea 2 objetos llamados render1
y render2 del tipo CRender.
CRender render1, render2;
Cuando un objeto de la clase es creado, ste tendr su propia copia de las variables miembros que contiene la clase. Esto
signica que render1 y render2 tendrn su propia e independiente copia de buer. Los datos asociados con render1 son
distintos y separados de los datos asociados con render2.
Recordemos: En C++, una clase es un nuevo tipo de dato que puede ser usado para crear objetos. Especcamente, una
clase crea una consistencia lgica que dene una relacin entre sus miembros. Cuando se declara una variable de una
clase, se est creando un objeto. Un objeto tiene existencia fsica, y es una instancia especca de una clase. ( Esto es,
un objeto ocupa espacio de memoria, pero una denicin de tipo no ). Adems, cada objeto de una clase tiene su propia
copia de los datos denidos dentro de esa clase.
Dentro de la declaracin de CRender, el prototipo de una funcin es especicado. Ya que las funciones miembros son
prototipadas dentro de la denicin de la clase, no necesitan ser prototipadas en otro lugar cualquiera.
Para implementar una funcin que es un miembro de una clase, debe indicarle al compilador a cual clase pertenece la
funcin calicando el nombre de la funcin con el nombre de la clase. Por ejemplo, esta es una manera de codicar la
funcin m_Renderizar().
void CRender::m_Renderizar() { strcpy(buer, C++ en wikibooks); return; }

13.2.1 Resolucin de mbito


El :: es llamado el operador de resolucin de mbito. Esencialmente le dice al compilador que esta versin de m_Renderizar
pertenece a la clase CRender. Dicho de otra forma, :: declara que m_Renderizar se encuentra en el mbito de CRender.
Varias clases diferentes pueden usar los mismos nombres de funcin. El compilador sabe cul funcin pertenece a cul
clase y esto es posible por el operador de resolucin de mbito y el nombre de la clase.

13.2.2 Acceso a las funciones


Las funciones miembros de una clase slo pueden ser llamadas relativas a un objeto especco. Para llamar a una funcin
miembro desde alguna parte del programa que se encuentre fuera de la clase, se debe usar el nombre del objeto y el
operador de direcionamiento '.' ( punto ). Por ejemplo, lo siguiente llama a m_Renderizar() en el objeto objeto1.
CRender objeto1, objeto2; objeto1.m_Renderizar();
La invocacin de objeto1.m_Renderizar() causa a m_Renderizar() operar en los datos de la copia de objeto1. Mantenga
en mente que objeto1 y objeto2 son 2 objetos separados. Esto signica, por ejemplo, que inicializar objeto1 no causa que
objeto2 sea inicializado, La nica relacin que objeto1 tiene con objeto2 es que es un objeto del mismo tipo.
Cuando una funcin miembro llama a otra funcin miembro de la misma clase, puede hacerlo directamente, sin usar un
objeto y el operador '.' En este caso, el compilador ya conoce en cul objeto se est operando. Solamente cuando una
funcin miembro es llamada por cdigo que se encuentra fuera de la clase es cuando debe utilizarse el nombre del objeto
y el operador '.' Por la misma razn, una funcin miembro puede referirse directamente a una variable miembro, pero
cdigo fuera de la clase debe referenciarse a la variable a travs de un objeto y el operador '.'
El programa siguiente muestra aqu todas las piezas juntas y detalles perdidos, e ilustra la clase CRender.
54 CAPTULO 13. OBJETOS Y CLASES

13.2.3 Ejemplo

// Programa OPP01.CPP #include <iostream> #include <cstring> using std::cout; using std::endl; // Esto dene la clase
CRender class CRender { public: char buer[256]; void m_Renderizar(const char *cadena); }; /* implementar m_Renderizar()
para la c;*/ void CRender::m_Renderizar(const char *cadena){ strcpy(buer, cadena);//copia la cadena return; } int main
(int argc, char **argv){ // crear 2 objetos CRender CRender render1, render2; render1.m_Renderizar(Inicializando el
objeto render1); render2.m_Renderizar(Inicializando el objeto render2); cout << buer en render1: "; cout << ren-
der1.buer << endl; // tenemos acceso a buer ya que es publico. cout << buer en render2: "; cout << render2.buer
<< endl; return (0); }

Este programa imprime:


buer en render1: Inicializando el objeto render1
buer en render2: Inicializando el objeto render2

13.3 Miembros de una clase ( mtodos y atributos )


En el lenguaje coloquial de la programacin orientada al objeto es comn escuchar trminos tales como: mtodos, atri-
butos, herencia, polimorsmo, etc. En esta seccin nos encargaremos de hablar de los dos primeros.
Mtodos:

En comparacin con la programacin tradicional, un mtodo es lo mismo que una funcin cualquiera, salvo
que como los mtodos se declaran para pertenecer a una clase especca, se dice que todos los mtodos
de dicha clase son miembros de la misma. Por lo dems, la declaracin y denicin de los mtodos es
exactamente igual que declarar y denir cualquier otra funcin.

Atributos:

En comparacin con la programacin tradicional, un atributo es lo mismo que una variable cualquiera, salvo
que como los atributos se declaran para pertenecer a una clase especca, se dice que todos los atributos de
dicha clase son miembros de la misma. Por lo dems, la declaracin de los atributos es exactamente igual
que declarar cualquier otra variable.

Miembros:

A partir de este momento usaremos la palabra miembro para referirnos al hecho de que un mtodo o un
atributo pertenece a tal o cual clase.

Por Ejemplo, en el programa OOP01.CPP ( visto anteriormente ) la Clase CRender posee dos miembros, buer que es
un atributo; y m_Renderizar que es un mtodo.
class CRender { public: char buer[256]; // atributo void m_Renderizar(const char *cadena); // mtodo };

13.4 Visibilidad de los miembros de una clase


Por visibilidad se entiende al acto de acceder a los miembros de una clase. En este sentido, los miembros de una clase
pueden ser: pblicos, privados y protegidos.
13.5. SUBCLASES 55

Un miembro pblico signica que el acceso al mismo puede darse dentro del interior de la clase, dentro de una
subclase, y desde un objeto instanciado de cualquiera de estas. Por ejemplo, los miembros de la clase CRender son
accesibles dentro de la misma y podrn accederse desde cualquier otra clase que se derive de CRender, as como
desde cualquier objeto instanciado de estas.

Un miembro privado signica que el acceso al mismo puede darse solamente dentro del interior de la clase que lo
posee. Normalmente, el programador creador de una clase declara a los atributos de la clase como privados y a los
mtodos como pblicos, esto con la idea de que el usuario de la clase no pueda tener acceso a los atributos sino es
a traves de los mtodos denidos para el caso.

Un miembro protegido se comporta de manera parecida a un miembro privado, salvo que estos son accesibles
dentro de la clase que lo posee y desde las clases derivadas, pero no desde los objetos instanciados a raiz de dichas
clases.

Nota: por defecto, los miembros de una clase son privados.


En la clase Pareja que se ver en seguida, se declaran dos atributos y cuatro mtodos para la manipulacin de dichos
atributos. Observe que los atributos son privados( por defecto ), mientras que los mtodos se declaran pblicos.
class Pareja { // atributos double a, b; public: // mtodos double getA(); double getB(); void setA(double n); void
setB(double n); }; // implementacin de los mtodos de la clase Pareja // double Pareja::getA() { return a; } double
Pareja::getB() { return b; } void Pareja::setA(double n) { a = n; } void Pareja::setB(double n) { b = n; }

13.5 Subclases
Una subclase es una clase que se deriva de otra. La clase que sirve de base suele conocerse como parent (padre), y a la
subclase se le llama child (hija). En C++ cada clase que es creada se convierte en candidata para servir de base de donde
se deriven otras. Por ejemplo, la clase Pareja es candidata para convertirse en la base para las subclases Suma, Resta,
Multiplica, Divide, y otras posibles subclases en donde se utilice un par de valores numricos.
Para poner un ejemplo, pensemos en que deseamos crear la clase Suma, misma que ser utilizada para obtener la suma
de dos nmeros. Puesto que la clase Pareja posee dos atributos nmericos puede ser usada como base para la clase que
estamos proyectando. As, el siguiente ejemplo se constituye en un caso de clases derivadas.
Nota: Observe que la sintaxis para crear una subclase es: class hija : [public | private] padre // Qu signican public y
private aqu? { ... }; Donde padre es la clase base e hija es la subclase.
class Suma : public Pareja { // atributos de Suma double resultado; public: // mtodos de Suma double calcular(); }; //
implementacin de Suma // double Suma::calcular() { return getA() + getB(); }

Probando las clases Pareja y Suma


// programa OOP02.CPP // Este programa pone a prueba el uso de las clase Suma, // misma que es una subclase de la
clase Pareja ( ambas denidas anteriormente). #include <iostream.h> int main() { Suma s; s.setA(80); s.setB(100); cout
<< s.getA() << " + " << s.getB() << " = " << s.calcular() << endl; cin.get(); return 0; }

13.6 Herencia
La herencia es uno de los mecanismos ms tiles de la programacin orientada al objeto, ya que por medio de la misma
se puede llevar a cabo la reutilizacin de cdigo. Es decir, puesto que toda clase denida se convierte en candidata para ser
usada como base de donde se deriven otras, esto da como resultado que las clases derivadas hereden todos los miembros de
la clase base. Por ejemplo, la clase Suma vista en la seccin anterior, hereda todos los miembros de la clase Pareja puesto
56 CAPTULO 13. OBJETOS Y CLASES

que Suma es una extensin de Pareja. En ese sentido, podemos decir que existen dos tipos de herencia, por extensin y
por agregacin o composicin. En el caso de las clases Pareja y Suma, se dice que Suma es una extensin de Pareja.
Vista grcamente, la herencia por extensin se puede representar as:

13.6.1 Herencia por extensin

Al tipo de diagrama mostrado arriba (Herencia por extensin) se le conoce como UML y es utilizado para mostrar de
forma graca la relacin existente entre una clase hija con la clase padre. En el caso del ejemplo, se muestra que la clase
Suma es una extensin de la clase Pareja y, en consecuencia, Suma posee a los miembros { a, b, getA(), getB(), setA(),
setB() } heredados de la clase Pareja. Observe como la clase Suma posee otros dos miembros no heredados, { resultado,
y calcular() }, y es precisamente a este tipo de situacin por lo que se dice que Suma es una extensin de Pareja, ya que
Suma, adems de poseer a todos los miembros de Pareja, se extiende para poseer otros dos miembros.
13.6.2 Agregacion o composicin

La composicin se da en los casos en donde una clase posee un objeto que es una instancia de otra clase. Por ejemplo, la
clase Suma podra escribirse de la siguiente forma:
class Suma { // atributo privado double resultado; public: // mtodo pblico double calcular(); // atributo pblico Pareja
p; }; // implementacin del metodo calcular de la clase Suma. double Suma::calcular() { return p.getA() + p.getB(); }

Luego, si usted presta atencin, notar que el miembro p de la clase Suma es un objeto o instancia de la clase Pareja, en
consecuencia, la clase Suma puede acceder a los miembros de la clase Pareja a travs de la variable p. Tambin se debe
observar que la implementacin del mtodo calcular() es diferente que el mismo de la clase Suma original. Si usted desea
poner a prueba a la nueva clase Suma, compile y ejecute el siguiente programa.
// programa herencia_por_composicion.CPP #include <iostream> using namespace std; class Pareja { // atributos double
a, b; public: // mtodos double getA(); double getB(); void setA(double n); void setB(double n); }; // implementacin de los
mtodos de la clase Pareja double Pareja::getA() { return a; } double Pareja::getB() { return b; } void Pareja::setA(double
n) { a = n; } void Pareja::setB(double n) { b = n; } class Suma { // atributo privado double resultado; public: // mtodo
pblico double calcular(); // atributo pblico Pareja p; }; // implementacin del metodo calcular de la clase Suma. double
Suma::calcular() { return p.getA() + p.getB(); } int main() { Suma s; s.p.setA(80); s.p.setB(100); cout << s.p.getA() <<
" + " << s.p.getB() << " = " << s.calcular() << endl; cin.get(); return 0; }

////SALIDA////
80 + 100 = 180

13.7 Constructores
Un constructor es un mtodo que pertenece a una clase y el cual (en C++) debe tener el mismo nombre de la clase
a la que pertenece. A diferencia de los otros mtodos de la clase, un constructor deber ser del tipo void, es decir, el
mismo no regresar valor alguno. Una clase puede tener ms de un mtodo constructor. Cada clase debe tener al menos
un constructor, tanto as que si el programador creador de una clase no dene un mtodo constructor para la misma, el
sistema, o sea el compilador, crear de manera automtica un constructor nulo.
El objetivo principal del constructor es establecer las condiciones necesarias dentro de la memoria y crear una copia del
objeto mismo dentro de la memoria.
Los constructores suelen usarse para la inicializacin de los atributos de los objetos instanciados. Por ejemplo, con las
instrucciones:
Suma s; s.setA(80); s.setB(100);
13.8. DESTRUCTORES 57

del programa OOP02.CPP, se declara el objeto s de la clase Suma y luego se inicializan los atributos del objeto por
medio de los mtodos setA() y setB(). En este caso, es necesario hacer uso de dichos mtodos debido al hecho de que
no se ha denido un constructor para la clase Suma. Ahora bien, para evitar este tipo de situaciones podemos denir un
mtodo constructor para Suma y que este se encargue de inicializar los atributos. Veamos.
Notas: ya que Suma es una extensin de Pareja, se ha denido el mtodo constructor de Pareja y ste a la vez es invocado
por el constructor de Suma. Otro punto de inters es el hecho de la denicin de un constructor base para Pareja, ya que
de acuerdo con las reglas de programacin en C++ toda clase en donde se dena un constructor parametrizado deber
denir un constructor base; esta regla se aplica en los casos en donde la clase proyectada servir de base para otras.
// programa OOP04.CPP // Ejemplo: clases Pareja y Suma, ambas con constructor #include <iostream> using names-
pace std; //------------------------ class Pareja { // atributos double a, b; public: // constructor de base ( null ) Pareja() {}
// constructror parametrizado Pareja(double x, double y) : a(x), b(y) {} // mtodos double getA(); double getB(); void
setA(double n); void setB(double n); }; // implementacin de los mtodos de la clase Pareja // double Pareja::getA() {
return a; } double Pareja::getB() { return b; } void Pareja::setA(double n) { a = n; } void Pareja::setB(double n) { b
= n; } //------------------------ class Suma : public Pareja { // atributos de Suma double resultado; public: // constructor
Suma(double a, double b) : Pareja(a, b) {} // mtodos de Suma double calcular(); }; // implementacin de Suma // double
Suma::calcular() { return getA() + getB(); } //------------------------ int main() { Suma s(80, 100); cout << s.getA() << "
+ " << s.getB() << " = " << s.calcular() << endl; cin.get(); return 0; }

13.8 Destructores
Un destructor es un mtodo que pertenece a una clase y el cual (en C++) debe tener el mismo nombre de la clase a la
que pertenece. A diferencia de los otros mtodos de la clase, un destructor deber ser del tipo void, es decir, el mismo no
regresar valor alguno. Para diferenciar a un mtodo destructor de un mtodo constructor, al nombre del destructor se le
debe anteponer el caracter ~ (Alt + 126).
El objetivo principal del destructor es el de retirar de la memoria al objeto, o sea, el destructor hace todo lo contrario que
el constructor.
Los destructores suelen usarse para liberar memoria que haya sido solicitada por el objeto a travs de las ordenes malloc(),
new, etc. En tales casos se deber incluir dentro del mtodo destructor la orden free, delete, etc., segn sea el caso.
// clase Pareja con constructor y destructor class Pareja { // atributos double a, b; public: // constructor de base ( nulo )
Pareja() {} // constructor parametrizado Pareja(double x, double y) : a(x), b(y) {} // destructor ~Pareja() {} // mtodos
double getA(); double getB(); void setA(double n); void setB(double n); };
58 CAPTULO 13. OBJETOS Y CLASES

Herencia
Captulo 14

Sobrecarga de Operadores

14.1 Sobrecarga de operadores


La sobrecarga de operadores es uno de los mecanismos que nos permite ampliar las capacidades de los lenguajes de
programacin orientados a objetos. En C++, la declaracin y denicin de una sobrecarga de operador es muy similar
a la declaracin y denicin de una funcin cualquiera. El ejemplo ms sencillo de una sobrecarga de operadores nos lo
da el lenguaje mismo, es decir, en una operacin aritmtica (por ejemplo, una suma ) el compilador determina el tipo de
operacin requerida de acuerdo con el tipo de datos involucrados. Vamos a suponer que se tienen las variables: int A, B;
double X, Y; int R; y las siguientes instrucciones:
R = A + B - C; R = A + X; R = X + Y;

Ahora bien:

en el primer caso el compilador determina que el tipo de operacin requerida es una suma de enteros debido a que
los dos miembros ( A y B ) de la suma son del tipo entero.

en el segundo caso parece ser que las cosas no son tan claras, ya que en este los miembros involucrados en la suma
son de tipos diferentes, sin embargo el compilador determinar el tipo de operacin requerida y depositar en el
resultado ( R )el valor resultante redondeado.

en el tercero y ltimo de los casos el compilador determina que el tipo de operacin requerida es una suma de reales
debido a que los dos miembros ( X e Y ) de la suma son del tipo double. Tambin en este caso el resultado de la
suma se redondea antes de ponerlo en R.

14.2 Mi primer sobrecarga


Para poner un ejemplo prctico de sobrecarga del operador de suma ( + ) vamos a considerar el caso de la clase pareja
mostrada en seguida:
class Pareja { public: double a, b; // constructor parametrizado Pareja(const double a,const double b) { this->a = a; this->b
= b; } }; int main() { Pareja A(50, 75 ); Pareja B(150, 175 ); Pareja C = A + B; return 0; }

Si usted trata de compilar el programa anterior descubrir que el compilador se queja, es decir, el compilador no sabe
que hacer en el caso de la instruccin Pareja C = A + B; aunque es evidente que se trata de la declaracion del objeto C y
que el mismo se espera que sea igual a la suma de los objetos A y B. El error se debe al hecho de que el tipo Pareja es en
realidad una clase y no un tipo primitivo y, en consecuencia, se debe de adiestrar al mismo compilador para que ste sepa

59
60 CAPTULO 14. SOBRECARGA DE OPERADORES

de que manera hara la suma de dos objetos del tipo Pareja. As, para solucionar el error vamos a sobrecargar el operador
+ para trabajar la suma de dos de dichos objetos. Vamos:
// Titulo..: programa sobrecarga01.cpp // Objetivo: demostracin de sobrecarga de operadores // Autor...: Oscar E. Pa-
lacios #include <iostream> using namespace std; class Pareja { public: double a, b; // constructor parametrizado Pare-
ja(const double a,const double b) { this->a = a; this->b = b; } }; // Sobrecarga del operador + Pareja& operator +(const
Pareja &p1,const Pareja &p2) { return *(new Pareja(p1.a + p2.a, p1.b + p2.b) ); } int main() { Pareja A(50, 75 ); Pareja
B(150, 175 ); Pareja C = A + B; cout << A = " << A.a << ',' << A.b << "\n"; cout << B = " << B.a << ',' << B.b <<
"\n"; cout << C = " << C.a << ',' << C.b << "\n"; return 0; }

14.3 Sintaxis general


Tal y como usted lo habr notado, la declaracin de sobrecarga en el programa anterior es lo ms parecido a una funcin,
es decir, la sintaxis general para sobrecargar uno operador cualquiera es:
tipo operator + (lista de parmetros);
en donde,

1. tipo se reere al tipo regresado por el operador

2. operator es una palabra reservada y debe aparecer en toda declaracin de sobrecarga de operadores

3. el simbolo + est en representacin de cualquier operador

4. lista de parmetros indica los argumentos sobre los que operarar la funcin de sobrecarga

Nota: en el caso de que el operador sobrecargado se hace para una clase especca la sintaxis se extiende de la siguiente
manera:
tipo nombre_clase::operator + (lista de parmetros);
Regresando al caso de la clase Pareja, especcamente al operador + de sobrecarga para dicha clase, nos damos cuenta
que dicho operador opera sobre dos objetos de dicha clase y tambin devuelve un resultado de la misma clase. Esto es lo
ms recomendable ya que si usted recuerda el compilador regresa un resultado entero si es que (por ejemplo) se le da una
instruccin como: R = 100 + 23;. En todo caso, el resultado devuelto por los operadores sobrecargados es responsabilidad
del que esta programando

14.4 Sobrecarga permitida de operadores


En C++ no es posible sobrecargar todos los operadores, pero al menos la mayora de ellos s. Los operadores que no se
pueden sobrecargar son: operadores de directivas de procesador #, ## ; Selector directo de componente . ; operador para
valores por defecto de funciones de clase : ; Operador de acceso a mbito :: ; Operador de indireccin de puntero-a-
miembro .* ; Condicional ternario ?; sizeof y typeid.

14.5 Sobrecarga del operador << ( iostream )


Normalmente cuando se escribe una clase y se desea que el stream estndar de salida ( cout ) pueda mostrar una re-
presentacin de su valor se debe de sobrecargar el operador <<. Para mostrar un ejemplo retomemos el programa sobre-
carga01.cpp ( visto arriba ), en el mismo se imprimen por medio de cout los miembros ( a y b ) de los objetos A, B y
C.
14.6. SOBRECARGA DEL OPERADOR >> ( ISTREAM ) 61

cout << A = " << A.a << ',' << A.b << "\n"; cout << B = " << B.a << ',' << B.b << "\n"; cout << C = " << C.a << ','
<< C.b << "\n";
Usted puede ver cmo para cada uno de los miembros de los objetos se debe de usar el operador de direccin ( . ), pues
bien, nuestro objetivo es lograr que dado un objeto de la clase Pareja ste pueda ser desplegado por cout y para ello
haremos la sobrecarga de operador << con el n de que pueda operar con objetos de la clase mencionada. Veamos:
// Titulo..: programa sobrecarga03.cpp // Objetivo: demostracin de sobrecarga de operadores // Autor...: Oscar E. Pa-
lacios #include <iostream> using namespace std; class Pareja { public: double a, b; // constructor parametrizado Pare-
ja(const double a,const double b) { this->a = a; this->b = b; } }; // Sobrecarga del operador + Pareja& operator +(const
Pareja &p1,const Pareja &p2) { return *(new Pareja(p1.a + p2.a, p1.b + p2.b) ); } // Sobrecarga del operador << para
la clase Pareja ostream& operator << (ostream &o,const Pareja &p) { o << "(" << p.a << ", " << p.b << ")"; return o; }
int main() { Pareja A(50, 75 ); Pareja B(150, 175 ); Pareja C = A + B; cout << A = " << A << "\n"; cout << B = " <<
B << "\n"; cout << C = " << C << "\n"; return 0; }

14.6 Sobrecarga del operador >> ( istream )


As como el operador << debe ser sobrecargado, lo mismo es cierto para el operador >> para poder ser usado con el
stream estndar de entrada ( cin ). Retomaremos nuevamente a la clase Pareja para dar un ejemplo.
// Titulo..: programa sobrecarga03.cpp // Objetivo: demostracin de sobrecarga de operadores // Autor...: Keylord Mal-
donado Maciel y Arlette Any Conchas Vergara #include <iostream> using namespace std; // Arcad. inserte aqu la clase
Pareja y los operadores sobrecargados vistos // mientras en el programa anterior. // entonces Se deber agregar un cons-
tructor en // la clase // Pareja como // el siguiente // luego de insertar todo el codigo bien // a continuacin como se
muestra /*Pareja() { this->a=0; this->b=0; }*/ // Sobrecarga del operador >> para la clase Pareja istream& operator >>
(istream &i, Pareja &p) { cout << Introducir valores para ( a, b) : "; i >> p.a >> p.b; i.ignore(); return i; } int main() {
Pareja A(50, 75 ); Pareja B; cin >> B; Pareja C = A + B; cout << A = " << A << "\n"; cout << B = " << B << "\n";
cout << C = " << C << "\n"; return 0; }

14.7 Operadores amigos ( friend )


Un operador amigo, al igual que una funcin amiga, es aquel que an cuando no es miembro de una clase tiene todos los
privilegios de acceso a los miembros de dicha clase.
En la sobrecarga de los operadores +, << y >> para la clase Pareja de los programas anteriores, se puede notar que dichos
operadores no son parte de la clase Pareja, sino que ms bien stos operan sobre objetos de dicha clase y es a travs de
dichos objetos que pueden manipular a los miembros de la clase. Ahora bien, no hemos tenido ningn problema debido
a que todos los miembros de Pareja han sido declarados como pblicos (public:), pero qu sucede si un operador o una
funcin que no sea parte de la clase trata de acceder a los miembros privados o protegidos de sta? En estos casos, el
compilador reportara el error indicando que tal o cual miembro es privado dentro de cierto contexto.
Si queremos que un operador o funcin que no es miembro de una clase pueda acceder a los miembros pblicos, privados
o protegidos deberemos declarar a dicho operador o funcin como amigo (friend) dentro de la clase especca. Para
mostrar un ejemplo modicaremos la clase Pareja, donde sus atributos sern privados.
// Titulo..: programa sobrecarga04.cpp // Objetivo: demostracin de sobrecarga de operadores // Autor...: Oscar E. Pa-
lacios #include <iostream> using namespace std; class Pareja { private: double a, b; public: // constructor parametrizado
Pareja(const double a,const double b) { this->a = a; this->b = b; } friend Pareja& operator +(const Pareja &p1,const
Pareja &p2); friend ostream& operator << (ostream &o,const Pareja &p); }; // Sobrecarga del operador + para la clase
Pareja Pareja& operator +(const Pareja &p1,const Pareja &p2) { return *(new Pareja(p1.a + p2.a, p1.b + p2.b) ); } //
Sobrecarga del operador << para la clase Pareja ostream& operator << (ostream &o,const Pareja &p) { o << "(" << p.a
<< ", " << p.b << ")"; return o; } int main() { Pareja A(50, 75 ); Pareja B(150, 175 ); Pareja C = A + B; cout << A = "
62 CAPTULO 14. SOBRECARGA DE OPERADORES

<< A << "\n"; cout << B = " << B << "\n"; cout << C = " << C << "\n"; return 0; }

14.8 Sobrecarga de operadores dentro de una clase


Tal y como hemos dicho antes, los operadores que hemos sobrecargado para la clase Pareja ( de los ejemplos anteriores
) no son parte de la clase, pero en la mayora de las veces se ver que los operadores para una clase especca se deben
sobrecargar dentro de la misma clase, es decir, dichos operadores sern miembros de la clase. Antes de sobrecargar
cualquier operador para una clase se deben tener en cuenta los siguientes factores:

1. Los operadores binarios se declaran con un solo parmetro, ya que el primer parmetro es pasado por el programa
como this, es decir, un puntero al mismo objeto.

2. Los operadores unarios se declaran sin paramtros, ya que el nico parmetro es pasado por el programa como
this.

Nota: Los operadores binarios son aquellos que poseen dos partes ( izquierda y derecha), por ejemplo, una operacin de
suma requiere dos operandos ( o1 + o2 ). Los operadores unarios son aquellos que poseen solo una parte, por ejemplo,
una operacin de incremento ( o1 ++ ).
Con el propsito de ver un ejemplo prctico vamos a retomar una vez ms la tan famosa clase Pareja, salvo que en esta
ocasin vamos a sobrecargar dentro de la misma a los operadores binarios: + (suma), - (resta), * (multiplicacin) y /
(divisin); el operador de asignacin (=); el operador de incremento (++) y el operador de comparacin (==).
// Programa: sobrecarga05.cpp // Objetivo: mostrar sobrecarga de operadores // Autor...: Oscar Edmundo Palacios. #in-
clude <iostream> using namespace std; class Pareja { private: int a, b; public: // constructor base Pareja() : a(0), b(0)
{} // constructor parametrizado Pareja(const int a,const int b) { this->a = a; this->b = b; } // constructor de copia Pare-
ja(const Pareja&); // operadores miembros Pareja& operator + (const Pareja &p); Pareja& operator - (const Pareja &p);
Pareja& operator * (const Pareja &p); Pareja& operator / (const Pareja &p); Pareja& operator = (const Pareja &p); Pa-
reja& operator ++(); bool operator ==(const Pareja &p) const; // operadores no miembros friend ostream& operator <<
(ostream &o,const Pareja &p); friend istream& operator >> (istream &o, Pareja &p); }; // implementacion de los opera-
dores para la clase Pareja //.................................... Pareja::Pareja(const Pareja &p) { *this=p; } //....................................
Pareja& Pareja::operator + (const Pareja &p) { this->a += p.a; this->b += p.b; return *this; } //....................................
Pareja& Pareja::operator - (const Pareja &p) { this->a -= p.a; this->b -= p.b; return *this; } //....................................
Pareja& Pareja::operator * (const Pareja &p) { this->a *= p.a; this->b *= p.b; return *this; } //....................................
Pareja& Pareja::operator / (const Pareja &p) { if (p.a != 0) this->a /= p.a; if (p.b != 0) this->b /= p.b; return *this;
} //.................................... Pareja& Pareja::operator = (const Pareja &p) { if(this!=&p){ //Comprueba que no se es-
t intentanod igualar un objeto a s mismo if (p.a != 0) this->a = p.a; if (p.b != 0) this->b = p.b; } return *this; }
//.................................... Pareja& Pareja::operator ++ () { this->a ++; this->b ++; return *this; } //....................................
bool Pareja::operator == (const Pareja &p) const { return this->a == p.a && this->b == p.b; } // implemetacin de ope-
radores no miembros ostream& operator << (ostream &o,const Pareja &p) { o << "(" << p.a << ", " << p.b << ")";
return o; } istream& operator >> (istream &i, Pareja &p) { cout << Introducir valores para ( a, b) :"; i >> p.a >> p.b;
i.ignore(); return i; } // prueba para la clase Pareja int main() { Pareja A(50, 75); Pareja B(100, 15); Pareja C; cout <<
A = " << A << "\n"; cout << B = " << B << "\n"; cout << "......................... << endl; C = A * B; cout << A = " << A
<< "\n"; cout << C = " << C << endl; cout << "......................... << endl; ++C; cout << C = " << C << endl; cout <<
A == B " << ( (A==B) ? Si": No ); cin.get(); return 0; }
Captulo 15

Herencia

Editores:

15.1 Introduccin
La 'herencia' es una de las piedras angulares de la POO ya que sta permite la creacin de clasicaciones jerrquicas. Con
la herencia, es posible crear una clase general que dena tratos comunes a una serie de elementos relacionados. Esta clase
podra luego ser heredada por otras clases ms especcas, cada una agregando solo aquellas cosas que son nicas para la
clase 'heredera'.
En terminologa estndar C++, una clase que es heredada es referida como la clase 'base'. La clase que efecta la herencia
es llamada la clase 'derivada'. Adems, una clase derivada puede ser usada como una clase base por otra clase derivada.
De esta manera, una jerarqua multicapa de clases puede ser lograda.

15.2 INTRODUCIENDO LA HERENCIA.


C++ soporta herencia permitiendo a una clase incorporar otra clase dentro de su declaracin. Antes de discutir los detalles
y la teora, se procede a comenzar por un ejemplo de herencia. La siguiente clase, llamada 'VehiculoRodante', dene muy
ampliamente a vehculos que viajan por la carretera. Este almacena el numero de ruedas que un vehculo tiene y el numero
de pasajeros que puede llevar.
Un regla sencilla para recordar esto es: Una clase derivada hereda de una clase base
// Denicin de una clase base para vehiculos class VehiculoRodante { public: // CICLO DE VIDA /* En este lugar se
sitan los constructores, los destructores, y/o los constructores copia */ // OPERADORES /* Aqu van los mtodos que
se apliquen sobre operadores */ // OPERACIONES /* Aqu van los mtodos de esta clase que no sean ni de acceso ni
de peticin o tratamiento */ // ACCESO /* Aqu van las funciones de acceso a los datos miembro o variables propias del
objeto */ /* * Funcin 'set_ruedas * Recibe: num como int * Devuelve: void * Asigna al dato miembro 'mRuedas el
valor 'num' */ void set_ruedas(int num) { this->mRuedas = num; } /* * Funcin 'get_ruedas * Recibe: void * Devuelve:
int * Devuelve el valor que hay dentro del dato miembro 'mRuedas */ int get_ruedas(void) { return this->mRuedas; } /*
* Funcin 'set_pasajeros * Recibe: num como int * Devuelve: void * Asigna al dato miembro 'mPasajeros el valor 'num'
*/ void set_pasajeros(int num) { this->mPasajeros = num; } /* * Funcin 'get_pasajeros * Recibe: void * Devuelve: int
* Devuelve el valor que hay dentro del dato miembro 'mPasajeros */ int get_pasajeros(void) { return this->mPasajeros;
} // PETICIONES/TRATAMIENTOS /* Aqu van las funciones del tipo Is, que generalmente devuelven true/false */
private: /* Generalmente en 'private' se sitan los datos miembros */ int mRuedas; int mPasajeros; };

63
64 CAPTULO 15. HERENCIA

Se puede usar esta amplia denicin de un vehculo rodante para ayudar a denir tipos especcos de vehculos. Por
ejemplo, el fragmento de cdigo mostrado aqui podra usarse para ser heredado por una clase derivada llamada 'Camion'.
// Denicin de una clase 'Camion' derivada de la clase base 'VehiculoRodante'. class Camion : public VehiculoRodante {
public: // CICLO DE VIDA /* En este lugar se sitan los constructores, los destructores, y/o los constructores copia */ //
OPERADORES /* Aqu van los mtodos que se apliquen sobre operadores */ // OPERACIONES /* Aqu van los mto-
dos de esta clase que no sean ni de acceso ni de peticin o tratamiento */ // ACCESO /* Aqu van las funciones de acceso
a los datos miembro o variables propias del objeto */ /* * Funcin 'set_carga' * Recibe: size como int * Devuelve: void *
Asigna al dato miembro 'mCarga' el valor 'size' */ void set_carga(int size) { this->mCarga = size; } /* * Funcin 'get_carga'
* Recibe: void * Devuelve: int * Devuelve el valor que hay dentro del dato miembro 'mCarga' */ int get_carga(void) {
return this->mCarga; } /* * Funcin 'Mostrar' * Recibe: void * Devuelve: void * Muestra por pantalla las ruedas, pasa-
jeros y la capacidad de carga del objeto 'Camion' */ void Mostrar(void); // PETICIONES/TRATAMIENTOS /* Aqu
van las funciones del tipo Is, que generalmente devuelven true/false */ private: /* Generalmente en 'private' se sitan
los datos miembros */ int mCarga; };

Como 'Camion' hereda de 'VehiculoRodante', 'Camion' incluye todo de 'vehiculo_rodante'. Entonces agrega 'carga' a la
misma, en conjunto con las funciones miembros que trabajen sobre este dato.
Ntese como 'VehiculoRodante' es heredado. La forma general para la herencia se muestra aqu:
class ClaseDerivada : acceso ClaseBase { //cuerpo de la nueva clase }

Aqu, 'acceso' es opcional. Sin embargo, si se encuentra presente, debe ser 'public', 'private', o 'protected'. Se aprender
ms sobre estas opciones ms tarde, por ahora para todas las clases heredadas se usar el acceso 'public'. Usar public
signica que todos los miembros pblicos de la clase base sern tambin miembros pblicos de la clase derivada. Por
tanto, en el ejemplo anterior, miembros de la clase 'Camion' tienen acceso a los miembros pblicos de 'VehiculoRodante',
justo como si ellos hubieran sido declarados dentro de 'Camion'. Sin embargo, 'camion' no tiene acceso a los miembros
privados de 'VehiculoRodante'. Por ejemplo, 'Camion' no tiene acceso a ruedas.
He aqu un programa que usa herencia para crear dos subclases de 'VehiculoRodante'. Una es 'Camion' y la otra es
'Automovil'.
// Programa que demuestra la herencia. // INCLUDES DE SISTEMA // #include <iostream> // INCLUDES DEL PRO-
YECTO // // INCLUDES LOCALES // // DECLARACIONES // // Denicin de una clase base para vehiculos class
VehiculoRodante { public: // CICLO DE VIDA /* En este lugar se sitan los constructores, los destructores, y/o los cons-
tructores copia */ // OPERADORES /* Aqu van los mtodos que se apliquen sobre operadores */ // OPERACIONES
/* Aqu van los mtodos de esta clase que no sean ni de acceso ni de peticin o tratamiento */ /* * Funcin 'set_ruedas *
Recibe: num como int * Devuelve: void * Asigna al dato miembro 'mRuedas el valor 'num' */ void set_ruedas(int num) {
this->mRuedas = num; } /* * Funcin 'get_ruedas * Recibe: void * Devuelve: int * Devuelve el valor que hay dentro del
dato miembro 'mRuedas */ int get_ruedas(void) { return this->mRuedas; } /* * Funcin 'set_pasajeros * Recibe: num
como int * Devuelve: void * Asigna al dato miembro 'mPasajeros el valor 'num' */ void set_pasajeros(int num) { this-
>mPasajeros = num; } /* * Funcin 'get_pasajeros * Recibe: void * Devuelve: int * Devuelve el valor que hay dentro del
dato miembro 'mPasajeros */ int get_pasajeros(void) { return this->mPasajeros; } // PETICIONES/TRATAMIENTOS
/* Aqu van las funciones del tipo Is, que generalmente devuelven true/false */ private: /* Generalmente en 'private' se
sitan los datos miembros */ int mRuedas; int mPasajeros; }; // Denicin de una clase 'Camion' derivada de la clase
base 'VehiculoRodante'. class Camion : public VehiculoRodante { public: // CICLO DE VIDA /* En este lugar se si-
tan los constructores, los destructores, y/o los constructores copia */ // OPERADORES /* Aqu van los mtodos que
se apliquen sobre operadores */ // OPERACIONES /* Aqu van los mtodos de esta clase que no sean ni de acceso ni
de peticin o tratamiento */ // ACCESO /* Aqu van las funciones de acceso a los datos miembro o variables propias
del objeto */ /* * Funcin 'set_carga' * Recibe: size como int * Devuelve: void * Asigna al dato miembro 'mCarga' el
valor 'size' */ void set_carga(int size) { this->mCarga = size; } /* * Funcin 'get_carga' * Recibe: void * Devuelve: int *
Devuelve el valor que hay dentro del dato miembro 'mCarga' */ int get_carga(void) { return this->mCarga; } /* * Fun-
cin 'Mostrar' * Recibe: void * Devuelve: void * Muestra por pantalla las ruedas, pasajeros y la capacidad de carga del
objeto 'Camion' */ void Mostrar(void); // PETICIONES/TRATAMIENTOS /* Aqu van las funciones del tipo Is, que
generalmente devuelven true/false */ private: /* Generalmente en 'private' se sitan los datos miembros */ int mCarga; };
void Camion::Mostrar(void) { std::cout << ruedas: " << this->get_ruedas() << std::endl; std::cout << pasajeros: " <<
15.3. CONTROL DE ACCESO DE LA CLASE BASE. 65

this->get_pasajeros() << std::endl; std::cout << Capacidad de carga en pies cbicos: " << this->get_carga() << std::endl;
} /* * Este enumerador sirve para denir diferentes tipos de automvil */ enum tipo {deportivo, berlina, turismo}; // De-
nicin de una clase 'Automovil' derivada de la clase base 'VehiculoRodante'. class Automovil : public VehiculoRodante
{ public: // CICLO DE VIDA /* En este lugar se sitan los constructores, los destructores, y/o los constructores copia
*/ // OPERADORES /* Aqu van los mtodos que se apliquen sobre operadores */ // OPERACIONES /* Aqu van los
mtodos de esta clase que no sean ni de acceso ni de peticin o tratamiento */ // ACCESO /* Aqu van las funciones
de acceso a los datos miembro o variables propias del objeto */ /* * Funcin 'set_tipo' * Recibe: t como tipo * Devuel-
ve: void * Asigna al dato miembro 'mTipoDeAutomovil' el valor 't' */ void set_tipo(tipo t) { this->mTipoDeAutomovil
= t; } /* * Funcin 'get_tipo' * Recibe: void * Devuelve: tipo * Devuelve el valor que hay dentro del dato miembro
'mTipoDeAutomovil' */ enum tipo get_tipo(void) { return this->mTipoDeAutomovil; }; /* * Funcin 'Mostrar' * Re-
cibe: void * Devuelve: void * Muestra por pantalla las ruedas, pasajeros y la capacidad de carga del objeto 'Camion'
*/ void Mostrar(void); private: enum tipo mTipoDeAutomovil; }; void Automovil::Mostrar(void) { std::cout << rue-
das: " << this->get_ruedas() << std::endl; std::cout << pasajeros: " << this->get_pasajeros() << std::endl; std::cout <<
tipo: "; switch(this->get_tipo()) { case deportivo: std::cout << deportivo"; break; case berlina: std::cout << berlina";
break; case turismo: std::cout << turismo"; } std::cout << std::endl; } /* * Funcin 'main' * Recibe: void * Devuel-
ve: int * El cdigo es una posible implementacin para claricar el uso efectivo de clases bases y clases derivadas. *
Muestra por pantalla valores de la clase base y de las clases derivadas. */ int main(void) { Camion Camion1; Camion
Camion2; Automovil Automovil1; Camion1.set_ruedas(18); Camion1.set_pasajeros(2); Camion1.set_carga(3200); Ca-
mion2.set_ruedas(6); Camion2.set_pasajeros(3); Camion2.set_carga(1200); Camion1.Mostrar(); std::cout << std::endl;
Camion2.Mostrar(); std::cout << std::endl; Automovil1.set_ruedas(4); Automovil1.set_pasajeros(6); Automovil1.set_tipo(tipo::deportivo);
Automovil1.Mostrar(); return 0; }

La salida de este programa se muestra a continuacin:


ruedas: 18
pasajeros: 2
Capacidad de carga en pies cbicos: 3200

ruedas: 6
pasajeros: 3
Capacidad de carga en pies cbicos: 1200

ruedas: 4
pasajeros: 6
tipo: deportivo

Como este programa muestra, la mayor ventaja de la herencia es que permite crear una clase base que puede ser in-
corporada en clases ms especcas. De esta manera, cada clase derivada puede ser precisamente ajustada a las propias
necesidades y aun siendo parte de la clasicacin general.
Por otra parte, ntese que ambos, 'Camion' y 'Automovil', incluyen una funcin miembro llamada 'Mostrar()', la cual
muestra informacin sobre cada objeto. Esto ilustra un aspecto del polimorsmo. Como cada funcin 'Mostrar()' esta
enlazada con su propia clase, el compilador puede fcilmente indicar cul llamar para cualquier objeto dado.
Ahora que se ha visto los procedimientos bsicos por los cules una clase hereda de otra, se examinar la herencia en
detalle.

15.3 CONTROL DE ACCESO DE LA CLASE BASE.


Cuando una clase hereda de otra, los miembros de la clase base se convierten en miembros de la clase derivada. El estado
de acceso de los miembros de la clase base dentro de la clase derivada es determinado por el especicador de acceso
usado para heredar la clase base. El especicador de acceso de la clase base debe ser 'public', 'private' o 'protected'. Si
el especicador de acceso no es usado, entonces se usara private por defecto si la clase derivada es una clase. Si la clase
66 CAPTULO 15. HERENCIA

derivada es una 'struct' entonces 'public' es el acceso por defecto por la ausencia de un expecicador de acceso explcito.
Examinemos las ramicaciones de usar accesos public o private. ( El especicador 'protected' se describe en la prxima
seccin.)
Cuando una clase base es heredada como 'public', todos los miembros pblicos de la clase base se convierten en miembros
de la clase derivada. En todos los casos, los elementos privados de la clase base se mantienen de esa forma para esta clase,
y no son accesibles por miembros de la clase derivada. Por ejemplo, en el siguiente programa, los miembros pblicos de
'base' se convierten en miembros publics de 'derivada'. Encima, son accesibles por otras partes del programa.
#include <iostream> using namespace std; class base { int i, j; public: void set(int a, int b) { i = a; j = b; } void mostrar()
{ cout << i << " " << j << "\n"; } }; class derivada : public base { int k; public: derivada(int x) { k = x; } void mostrar_k()
{ cout << k << "\n"; } }; int main() { derivada obj(3); obj.set(1, 2); // accesar a miembro de base obj.mostrar(); // accesar
a miembro de base obj.mostrar_k(); // usa miembro de la clase derivada return 0; }

Como set() y mostrar() son heredadas como 'public', ellas pueden ser llamadas en un objeto del tipo 'derivada' desde
main(). Como i y j son especicadas como 'private', ellas se mantienen privadas a base.
El opuesto de herencia publica es herencia privada. Cuando la clase base es heredad como privada, entonces todos los
miembros pblicos de la clase base se convierten en miembros privados de la clase derivada. Por ejemplo, el programa
mostrado a continuacin no compilara, porque set() y mostrar() son ahora miembros privados de 'derivada', y por ende
no pueden ser llamados desde main().
TIP:"Cuando una clase base es heredada como 'private' sus miembros pblicos se convierten en miembros privados de
la clase derivada.
// Este programa no compilara. #include <iostream> using namespace std; class base { int i, j; public: void set(int a, int
b) { i = a; j = b; } void mostrar() { cout << i << " " << j << "\n"; } }; // Miembros publicos de 'base' son privados en
'derivada' class derivada : private base { int k; public: derivada(int x) { k = x; } void mostrar_k() { cout << k << "\n";
} }; int main() { derivada obj(3); obj.set(1, 2); // Error, no se puede acceder a set() obj.mostrar(); // Error, no se puede
acceder a mostrar() return 0; }

La clave a recordar es que cuando una clase base es heredada como 'private', los miembros pblicos de la clase base se
convierten en miebros privados de la clase derivada. Esto signica que aun ellos son accesibles por miembros de la clase
derivada, pero no pueden ser accedidos por otras partes de su programa.

15.3.1 USANDO MIEMBROS PROTEGIDOS.

En adicin a public y private, un miembro de la clase puede ser declarado como protegido. Adems, una clase base
puede ser heredada como protegida. Ambas de estas acciones son cumplidas por el especicador de acceso 'protected'.
La palabra clave 'protected' esta incluida en C++ para proveer gran exibilidad para el mecanismo de herencia.
Cuando un miembro de una clase es declarado como 'protected', ese miembro, no es accesible a otros elementos no-
miembros de la clase en el programa. Con una importante excepcin, el acceso a un miembro protegido es lo mismo
que el acceso a un miembro privado, este puede ser accedido solo por otros miembros de la clase de la cual es parte. La
nica excepcin a esta regla es cuando un miembro protegido es heredado. En este caso, un miembro protegido diere
sustancialmente de uno privado.
Como debe conocer, un miembro privado de una clase base no es accesible por cualquier otra parte de su programa,
incluyendo cualquier clase derivada. Sin embargo, los miembros protegidos se comportan diferente. Cuando una clase
base es heredada como publica, los miembros protegidos en la clase base se convierten en miembros protegidos de la
clase derivada, y 'son' accesibles a la clase derivada. Adems, usando 'protected' usted puede crear miebros de clases que
son privados para su clase, pero que aun asi pueden ser heredados y accedidos por una clase derivada.
Considere este programa de ejemplo:
#include <iostream> using namespace std; class base { protected: int i, j; // privados a base, pero accesibles a derivada.
public: void set(int a, int b) { i = a; j = b; } void mostrar() { cout << i << " " << j << "\n"; } }; class derivada : public base
{ int k; public: // derivada puede accesar en base a 'j' e 'i' void setk() { k = i * j; } void mostrark() { cout << k << "\n";
15.3. CONTROL DE ACCESO DE LA CLASE BASE. 67

} }; int main() { derivada obj; obj.set(2, 3); // OK, conocido por derivada. obj.mostrar(); // OK, conocido por derivada.
obj.setk(); obj.mostrark(); return 0; }

Aqu, porque 'base' es heredada por 'derivada' como pblica, y porque j e i son declaradas como protegidas, la funcin
setk() en 'derivada' puede acceder a ellas. Si j e i hubieran sido declaradas como privadas por 'base', entonces 'derivada'
no tuviera acceso a ellas, y el programa no compilara.
RECUERDE: El especicador 'protected' le permite crear un miembro de la clase que es accesible desde la jerarqua de
la clase, pero de otra manera es privado.
Cuando una clase derivada es usada como clase base para otra clase derivada, entonces cualquier miembro protegido de
la clase base inicial que es heredado ( como public ) por la primera clase derivada puede ser heredado nuevamente, como
miembro protegido, por una segunda clase derivada. Por ejemplo, el siguiente programa es correcto, y derivada2 tiene,
de hecho, acceso a 'j' e 'i':
#include <iostream> using namespace std; class base { protected: int i, j; public: void set(int a, int b) { i = a; j = b; } void
mostrar() { cout << i << " " << j << "\n"; } }; // j e i se heredan como 'protected' class derivada1 : public base { int k;
public: void setk() { k = i*j; } // legal void mostrark() { cout << k << "\n"; } }; // j e i se heredan indirectamente a travs
de derivada1 class derivada2 : public derivada1 { int m; public: void setm() { m = i-j; } // legal void mostrarm() { cout <<
m << "\n"; } }; int main() { derivada1 obj1; derivada2 obj2; obj1.set(2, 3); obj1.mostrar(); obj1.setk(); obj1.mostrark();
obj2.set(3, 4); obj2.mostrar(); obj2.setk(); obj2.setm(); obj2.mostrark(); obj2.mostrarm(); return 0; }

Cuando una clase base es heredada como 'private', miembros protegidos de la clase base se convierten en miembros
privados de la clase derivada. Adems, en el ejemplo anterior, si 'base' fuera heredada como 'private', entonces todos los
miembros de 'base' se hubieran vuelto miembros privados de derivada1, signicando que ellos no podran estar accesibles
a derivada2. ( Sin embargo, j e i podran aun ser accesibles a derivada1.) Esta situacin es ilustrada por el siguiente
programa, el cual es un error ( y no compilara ). Los comentarios describen cada error.
// Este programa no compilara. #include <iostream> using namespace std; class base { protected: int i, j; public: void
set(int a, int b) { i = a; j = b; } void mostrar() { cout << i << " " << j << "\n"; } }; // Ahora todos los elementos de
base son privados en derivada1. class derivada1 : private base { int k; public: // Esto es legal porque j e i son privadas
a derivada1 void setk() { k = i*j; } // OK void mostrark() { cout << k << "\n"; } }; // Acceso a j, i, set() y mostrar no
heredado class derivada2 : public derivada1 { int m; public: // Ilegal porque j e i son privadas a derivada1 setm() { m =
j-i; } // error void mostrarm() { cout << m << "\n"; } }; int main() { derivada1 obj1; derivada2 obj2; obj1.set(1, 2); //
Error, no se puede usar set() obj1.mostrar(); // Error, no se puede usar show() obj2.set(3, 4); // Error, no se puede usar
set() obj2.mostrar(); // Error, no se puede usar show() return 0; }

Incluso aunque 'base' es heredada como privada por derivada1, derivada2 aun tiene acceso a los elementos publicos y
protegidos de 'base'. Sin embargo, no puede sobrepasar este privilegio a todo lo largo. Esta es la razon de las partes
'protected' del lenguaje C++. Este provee un medio de proteger ciertos miembros de ser modicados por funciones no-
miembros, pero les permite ser heredadas.
El especicador 'protected' puede ser usado con estructuras. Sin embargo no puede ser usado con una 'union' porque la
union no puede heredar otra clase o ser heredad. ( Algunos compiladores aceptaran su uso en una declaracion en una
union, pero como las uniones no pueden participar en la herencia, 'protected' es lo mismo que 'private' en este contexto.)
El especicador 'protected' puede ocurrir en cualquier lugar en la declaracion de una clase, aunque tipicamente ocurre
despues de ( por defecto ) que los miembros privados son declarados, y antes de los miebros publicos. Ademas, la mayor
forma completa de la declaraciond de una clase es
class nombre-clase { miembros privados protected: miembros protegidos public: miembros publicos };
Por supuesto, la categoria protected es opcional.
68 CAPTULO 15. HERENCIA

15.3.2 USANDO PROTECTED PARA LA HERENCIA DE UNA CLASE BASE.

Como adicin a especicar el estado protegido para los miembros de una clase, la palabra clave 'protected' tambien puede
ser usada para heredar una clase base. Cuando una clase base es heredada como protected, todos los miembros publicos
y protegidos de la clase base se convierten en miembros protegidos de la clase derivada. Aqui hay un ejemplo:
// Demuestra la herencia de una clase base protegida #include <iostream> using namespace std; class base { int i; protec-
ted: int j; public: int k; void seti(int a) { i = a; } int geti() { return i; } }; // heredar 'base' como protected. class derivada
: protected base { public: void setj(int a) { j = a; }; // j es protected aqui. void setk(int a) { k = a; }; // k es tambien
protected. int getj() { return j; } int getk() { return k; } }; int main() { derivada obj; /* La proxima linea es ilegal porque
seti() es un miembro protegido de derivada, lo cual lo hace inaccesible fuera de derivada. */ // obj.seti(10); // cout <<
obj.geti(); // ilegal -- geti() es protected. // obj.k = 10; // tambien ilegal porque k es protected. // estas declaraciones son
correctas obj.setk(10); cout << obj.getk() << " "; obj.setj(12); cout << obj.getj() << " "; return 0; }

Como puede ver leyendo los comentarios en este programa, k,j, seti() y geti() en 'base' se convierten miembros 'protected'
en 'derivada'. Esto signica que ellos no pueden ser accesados por codigo fuera de 'derivada'. Ademas, dentro de main(),
referencias a estos miembros a traves de obj son ilegales.

15.3.3 REVISANDO public, protected, y private

Ya que los permisos de acceso que son denidos por 'public', 'protected', y 'private' son fundamentales para la programa-
cin en C++, volvamos a revisar sus signicados.
Cuando una clase miembro es declarada como 'public', esta puede ser accedida por cualquier otra parte del programa.
Cuando un miembro es declarado como 'private', este puede ser accedido solo por miembros de su clase. Adems, clases
derivadas no tienen acceso a miembros privados de la clase base. Cuando un miembro es declarado como 'protected', este
puede ser accedido solo por miembros de su clase, o por clases derivadas. Adems, protected permite que un miembro
sea heredado, pero que se mantenga privado en la jerarqua de la clase.
Cuando una clase base es heredada por el uso de 'public', sus miembros publicos se convierten en miebros publicos de la
clase derivada, y sus miembros protected se convierten en miembros protected de la clase derivada.
Cuando una clase base es heredada por el uso de 'protected', sus miembros publicos y protegidos se convierten en miem-
bros protected de la clase derivada.
Cuando una clase base es heredada por el uso de 'private', sus miembros publicos y protegidos se convierten en miebros
private de la clase derivada.
En todos los casos, los miembros privados de la clase base se mantienen privados a la clase base, y no son heredados.

15.3.4 HEREDANDO MULTIPLES CLASES BASE

Es posible para una clase derivada heredar dos o mas clases base. Por ejemplo, en este corto programa, derivada hereda
de ambas clases base1 y base2:
// Un ejemplo de multiples clases base #include <iostream> using namespace std; class base1 { protected: int x; int m;
public: void showx() { cout << x << "\n"; } }; class base2 { protected: int y; public: void showy() { cout << y << "\n"; }
}; // Heredar multiples clases base. class derivada : public base1, public base2 { public: void set(int i, int j) { x= i; y = j;
}; }; int main() { derivada obj; obj.set(10, 20); // proveida por derivada. obj.showx(); // desde base1 obj.showy(); // desde
base2 return 0; }

Como este ejemplo ilustra, para causar que mas de una clase base sea heredad, debe usarse una lista separada por comas.
Ademas, asegurese de usar un especicador de acceso para cada clase heredada.
15.3. CONTROL DE ACCESO DE LA CLASE BASE. 69

15.3.5 CONSTRUCTORES, DESTRUCTORES, Y HERENCIA

Existen dos importantes preguntas relativas a los constructores y destructores cuando la herencia se encuentra implicada.
Primera, dnde son llamados los constructores y destructores de las clases base y clases derivadas? Segunda, como se
pueden pasar los parmetros al constructor de una clase base? Esta seccin responde estas preguntas.
Examine este corto programa:
#include <iostream> using namespace std; class base { public: base() { cout << Construyendo base\n"; } ~base() { cout
<< Destruyendo base\n"; } }; class derivada : public base { public: derivada() { cout << Construyendo derivada\n";
} ~derivada() { cout << Destruyendo derivada\n"; } }; int main() { derivada obj; // no hacer nada mas que construir y
destruir obj return 0; }

Como el comentario en main() indica, este programa simplemente construye y destruye un objeto llamado obj, el cual es
de la clase 'derivada'. Cuando se ejecuta, este programa muestra:
Construyendo base Construyendo derivada Destruyendo derivada Destruyendo base

Como puede ver, el constructor de 'base' es ejecutado, seguido por el constructor en 'derivada'. A continuacin ( ya que
obj es inmediatamente destruido en este programa), el destructor en 'derivada' es llamado, seguido por el de 'base'.
El resultado del experimento anterior puede ser generalizado como lo siguiente: Cuando un objeto de una clase derivada
es creado, el constructor de la clase base es llamado primero, seguido por el constructor de la clase derivada. Cuando un
objeto derivada es destruido, su destructor es llamado primero, seguido por el destructor de la clase base. Vindolo de otra
manera, los constructores son ejecutados en el orden de su derivacin. Los destructores son ejecutado en orden inverso
de su derivacin.
Si lo piensa un poco, tiene sentido que las funciones constructor sean ejecutadas en el orden de su derivacin. Porque
una clase base no tiene conocimiento de las clases derivadas, cualquier inicializacin que necesite realizar es separada de,
y posiblemente un prerequisito a, cualquier inicializacin realizada por la clase derivada. Adems, esta debe ejecutarse
primero.
Asimismo, es bastante sensible que los destructores sean ejecutados en orden inverso a su derivacin. Ya que la clase
base contiene una clase derivada, la destruccion de una clase base implica la destruccion de su clase derivada. Adems,
el constructor derivado debe ser llamado antes de que el objeto sea completamente destruido.
En el caso de una gran jerarqua de clases ( ej: cuando una clase derivada se convierta en la base clase de otra clase derivada
), la regla general se aplica: Los constructores son llamados en orden de su derivacion, los destructores son llamados en
orden inverso. Por ejemplo, este programa
#include <iostream> using namespace std; class base { public: base() { cout << construyendo base\n"; } ~base() { cout
<< Destruyendo base\n"; } }; class derivada1 : public base { public: derivada1() { cout << Construyendo derivada1\n";
} ~derivada1() { cout << destruyendo derivada1\n"; } }; class derivada2 : public derivada1 { public: derivada2() { cout
<< Construyendo derivada2\n"; } ~derivada2() { cout << Destruyendo derivada2\n"; } }; int main() { derivada2 obj;
// construir y destruir obj return 0; }

Muestra la salida:
construyendo base Construyendo derivada1 Construyendo derivada2 Destruyendo derivada2 destruyendo derivada1 Des-
truyendo base
La misma regla general se aplica en situaciones en que se ven implicadas multiples clases base. Por ejemplo, este programa
#include <iostream> using namespace std; class base1 { public: base1() { cout << Construyendo base1\n"; } ~base1() {
cout << Desstruyendo base1\n"; } }; class base2 { public: base2() { cout << Construyendo base2\n"; } ~base2() { cout
<< Destruyendo base2\n"; } }; class derivada : public base1, public base2 { public: derivada() { cout << Construyendo
derivada\n"; } ~derivada() { cout << Destruyendo derivada\n"; } }; int main() { derivada obj; // construir y destruir obj
return 0; }
70 CAPTULO 15. HERENCIA

Produce la salida:
Construyendo base1 Construyendo base2 Construyendo derivada Destruyendo derivada Destruyendo base2 Destruyendo
base1
Como puede ver, los constructores son llamados en el orden de su derivacion, de izquierda a derecha, como se especico
en la lista de herencia en derivada. Los destructores son llamados en orden inverso, de derecha a izquierda. Esto signica
que si base2 fuera especicada antes de base1 en la lista de derivada, como se muestra aqui:
class derivada: public base2, public base1 {
entonces la salida del programa anterior hubiera sido asi:
Construyendo base2 Construyendo base1 Construyendo derivada Destruyendo derivada Destruyendo base1 Derstruyendo
base2

15.3.6 PASANDO PARAMETROS A LOS CONSTRUCTORES DE LA CLASE BASE

Hasta ahora, ninguno de los ejemplos anteriores han incluido constructores que requieran argumentos. En casos donde
solo el constructor de la clase derivada requiera uno o mas argumentos, simplemente use la sintaxis estandarizada de
parametrizacion del constructor. Pero, como se le pasan argumentos a un constructor en una clase base? La respuesta es
usar una forma expandida de la declaracion de la clase derivada, la cual pasa argumentos entre uno o mas constructores
de la clase base. La forma general de esta declaracion expandida se muestra aqui:
constructor-derivada(lista-argumentos) : base1(lista-argumentos), base2(lista-argumentos), baseN(lista-argumentos) { cuer-
po del constructor derivado }
Aqui, desde base1 hasta baseN son los nombres de las clases base heredadas por la clase derivada. Notese los dos puntos
separando la declaracion del constructor de la clase derivada de las clases base, y que las clases base estan separadas cada
una de la otra por comas, en el caso de multiples clases base.
Considere este programa de ejemplo:
#include <iostream> using namespace std; class base { protected: int i; public: base(int x) { i = x; cout << Construyendo
base\n"; } ~base() { cout << Destruyendo base\n"; } }; class derivada : public base { int j; public: // derivada usa x, y
es pasado en consjunto a base derivada(int x, int y) : base(y) { j = x; cout << Construyendo derivada\n"; } ~derivada()
{ cout << Destruyendo derivada\n"; } void mostrar() { cout << i << " " << j << "\n"; } }; int main() { derivada obj(3,
4); obj.mostrar(); // muestra 4 3 return 0; }

Aqui, el constructor en derivada es declarado para que tome 2 parametros, x, y. Sin embargo, derivada() usa solo x,
y es pasada a base(). En general, el constructor de la clase derivada debe declarar el/los parametro(s) que esta clase
requiere, tamnbien cualquiera requerido por la clase base. Como el ejemplo anterior ilustra, cualquiera de los parametrosr
requeridos por la clase base son pasados a esta en la lista de argumentos de la clase base, especicadas despues de los dos
puntos.
Aqui tenemos un programa de ejemplo que usa multiples clases base:
#include <iostream> using namespace std; class base1 { protected: int i; public: base1(int x) { i = x; cout << Constru-
yendo base1\n"; } ~base1() { cout << Destruyendo base1\n"; } }; class base2 { protected: int k; public: base2(int x) {
k = x; cout << construyendo base2\n"; } ~base2() { cout << Destuyendo base2\n"; } }; class derivada : public base1,
public base2 { int j; public: derivada(int x, int y, int z) : base1(y), base2(z) { j = x; cout << construyendo derivada\n";
} ~derivada() { cout << Destruyendo derivada\n"; } void mostrar() { cout << i << " " << j << " " << k << "\n"; } }; int
main() { derivada obj(3, 4, 5); obj.mostrar(); // mostrar 3 4 5 return 0; }

Es importante comprender que los argumentos al constructor de la clase base son pasado via argumentos al constructor de
la clase derivada. Ademas, incluso si el constructor de una clase derivada no usa ningun argumento, este aun debe declarar
uno o mas argumentos si la clase base toma uno o mas argumentos. En esta situacion, los argumentos pasado a la clase
derivada son simplemente pasados hacia la clase base. Por ejemplo en el siguiente programa, el constructor en derivada
no toma argumentos, pero base1() y base2() lo hacen:
15.3. CONTROL DE ACCESO DE LA CLASE BASE. 71

#include <iostream> using namespace std; class base1 { protected: int i; public: base1(int x) { i=x; cout << Construyendo
base1\n"; } ~base1() { cout << Destruyendo base1\n"; } }; class base2 { protected: int k; public: base2(int x) { k=x; cout
<< construyendo base2\n"; } ~base2() { cout << Destruyendo base2\n"; } }; class derivada : public base1, public base2
{ public: /* el constructor en derivada no usa parametros, pero aun asi debe ser declarado para tomarlos y pasarselos a
las clases base. */ derivada(int x, int y) : base1(x), base2(y) { cout << Construyendo derivada\n"; } ~derivada() { cout
<< Destruyendo derivada\n"; } void mostrar() { cout << i << "" << k << "\n"; } }; int main() { derivada obj(3, 4);
obj.mostrar(); // mostrar 3 4 return 0; }

El constructor de la clasde derivada es libre de usar cualquiera de todos los parameteos que se declara que toma, ya sea
que uno o mas sean pasados a una clase base. Viendolo diferente, solo porque un argumento es pasado a la clase base no
declara su uso en la clase derivada. Por ejemplo, este fragmento es perfectamente valido:
class derivada : public base { int j; public: // derivada usa x, y, y tambien las pasa a base derivada(int x, int y) : base(x,y)
{ j = x*y; cout << construyendo derivada\n"; } // ...

Un punto nal a tener en mente cuando se pase argumentos a los constructores de la clase base: Un argumento siendo
pasado puede consistir de cualquier expresion valida en ese momento, incluyendo llamadas a funciones y variables. Esto
es para sostener el hecho de que C++ permite la inicializacion dinamica.

15.3.7 GARANTIZANDO ACCESO

Cuando una clase base es heredada como private, todos los miembros de esa clase (public, protected o private) se con-
vierten en miembros privates de la clase derivada. Sin embargo, en ciertas circunstancias, podra restaurar uno o mas
miembros heredados a su especicacion de acceso original. Por ejemplo, quizas desee permitir a ciertos miembros pu-
blicos de la clase base tener un estado public en la clase derivada, incluso aunque la clase es heredada como private. Hay
dos maneras de lograr esto. Primero, usted deberia usar una declaracion 'using' con la clase derivada. Este es el metodo
recomendado por los estandares C++ para uso no nuevo codigo. Sin embargo, una discusion de 'using' se retrasa hasta
nales de este libro donde los 'namespaces son examinados. ( La primera razon para usar using es para dar soporte a
namespaces. ) La segunda manera para ajustar el acceso a un miembro heredado es emplear una declaracion de acceso.
Las declaraciones de acceso son aun soportadas por el estandar C++, pero han sido marcadas como deprecadas, lo que
signica que ellas no deben ser usadas para nuevo codigo, Como aun son usadas en codigo existente, una discusion de las
declaraciones de acceso es presentada aqui:
Una declaracion de acceso toma esta forma general:
clase-base::miembro;
La declaracion de acceso es ubicada bajo los apropiados encabezados de acceso en la clase derivada. Notese que no se
requiere ( o permite ) una declaracion de tipo en una declaracion de acceso.
para ver como una declaracion de acceso funciona, comenzemos con este corto fragmento:
class base { public: int j; // public en base }; // heredar base como private class derivada : private base { public: // aqui
esta la declaracion de acceso base::j; // hacer j publica nuevamente. // ... };

Como base es heredada como private por derivada, la variable publica j es hecha una variable 'private' en derivada. Sin
embargo, la inclusion de esta declaracion de acceso
base::j;
Puede usar una declaracion de acceso para restaturar los derechos de acceso de miembros 'public' y 'protected', Sin
embargo, no puede usar una declaracion de acceso para elevar o disminuir un estado de acceso de un miembro. Por
ejemplo, un miembro declarado como private dentro de una clase base no puede ser hecho public por una clase derivada.
( Permitir esto destruirira la encapsulacion!)
El siguiente programa ilustra el uso en declaraciones de acceso.
#include <iostream> using namespace std; class base { int i; // private en base public: int j, k; void seti(int x) { i = x;
72 CAPTULO 15. HERENCIA

} int geti() { return i; } }; // Heredar base como private class derivada : private base { public: /* las siguientes tres de-
claraciones sobreescriben la herencia de base como private y restablece j, seti() y geti() a publico acceso */ base::j; //
hacer j public nuevamente - pero no k base::seti; // hacer seti() public base::geti; // hacer geti() public // base::i; // ilegal
- no se puede elevar el acceso int a; // public }; int main() { derivada obj; // obj.i = 10; // ilegal porque i es private en
derivada obj.j = 20; // legal porque j es hecho public en derivada //obj.k = 30; // ilegal porque k es private en derivada
obj.a = 40; // legal porque a es public en derivada obj.seti(10); cout << obj.geti() << " " << obj.j << " " << obj.a; return 0; }

Notese como este programa usa declaraciones de acceso para restaurar j, seti(), y geti() a un estadi 'public'. Los comen-
tarios describen otras varias restricciones de acceso.
C++ provee la habilidad de ajustar acceso a los miembros heredados para acomodar aquellas situaciones especiales en las
cuales la mayoria de una clase heredad esta pensada para ser private, pero unos cuantos miembros se hacen para mantener
su estado public o protected. Es mejor usar esta caracteristica escasamente.

15.3.8 LEYENDO GRAFICOS DE HERENCIA EN c++


En ocasiones las jerarquias de clases en C++ son hechas gracamente para hacerlas mas facil de comprender. Sin embargo,
debido a la forma en que estas son usualmente dibujadas por programadores de C++, los gracos de herencia son a veces
incomprendidos por principiantes. Por ejemplo, considere una situacion en la cual la clase A es heredad por la clase B, la
cual es heredada por la clase C. Usando la notacion estandar C++, esta situacion es dibujada como se muestra aqui:
NOTA: el simbolo ^ es la punta de la fecha, que indica direccion. A ^ | B ^ | C
Como puede ver, las echas apuntan hacia arriba, no hacia abajo. Mientras que muchas personas encuentran inicialmente
la direccion de las echas contra-intuitivasm este es el estilo que la mayoria de los programadores de C++ han adoptado.
En gracos al estilo C++, las echas apuntan a la clase base. Ademas, la echa quiere decir derivada desde, y no
derivando. Aqui tenemos otro ejemplo. Puede describir en palabras que signica esto?
AB^^||^^CD^^\/\/\/E
Este graco declara que la clase E es derivada de C y D. ( Eso es, E tiene multiples clases base, llamadas C y D.) Ademas,
C es derivada de A, y D derivada de B.
Mientras que la direccion de las echas puede confundir a la primera, es mejor que se sienta familiar con este estilo de
notacion graca, ya que es comunmente usada en libros, revistas, y documentacion de compiladores.

15.3.9 CLASES BASE VIRTUALES


Un elemento de ambiguedad puede ser introducido en un programa C++ cuando multiples clases bases son heredadas.
Considere este incorrecto programa:
// Este programa contiene un error y no compilara. #include <iostream> using namespace std; class base { public: int
i; }; // derivada 1 hereda base class derivada1 : public base { public: int j; }; // derivada2 hereda base class derivada2
: public base { public: int k; }; /* derivada3 hereda de ambas, derivada1 y derivada2 esto signica que hay 2 copias de
base en derivada3! */ class derivada3 : public derivada1, public derivada2 { public: int sum; }; int main() { derivada3
obj; obj.i = 10; // esto es ambiguo; cual i??? obj.j = 20; obj.k = 30; // i es ambiguo aqui, tambien obj.sum = obj.i + obj.j
+ obj.k; // tambien ambiguo, cual i? cout << obj.i << " "; cout << obj.j << " " << obj.k << " "; cout << obj.sum; return 0; }

Como indican los comentarios en este programa, ambos derivada1 y derivada2 heredan base. Sin embargo, derivada3
hereda ambos, derivada1 y derivada2. Como resultado existen dos copias de base presenten en un objeto del tipo derivada3,
asi pues una expresion como
obj.i = 20;

hacia cual i se esta haciendo referencia? La que se encuentra en derivada1 o derivada2? Como existen dos copias de base
presentes en el objeto 'obj' entonces hays dos obj.is. Como puede ver, la declaracion es inherentemente ambigua.
15.3. CONTROL DE ACCESO DE LA CLASE BASE. 73

Existen dos maneras de remediar el anterior programa. La primera es aplicar el operador de resolucion de ambito para
manualmente seleccionar una 'i'. Por ejemplo, la siguiente version el programa compilara y se ejecutara como se esperaba:
// Este programa usa el la resolucion de // ambito explicita para seleccionar 'i' #include <iostream> using namespace std;
class base { public: int i; }; // derivada 1 hereda base class derivada1 : public base { public: int j; }; // derivada2 hereda
base class derivada2 : public base { public: int k; }; /* derivada3 hereda de ambas, derivada1 y derivada2 esto signica
que hay 2 copias de base en derivada3! */ class derivada3 : public derivada1, public derivada2 { public: int sum; }; int
main() { derivada3 obj; obj.derivada1::i = 10; // ambito resuelto, se usa la 'i' derivada1 obj.j = 20; obj.k = 30; // ambito
resuelto obj.sum = obj.derivada1::i + obj.j + obj.k; // tambien resuelto aqui.. cout << obj.derivada1::i << " "; cout << obj.j
<< " " << obj.k << " "; cout << obj.sum; return 0; }

Aplicando ::, el programa manualmente selecciona a derivada1 como version de base. Sin embargo, esta solucion levanta
un detalle profundo: Que pasa si solo una copia de base es en realidad requerida? Existe alguna forma de prevenir que dos
copias sean incluidas en derivada3? La respuesta, como probablemente ha adivinado se logra con 'clases base virtuales.
Cuando dos o mas objetos son derivados de una clase base comn, puede prevenir mltiples copias de la clase base de
estar presentes en un objeto derivado por esas clases, declarando la clase base como virtual cuando esta es heredada. para
hacer esto, se precede el nombre de la clase base con la palabra virtual cuando esta es heredada.
Para ilustrar este proceso, aqui esta otra version del programa de ejemplo. Esta vez, derivada3 contiene solo una copia de
base.
// Este programa usa clases base virtuales. #include <iostream> using namespace std; class base { public: int i; }; // de-
rivada1 hereda base como virtual class derivada1 : virtual public base { public: int j; }; // derivada2 hereda base como
virtual class derivada2 : virtual public base { public: int k; }; /* derivada3 hereda ambas, derivada1 y derivada2. Esta
vez, solo existe una copia de la clase base. */ class derivada3 : public derivada1, public derivada2 { public: int sum; }; int
main() { derivada3 obj; obj.i = 10; // ahora no-ambiguo obj.j = 20; obj.k = 30; // no-ambiguo obj.sum = obj.i + obj.j +
obj.k; // no-ambiguo cout << obj.i << " "; cout << obj.j << " " << obj.k << " "; cout << obj.sum; return 0; }

Como puede ver la palabra clave 'virtual' precede el resto de la especicacin de la clase heredada. Ahora que ambas,
derivada1 y derivada2 han heredado base como virtual, cualquier herencia multiple que las incluya causar que slo una
copia de base se encuentre presente. Adems, en derivada3 existe slo una copia de base, y obj.i = 10 es perfectamente
vlida y no-ambiguo.
Otro punto a tener en mente: incluso cuando ambas, derivada1 y derivada2 espcican base como virtual, base sigue
presente en los dos tipos derivados. Por ejemplo, la siguiente secuencia es perfectamente vlida:
// Denir una clase del tipo derivada1 derivada1 miclase; miclase.i = 88;

La diferencia entre una clase base normal y una virtual se hace evidente slo cuando un objeto hereda la clase base mas
de una vez; si la clase base ha sido declarada como virtual, entonces slo una instancia de esta estar presente en el objeto
heredado. De otra manera, mltiples copias seran encontradas.
Captulo 16

Funciones virtuales

Editores:

16.1 Introduccin
Una de las tres mayores facetas de la programacin orientada a objetos es el polimorsmo. Aplicado a C++, el trmino
polimorsmo describe el proceso por el cual diferentes implementaciones de una funcin pueden ser accedidas a travs
del mismo nombre. Por esta razn, el polimorsmo es en ocasiones caracterizado por la frase Un interfaz, mltiples
mtodos. Esto signica que cada miembro de una clase general de operaciones puede ser accedido del mismo modo,
incluso cuando las acciones especcas con cada operacin puedan diferir.
En C++, el polimorsmo es soportado en tiempo de ejecucin y en tiempo de compilacin. La sobrecarga de operadores
y funciones son ejemplos de polimorsmo en tiempo de compilacin. Aunque la sobrecarga de operadores y funciones es
muy poderosa, stos no pueden realizar todas las tareas requeridas por un lenguaje realmente orientado a objetos. Adems,
C++ tambin permite polimorsmo en tiempo de ejecucin a travs del uso de clases derivadas y funciones virtuales, que
son los principales temas de este captulo.
Este captulo comienza con una corta discusin de punteros a tipos derivados, ya que stos dan soporte al polimorsmo
en tiempo de ejecucin.

16.2 PUNTEROS A TIPOS DERIVADOS.


El fundamento del polimorsmo en tiempo de ejecucin es el puntero hacia la clase base. Punteros a la clase base y clases
derivadas estn relacionados en la manera en que otros tipos de puntero no lo estn. Como aprendi al principio del libro,
un puntero de un tipo generalmente no puede apuntar a un objeto de otro tipo. Sin embargo, los punteros de clase base y
objetos derivados son la excepcin a esta regla. En C++, un puntero de la clase base podra ser usado para apuntar a un
objeto de cualquier clase derivada de esa base. Por ejemplo, asumiendo que usted tiene una clase base llamada 'clase_B'
y una clase llamada 'clase_D', la cual es derivada de 'clase_B'. En C++, cualquier puntero declarado como un puntero a
'clase_B' puede tambin ser un puntero a 'clase_D'. Entonces, dado
clase_B *p; // puntero al objeto del tipo clase_B clase_B objB // objeto del tipo clase_B clase_D objD // objeto del tipo
clase_D

las siguientes dos declaraciones son perfectamente validas:


p = &objB; // p apunta a un objeto del tipo clase_B p = &objD /* p apunta a un objeto del tipo clase_D el cual es un
objeto derivado de clase_B. */

74
16.2. PUNTEROS A TIPOS DERIVADOS. 75

En este ejemplo, 'p' puede ser usado para acceder a todos los elementos de objD heredados de objB. Sin embargo elementos
especicos a objD no pueden ser referenciados con 'p'.
Para un ejemplo mas concreto, considere el siguiente programa, el cual dene una clase llamada clase_B y una clase
derivada llamada clase_D. Este programa usa una simple jerarquia para almacenar autores y titulos.
// Usando punteros base en objetos de una clase derivada #include <iostream> #include <cstring> using namespace std;
class clase_B { public: void put_autor(char *s) { strcpy(autor, s); } void mostrar_autor() { cout << autor << "\n"; } pri-
vate: char autor[80]; }; class clase_D : public clase_B { public: void put_titulo(char *num) { strcpy(titulo, num); } void
mostrar_titulo() { cout << Titulo: "; cout << titulo << "\n"; } private: char titulo[80]; }; int main() { clase_B *p; clase_B
objB; clase_D *dp; clase_D objD; p = &objB; // direccion la clase base // Accesar clase_B via puntero p->put_autor(Tom
Clancy); // Accesar clase_D via puntero base p = &objD; p->put_autor(William Shakespeare); // Mostrar cada autor
a traves de su propio objeto. objB.mostrar_autor(); objD.mostrar_autor(); cout << "\n"; /* Como put_titulo() y mos-
trar_titulo() no son parte de la clase base, ellos no son accesibles a traves del puntero base 'p' y deben ser accedidas
directamente, o, como se muestra aqui, a traves de un puntero al tipo derivado. */ *dp = objD; dp->put_titulo(La Tem-
pestad); p->mostrar_autor(); // los dos, 'p' o 'dp' pueden ser usados aqui. dp->mostrar_titulo(); return 0; }

Este programa muestra lo siguiente:


Tom Clancy William Shakespeare William Shakespeare Titulo: La Tempestad
En este ejemplo, el puntero 'p' es denido como un puntero a clase_B. Sin embargo, este puede apuntar a un objeto de la
clase derivada clase_D y puede ser usado para acceder aquellos elementos de la clase derivada que son heredados de la
clase base. Pero recuerde, un puntero base no puede acceder aquellos elementos especicos de la clase derivada. De ahi
el porque de que mostrar_titulo() es accesada con el puntero dp, el cual es un puntero a la clase derivada.
Si se quiere acceder a los elementos denidos en una clase derivada usando un puntero de clase base, se debe hacer
un casting hacia el puntero del tipo derivado. Por ejemplo, esta linea de codigo llamara apropiadamente a la funcion
mostrar_titulo() en objD:
((clase_D *)p)->mostrar_titulo();

Los parentesis externos son necesarios para asociar el cast con 'p y no con el tipo de retorno de mostrar_titulo(). Aunque
no hay nada tecnicamente erroneo con castear un puntero de esta manera, es probablemente mejor evitarlo, ya que este
simplemente agrega confusion a sus codigo. ( En realidad, la mayoria de los programadores de C++ consideran esto como
mala forma.)
Otro punto a comprender es que, mientras un puntero base puede ser usado para apuntar a cualquier objeto derivado, no
es posible hacerlo de manera inversa. Esto es, no puede acceder a un objeto de tipo base usando un puntero a una clase
derivada.
Un puntero es incrementado y decrementado relativamente a su tipo base. Por lo tanto, cuando un puntero de la clase
base esta apuntado a un objeto derivado, incrementarlo o decrementarlo no hara que apunte al siguiente objeto de la clase
derivada. En vez de eso, apuntara a ( lo que piense que es ) el proximo objeto de la clase base. Por lo tanto, deberia
considerar invalido incrementar o decrementar un puntero de clase base cuando esta apuntando a un objeto derivado.
El hecho de que un puntero a un tipo base pueda ser usado para apuntar a cualquier objeto derivado de la base es extrema-
damente importante, y fundamental para C++. Como aprendera muy pronto, esta exibilidad es crucial para la manera
en que C++ implementa su polimorsmo en tiempo de ejecucion.

16.2.1 REFERENCIAS A TIPOS DERIVADOS

Similar a la accin de punteros ya descritas, una referencia a la clase base puede ser usada para referirse a un objeto de
un tipo derivado. La mas comun aplicacion de esto es encontrada en los parametros de la funciones. Un parametro de
referencia de la clase base puede recibir objetos de la clase base asi como tambien otro tipo derivado de esa misma base.
76 CAPTULO 16. FUNCIONES VIRTUALES

16.2.2 FUNCIONES VIRTUALES

El polimorsmo en tiempo de ejecucion es logrado por una combinacion de dos caracteristicas: 'Herencia y funciones
virtuales. Aprendio sobre la herencia en el capitulo precedente. Aqui, aprendera sobre funcion virtual.
Una funcin virtual es una funcion que es declarada como 'virtual' en una clase base y es redenida en una o mas clases
derivadas. Ademas, cada clase derivada puede tener su propia version de la funcion virtual. Lo que hace interesantes a
las funciones virtuales es que sucede cuando una es llamada a traves de un puntero de clase base ( o referencia ). En esta
situacion, C++ determina a cual version de la funcion llamar basandose en el tipo de objeto apuntado por el puntero.
Y, esta determinacion es hecha en 'tiempo de ejecucion'. Ademas, cuando diferentes objetos son apuntados, diferentes
versiones de la funcion virtual son ejecutadas. En otras palabras es el tipo de objeto al que esta siendo apuntado ( no el
tipo del puntero ) lo que determina cual version de la funcion virtual sera ejecutada. Ademas, si la clase base contiene
una funcion virtual, y si dos o mas diferentes clases son derivadas de esa clase base, entonces cuando tipos diferentes
de objetos estan siendo apuntados a traves de un puntero de clase base, diferentes versiones de la funcion virtual son
ejecutadas. Lo mismo ocurre cuando se usa una refrencia a la clase base.
Se declara una funcion como virtual dentro de la clase base precediendo su declaracion con la palabra clave virtual.
Cuando una funcion virtual es redenida por una clase derivada, la palabra clave 'virtual' no necesita ser repetida ( aunque
no es un error hacerlo ).
Una clase que incluya una funcion virtual es llamada una 'clase polimorca'. Este termino tambien aplica a una clase que
hereda una clase base conteniendo una funcion virtual.
Examine este corto programa, el cual demuestra el uso de funciones virtuales:
// Un ejemplo corto que usa funciones virtuales. #include <iostream> using namespace std; class base { public: virtual
void quien() {cout << Base << endl;} // especicar una clase virtual }; class primera_d : public base { public: // rede-
nir quien() relativa a primera_d void quien() {cout << Primera derivacion << endl;} }; class segunda_d : public base {
public: // redenir quien relativa a segunda_d void quien() {cout << Segunda derivacion << endl;} }; int main() { base
obj_base; base *p; primera_d obj_primera; segunda_d obj_segundo; p = &obj_base; p->quien(); // acceder a quien() en
base p = &obj_primera; p->quien(); // acceder a quien() en primera_d p = &obj_segundo; p->quien(); // acceder a quien()
en segunda_d return 0; }

Este programa produce la siguiente salida:


Base Primera derivacion Segunda derivacion
Examinemos el programa en detalle para comprender como funciona:
En 'base', la funcion 'quien()' es declarada como 'virtual'. Esto signica que la funcion puede ser redenida en una clase
derivada. Dentro de ambas 'primera_d' y 'segunda_d', 'quien()' es redenida relativa a cada clase. Dentro de main(), cuatro
variables son declaradas: 'obj_base', el cual es un objeto del tipo 'base'; 'p' el cual un un puntero a objetos del tipo 'base';
'obj_primera' y 'obj_segunda', que son objetos de dos clases derivadas. A continuacion 'p' es asignada con la direccion
de 'obj_base', y la funcion quien() es llamada. Como quien() es declarada como virtual, C++ determina en tiempo de
ejecucion, cual version de quien() es referenciada por el tipo del objeto apuntado por 'p'. En este caso, 'p' apunta a un
objeto del tipo 'base', asi que es la version de 'quien()' declarada en 'base' la que es ejecutada. A continuacion, se le asigna
a 'p' la direccion de 'obj_primera'. Recuerde que un puntero de la clase base puede referirse a un objeto de cualquier clase
derivadas. Ahora, cuando quien() es llamada, C++ nuevamente comprueba para ver que tipo de objeto es apuntado por
'p' y basado en su tipo, determina cual version de quien() llamar. Como 'p' apunta a un objeto del tipo 'obj_primera', esa
version de quien() es usada. De ese mismo modo, cuando 'p' es asignada con la direccion de 'obj_segunda', la version de
quien() declarada en 'segunda_d' es ejecutada.
RECUERDE: Es determinado en tiempo de ejecucion cual version de una funcion virtual en realidad es llamada. Ademas,
esta determinacion es basada solamente en el tipo del objeto que esta siendo apuntado por un puntero de clase base.
Una funcion virtual puede ser llamada normalmente, con la sintaxis del operador estandar de 'objeto.' Esto quiere decir
que en el ejemplo precedente, no seria incorrecto sintacticamente acceder a quien() usando esta declaracion:
obj_primera.quien();
16.2. PUNTEROS A TIPOS DERIVADOS. 77

Sin embargo, llamar a una funcion virtual de esta manera ignora sus atributos polimorcos. Es solo cuando una funcion
virtual es accesada por un puntero de clase base que el polimorsmo en tiempo de ejecucion es logrado.
A primera vista, la redenicion de una funcion virtual en una clase derivada parece ser una forma especial de sobrecarga
de funcion. Sin embargo, este no es el caso. De hecho, los dos procesos son fundamentalmente diferentes. Primero,
una funcion sobrecargada debe diferir en su tipo y/o numero de parametros, mientras que una funcion virtual redenida
debe tener exactamente el mismo tipo y numero de parametros. De hecho, los prototipos para una funcion virtual y sus
redeniciones debe ser exactamente los mismos. Si los prototipos dieren, entonces la funcion simplemente se considera
sobrecargada, y su naturaleza virtual se pierde. Otra restriccion es que una funcion virtual debe ser un miembro, no una
funcion 'friend', de la clase para la cual es denida. Sin embargo, una funcion virtual puede ser una funcion 'friend' de
otra clase. Tambien, es permisible para funciones destructores ser virtuales, pero esto no es asi para los constructores.
Cuando una funcion virtual es redenida en una clase derivada, se dice que es una funcion 'overriden' ( redenida )"
Por las restricciones y diferencias entre sobrecargar funciones normales y redenir funciones virtuales, el termino 'ove-
rriding' es usado para describir la redenicion de una funcion virtual.

16.2.3 LAS FUNCIONES VIRTUALES SON HEREDADAS

Una vez que una funcion es declarada como virtual, esta se mantiene virtual sin importar cuantas capas de clases derivadas
esta debe perdurar. Por ejemplo, si 'segunda_d' es derivada de 'primera_d' en vez de 'base', como se muestra en el proximo
ejemplo, entonces quien() es aun virtual y la version apropiada es aun correctamente seleccionada.
// Derivar de primera_d, no de base. class segunda_d : public primera_d { public: void quien() { // denir 'quien()' relativa
a 'segunda_d' cout << Segunda derivacion\n"; } };

Cuando una clase derivada no redene una funcion virtual, entonces la funcion, como se denicio en la clase base, es
usada. Por ejemplo intente esta version del programa precedente en el cual 'segunda_d' no redene 'quien()':
#include <iostream> using namespace std; class base { public: virtual void quien() { cout << Base\n"; } }; class pri-
mera_d : public base { public: void quien() { cout << Primera derivacion\n"; } }; class segunda_d : public base {
public: // quien() no denida }; int main() { base obj_base; base *p; primera_d obj_primera; segunda_d obj_segunda; p
= &obj_base; p->quien(); // acceder a quien() en 'base' p = &obj_primera; p->quien(); // acceder a quien() en 'primera_d'
p = &obj_segunda; p->quien(); /* acceder a quien() en 'base' porque segunda_d no la redene */ return 0; }

El programa ahora muestra la salida siguiente:


Base Primera derivacion Base
Como conrma la salida, como 'quien()' no ha sido redenida por 'segunda_d', entonces 'p' apunta a 'obj_segunda', es la
version de 'quien()' en 'base' la que es ejecutada.
Mantenga en mente que las caracteristicas heredadas de 'virtual' son jeraquicas. Por tanto,, si el ejemplo precendente es
modicado para que 'segunda_d' sea derivada de 'primera_d' en vez de 'base', entonces cuando quien() es referenciada
relativa a un objeto del tipo 'segunda_d', es la version de 'quien()' declarada dentro de primera_d' la que es llamada ya
que es la clase mas cercana a 'segunda_d', no 'quien()' dentro de base. El siguiente programa demuestra esta jerarquia.
#include <iostream> using namespace std; class base { public: virtual void quien() { cout << Base\n"; } }; class prime-
ra_d : public base { public: void quien() { cout << Primera derivacion\n"; } }; // segunda_d ahora hereda primera_d --
no base. class segunda_d : public primera_d { // quien() no es denida. }; int main() { base obj_base; base *p; primera_d
obj_primera; segunda_d obj_segunda; p = &obj_base; p->quien(); // acceder a 'quien()' en 'base'. p = &obj_primera; p-
>quien(); // acceder a 'quien()' en 'primera'. p = &obj_segunda; p->quien(); /* acceder a 'quien()' en 'primera_d' porque
'segunda_d' no la redene */ return 0; }

Este programa produce la siguiente salida:


Base Primera derivacion Primera derivacion
78 CAPTULO 16. FUNCIONES VIRTUALES

Como puede ver, 'segunda_d' ahora usa la version 'quien()' de 'primera_d' porque esa version es la mas cercana en la
cadena de herencia.

16.2.4 PORQUE FUNCIONES VIRTUALES

Como se declaraba en el inicio de este captulo, las funciones virtuales en combinacin con tipos derivados le permiten a
C++ soportar polimorsmo en tiempo de ejecucin. El polimorsmo es esencial para la programacin orientada a objetos
por una razn: Esta permite a una clase generalizada especicar aquellas funciones que sern comunes a todas las derivadas
de esa clase, mientras que permite a una clase derivada denir la implementacin especca de algunos o todas de esas
funciones. A veces esta idea es expresada como: La clase base dicta la 'interface' general que cualquier objeto derivado
de esa clase tendr, pero permite a la clase derivada denir el mtodo actual usado para implementar esa interface. De
ah que la frase una interface mltiples mtodos sea usada a menudo para describir el polimorsmo.
Parte del truco de aplicar el polimorsmo de una manera satisfactoria es comprender que la clase base y derivada forman
una jerarqua, la cual se mueve de mayor a menor generalizacin (base a derivada). Diseada correctamente, la clase
base provee todos los elementos que una clase derivada puede usar directamente. Tambin dene cuales funciones la
clase derivada debe implementar por su cuenta. Esto permite a la clase derivada la exibilidad para denir sus propios
mtodos, y aun mantener un interface consistente. Eso es, como la forma de la interface es denida por la clase base,
cualquier clase derivada compartir esa interface comn. Adems, el uso de funciones virtuales hace posible para la clase
base denir interfaces genricas que sern usada por todas las clases derivadas. En este punto, usted debe preguntarse a
si mismo porque una consistente interface con mltiples implementaciones es importante. La respuesta, nuevamente, no
lleva a la fuerza central manejadora detrs de la programacin orientada a objetos: Esta ayuda al programador a manejar
programas de complejidad creciente. Por ejemplo, si usted desarrolla su programa correctamente, entonces usted sabr
que todos los objetos que usted derive de una clase base son accedidos en la misma manera general, incluso si las acciones
especcas varan de una clase derivada a la prxima. Esto signica que usted necesita solo recordar una interface, en vez de
varias. Tambin, su clase derivada es libre de usar cualquiera o toda la funcionalidad provista por la clase base. No necesita
reinventa esos elementos. Por tanto, la separacin de interface e implementacin permite la creacin de libreras de clases,
las cuales pueden ser provistas por un tercero. Si estas libreras son implementadas correctamente, ellas proveern una
interface comn que usted puede usar para derivar clases suyas propias que cumplan sus necesidades especicas. Por
ejemplo, tanto como las Microsoft Fundation Classes ( MFC ) y la librera de clases .NET Framework Windows Forms
soporta programacin en Windows. Usando estas clases, su programa puede heredar mucha de la funcionalidad requerida
por un programa de Windows. Usted necesita agregar solo las caractersticas nicas a su aplicacin. Este es un mayor
benecio cuando se programa en sistemas complejos.

16.2.5 UNA SIMPLE APLICACION DE LAS FUNCIONES VIRTUALES

Para tener una idea del poder del concepto una interface, multiples metodos, examinemos el siguiente programa. Este
crea una clase base llamada 'gura'. Esta clase almacena las dimensiones de varios objetos de 2-dimensiones y calcula sus
areas. La funcion 'set_dim()' es una funcion miembro estandar porque esta operacion sera comun a las clases derivadas.
Sin embargo, 'mostrar_area()' es declarada como virtual porque el metodo de computar el area de cada objeto puede
variar. El programa usa 'gura' para derivar dos clases especicas llamadas 'rectangulo' y 'triangulo'.
#include <iostream> using namespace std; class gura { protected: double x, y; public: void set_dim(double i, double j) {
x = i; y = j; } virtual void mostrar_area() { cout << No hay calculo de area denido "; cout << " para esta clase.\n"; } };
class triangulo : public gura { public: void mostrar_area() { cout << Triangulo con alto "; cout << x << " y base " << y;
cout << " tiene un area de "; cout << x * 0.5 * y << ".\n"; } }; class rectangulo : public gura { public: void mostrar_area()
{ cout << Rectangulo con dimensiones "; cout << x << " x " << y; cout << " tiene un area de "; cout << x * y << ".\n";
} }; int main() { gura *p; // crear un puntero al tipo base. triangulo t; // crear objetos de tipos derivados rectangulo r; p
= &t; p->set_dim(10.0, 5.0); p->mostrar_area(); p = &r; p->set_dim(10.0, 5.0); p->mostrar_area(); return 0; }

La salida es mostrada aqui:


Triangulo con alto 10 y base 5 tiene un area de 25. Rectangulo con dimensiones 10 x 5 tiene un area de 50.
16.2. PUNTEROS A TIPOS DERIVADOS. 79

En el programa, notese que ambas interfaces, 'rectangulo' y 'triangulo', son la misma, incluso ambas proveen sus propios
metodos para computar el area de cada uno de sus objetos.
Dada la declaracion para 'gura', es posible derivar una clase llamada 'circulo' que computara el area de un circulo, dado
en radio? La respuesta es 'Si'. Todo lo que necesita hacer es crear un nuevo tipo derivado que calcule el area de un circulo.
El poder de las funciones virtuales esta basado en el hecho de que usted puede facilmente derivar un nuevo tipo que
mantendra un interface comun con otros objetos relaciones. Por ejemplo, esta es una manera de hacerlo:
class circulo : public gura { public: void mostrar_area() { cout << Circulo con radio "; cout << x; cout << " tiene un
area de "; cout << 3.14 * x * x; } };

Antes de intentar usar 'circulo', vea de cerca la denicion de 'mostrar_area()'. Note que esta usa solo el valor de x, el
cual se asume que almacena el radio. ( Recuerde, el area de un circulo es calculada usando la formula 'PI*R a la 2'.)
Sin embargo, la funcion 'set_dim()' como se dene en 'gura', asume que seran pasados dos valores, no solo uno. Como
'circulo' no requiere este segundo valor, que tipo de accin podemos tomar?
Hay dos manera de resolver este problema. La primera y peor, usted simplemente llama a 'set_dim()' usando un valor
falso como segundo parametro cuando use un objeto 'circulo'. Esto tiene la desventaja de ser chapucero, en conjunto
requiriendo que recuerde una excepcion especial, la cual viola la losoa una interface, muchos metodos.
Una mejor manera de resolver este problema es pasarle al parametro 'y' dentro de 'set_dim()' un valor por defecto. En-
tonces, cuando se llame a 'set_dim()' para un circulo, necesita especicar solo el radio. Cuando llame a 'set_dim()' para
un triangulo o un rectangulo, especicara ambos valores. El programa expandido, el cual usa este metodo, es mostrado
aqui:
#include <iostream> using namespace std; class gura { protected: double x, y; public: void set_dim(double i, double j=0)
{ x = i; y = j; } virtual void mostrar_area() { cout << No hay calculo de area denido "; cout << " para esta clase.\n";
} }; class triangulo : public gura { public: void mostrar_area() { cout << Triangulo con alto "; cout << x << " y base
" << y; cout << " tiene un area de "; cout << x * 0.5 * y << ".\n"; } }; class rectangulo : public gura { public: void
mostrar_area() { cout << Rectangulo con dimensiones "; cout << x << " x " << y; cout << " tiene un area de "; cout <<
x * y << ".\n"; }; }; class circulo : public gura { public: void mostrar_area() { cout << Circulo con radio "; cout << x;
cout << " tiene un area de "; cout << 3.14 * x * x; } }; int main() { gura *p; // crear un puntero al tipo base triangulo
t; // crear objetos de tipos derivada rectangulo r; circulo c; p = &t; p->set_dim(10.0, 5.0); p->mostrar_area(); p = &r;
p->set_dim(10.0, 5.0); p->mostrar_area(); p = &c; p->set_dim(9.0); p->mostrar_area(); return 0; }

Este programa produce la siguiente salida:


Triangulo con alto 10 y base 5 tiene un area de 25. Rectangulo con dimensiones 10 x 5 tiene un area de 50. Circulo con
radio 9 tiene un area de 254.34
TIP: Mientras que las funciones virtuales son sintacticamente faciles de comprender, su verdadero poder no puede ser
demostrado en ejemplos cortos. En general, el polimorsmo logra su gran fuerza en sistemas largos y complejos. Si
continua usando C++, las oportunidades de aplicar funciones virtuales se presentaran por si mismas.

16.2.6 FUNCIONES VIRTUALES PURAS Y CLASES ABSTRACTAS

Como se ha visto, cuando una funcion virtual que no es redenida en una clase derivada es llamada por un objeto de
esa clase derivada, la version de la funcion como se ha denido en la clase base es utilizada. Sin embargo, en muchas
circunstancias, no habra una declaracion con signicado en una funcion virtual dentro de la clase base. Por ejemplo, en
la clase base 'gura' usada en el ejemplo anterior, la denicion de 'mostrar_area()' es simplemente un sustituto sintetico.
No computara ni mostrara el area de ningun tipo de objeto. Como vera cuando cree sus propias librerias de clases no es
poco comun para una funcion virtual tener una denicion sin signicado en el contexto de su clase base.
Cuando esta situacion ocurre, hay dos manera en que puede manejarla. Una manera, como se muestra en el ejemplo, es
simplemente hacer que la funcion reporte un mensaje de advertencia. Aunque esto puede ser util en ocasiones, no es el
apropiado en muchas circunstancias. Por ejemplo, puede haber funciones virtuales que simplemente deben ser denidas
por la clase derivada para que la clase derivada tenga algun signicado. Considere la clase 'triangulo'. Esta no tendria
80 CAPTULO 16. FUNCIONES VIRTUALES

signicado si 'mostrar_area()' no se encuentra denida. En este caso, usted desea algun metodo para asegurarse de que
una clase derivada, de hecho, dena todas las funciones necesarias. En C++, la solucion a este problema es la 'funcion
virtual pura.'
Una 'funcion virtual pura' es una funcin declarada en una clase base que no tiene denicion relativa a la base. Como
resultado, cualquier tipo derivado debe denir su propia version -- esta simplemente no puede usar la version denida en
la base. Para declarar una funcion virtual pura use esta forma general:
virtual 'tipo' 'nombre_de_funcion'(lista_de_parametros) = 0;
Aqui, 'tipo' es el tipo de retorno de la funcion y 'nombre_de_funcion' es el nombre de la funcion. Es el = 0 que designa la
funcion virtual como pura. Por ejemplo, la siguiente version de 'gura', 'mostrar_area()' es una funcion virtual pura.
class gura { double x, y; public: void set_dim(double i, double j=0) { x = i; y = j; } virtual void mostrar_area() = 0; //
pura };

Declarando una funcion virtual como pura, se fuerza a cualquier clase derivada a denir su propia implementacin. Si
una clase falla en hacerlo, el compilador reportara un error. Por ejemplo, intente compilar esta version modicando del
programa de guras, en el cual la denicin de mostrar_area() ha sido removida de la clase 'circulo':
/* Este programa no compilara porque la clase circulo no redenio mostrar_area() */ #include <iostream> using names-
pace std; class gura { protected: double x, y; public: void set_dim(double i, double j=0) { x = i; y = j; } virtual void
mostrar_area() = 0; // pura }; class triangulo : public gura { public: void mostrar_area() { cout << Triangulo con alto
"; cout << x << " y base " << y; cout << " tiene un area de "; cout << x * 0.5 * y << ".\n"; } }; class rectangulo : public
gura { public: void mostrar_area() { cout << Rectangulo con dimensiones "; cout << x << " x " << y; cout << " tiene
un area de "; cout << x * y << ".\n"; }; }; class circulo : public gura { // la no denicion de mostrar_area() causara un
error }; int main() { gura *p; // crear un puntero al tipo base triangulo t; // crear objetos de tipos derivada rectangulo r;
circulo c; // ilegal -- no puedo crearla! p = &t; p->set_dim(10.0, 5.0); p->mostrar_area(); p = &r; p->set_dim(10.0, 5.0);
p->mostrar_area(); return 0; }

Si una clase tiene al menos una funcion virtual pura, entonces esa clase se dice que es 'abstracta'. Una clase abstracta tiene
una caracteristica importante: No puede haber objetos de esa clase. En vez de eso, una clase abstracta debe ser usada
solo como una base que otras clases heredaran. La razon por la cual una clase abstracta no puede ser usada para declarar
un objeto es, por supuesto, que una o mas de sus funciones no tienen denicion. Sin embargo, incluso si la clase base es
abstracta, la puede usar aun para declarar punteros o referencias, los cuales son necesarios para soportar el polimorsmo
en tiempo de ejecucion.

16.2.7 ENLACE TEMPRANO VS TARDIO

Existen dos terminos que son comunmente usado cuando se discute sobre programacin orientada a objetos: Enlace
temprano y Enlace Tardio ( del ingles early binding and late binding ). Relativo a C++, estos terminos se reeren a
eventos que ocurren en tiempo de compilacion y eventos que ocurren en tiempo de ejecucion, respectivamente.
Enlace temprano signica que una llamada a una funcion es resuelta en tiempo de compilacion. Esto es, toda la informacion
necesaria para llamar a una funcion es conocida cuando el programa es compilado. Ejemplos de enlace temprano incluyen
llamadas a funciones estandar, llamadas a funciones sobrecargadas y llamadas a funciones de operadores sobrecargados.
La principal ventaja del enlace temprano es la eciencia -- es rapido, y a menudo requiere menos memoria. Su desventaja
es falta de exibilidad.
Enlace tardio signica que una llamada a la funcion es resuelta en tiempo de ejecucin. Mas precisamente a que la llamada
a la funcion es determinada al vuelo mientras el programa se ejecuta. Enlace tardio es logrado en C++ a traves del uso
de funciones virtuales y tipos derivados. La ventaja del enlace tardio es que permite gran exibilidad. Puede ser usada
para soportar una interface comn, mientras que se permite a varios objetos utilizar esa interface para denir sus propias
implementaciones. Por tanto, puede ser usada para ayudar a crear librerias de clases, las cuales pueden ser reusadas y
extendidas. Su desventaja, sin embargo, es una ligera perdida de velocidad de ejecucion.
16.2. PUNTEROS A TIPOS DERIVADOS. 81

16.2.8 POLIMORFISMO Y EL PURISTA


A traves de este libro, y en este capitulo especicamente, se ha hecho una distincion entre polimorsmo de tiempo de
ejecucion y en tiempo de compilacion. Caracteristicas polimorcas en tiempo de compilacion son sobrecarga de opera-
dores y funciones. El polimorsmo en tiempo de ejecucion es logrado con funciones virtuales. La denicion mas comun
de polimorsmo es, una interface, multiples metodos, y todas estas caracteristicas se ajustan a este signicado. Sin
embargo, aun existe controversia con el uso del termino polimorsmo.
Algunos puristas POO han insistido que el termino debe usarse para referise solo a eventos que ocurren en tiempo de
ejecucin. Tambin, ellos podrian decir que solo las funciones virtuales soportan el polimorsmo. Parte de este punto
de vista esta fundado en el hecho de que los primeros lenguajes de computacin polimrcos fueran intrpretes (en
el que todos los eventos ocurrian en tiempo de ejecucin). La llegada de lenguajes polimrcos compilador expandi
el concepto de polimorsmo. Sin embargo, aun algunos aseguran que el trmino polimorsmo debera referirse solo a
eventos en tiempo de ejecucin. La mayora de los programadores de C++ no estan de acuerdo con este punto de vista
y establecen que el trmino aplica en ambos casos a caractersticas en tiempo de compilacin y en tiempo de ejecucin.
Sin embargo, no se sorprenda si algun dia, alguien va en contra de usted sobre el uso de este termino!
Si su programa usa enlace tardio o temprano depende de lo que el programa este diseado para hacer. ( En realidad, la
mayoria los programas grandes usan una combinacion de los dos.) Enlace tardio es una de las caractersticas mas poderosas
de c++. Sin embargo, el precio que usted paga por este poder es que su programa se ejecutara ligeramente mas lento. Por
lo tanto, es mejor usar enlace tardio solo cuando este le agregue signicado a la estructura y manejabilidad de su programa.
( En esencia, use -- pero no abuse -- el poder.) Mantenga en mente, sin embargo, que la perdida de desempeo causada
por enlace tardio es muy ligera, asi pues cuando la situacin requiera enlace tardio, usted denitivamente deberia usarlo.
Captulo 17

Punteros

Editores:

17.1 Punteros

Los punteros permiten simular el paso por referencia, crear y manipular estructuras dinamicas de datos, tales como listas
enlazadas, pilas, colas y rboles. Generalmente las variables contienen valores especicos. Los punteros son variables
pero en vez de contener un valor especico, contienen las direcciones de las variables a las que apuntan. Para obtener o
modicar el valor de la variable a la que apuntan se utiliza el operador de indireccin. Los punteros, al ser variables deben
ser declaradas como punteros antes de ser utilizadas.

17.1.1 Sintaxis

int *ptrID, ID; ID = 8; ptrID = &ID; // puntero a ID

ptrID es un puntero a int, mientras que la variable ID es solo una variable del tipo int. Todo puntero debe ser precedido
por un asterisco (*) en la declaracin.
Se puede declarar ms de un puntero en la misma sentencia. En el ejemplo que sigue se ve la declaracin de dos punteros
a int.
int *ptrY, *ptrX;

17.1.2 Operadores

Existen dos operadores a tener en cuenta cuando trabajamos con punteros. El operador de direccin (&) que devuelve
la direccin de memoria de su operando y el operador de indireccin (*) que devuelve un alias para el objeto al cual
apunta el operando del puntero.
En el siguiente ejemplo vemos como se inicializa una variable X con el valor 15. Luego se crea un puntero a int y por
ltimo el puntero pasa a apuntar a la variable X. Esto es, ptrX es un puntero a X.
int X = 15; int *ptrX; ptrX = &X;

82
17.1. PUNTEROS 83

17.1.3 Punteros y vectores (arrays)

Los vectores son punteros constantes. Un vector sin subindice es un puntero al primer elemento del vector. Una matriz
es un vector de vectores. (Ej: int M[3][3];) de manera que en cada elemento del primer vector se cuelga otro vector,
pudiendo hacer as referencia a las y columnas.
int X[15]; int *ptrX; ptrX = X; // ptrX recibe la direccin del primer elemento ( 0 ) de X

As como tambin podra escribirse


int X[15]; int *ptrX; ptrX = &X[0]; // ptrX es igual a la direccin del primer elemento de X

Se pueden utilizar distintos elementos del vector teniendo en cuenta la sintaxis de punteros.
int X[15], Y, *ptrX; ptrX = X; Y = *( ptrX + 7 );

En este caso puede verse que Y toma el valor del elemento 7 del vector X, siendo 7 el desplazamiento dentro del vector. El
operador de indireccin queda fuera del parntesis porque tiene una prioridad superior a la del operador +. De no existir
los parntesis, se sumaria 7 al elemento X[0]. Teniendo en cuenta que los vectoresson punteros constantes, el nombre del
vector puede tratarse como un puntero:
Y = *( X + 7 );

17.1.4 Aritmtica de Punteros

Al usar punteros a matrices, hay que tener en cuenta que la aritmtica cambia sensiblemente.
#include <iostream> using std::cout; using std::endl; void main() { int X[6] = { 1, 2, 3, 4, 5, 6 }; int *prtX; prtX = X;
// incializo el valor del puntero. cout << endl << *prtX; prtX += 2; cout << endl << *prtX; prtX -= 2; cout << endl <<
*prtX; prtX++; cout << endl << *prtX; } atte: Oscar Torres & David Paz

En el siguiente cdigo, primero se crea un puntero a un arreglo de 6 elementos y si inicializa el puntero prtX al primer
elemento del arreglo X[0]. Si tenemos en cuenta que el siguiente ejemplo se ejecuta en una computadora con enteros de 4
bytes, el segundo elemento de la matriz tendr en memoria un desplazamiento de 4 bytes, el 2 de ocho y asi sucesivamente.
La operacin prtX += 2; produce un desplazamiento llevndolo al 3 elemento dentro del arreglo. Debe entenderse que prtX
ahora apunta a una direccin de memoria y la instruccin cambia esta direccin de memoria sumndole 2 multiplicado
por el tamao del tipo de dato del arreglo que en este supuesto sera de 4. (dir = ( dir + 2 * 4 )), dando por resultado un
desplazamiento de 8 bytes. Sera igual que ejecutar la operacin prtX = &X[2];. La operacin prtX -= 2 obedece a la misma
lgica estableciendo el puntero al primer elemento del array X[0] y el operador ++ modica el puntero desplazndolo 4
bytes y asignndole el segundo elemento de la matriz.

17.1.5 Matrices de punteros

Para realizar una estructura de datos dinmica, se puede utilizar una matriz donde sus elementos sean punteros. Supo-
niendo que queramos hacer un calendario y lo dividamos por semanas. Podramos utilizar una matriz con los das de la
semana.
const char *dias[7] = { Domingo, Lunes, Martes, Miercoles, Jueves, Viernes, Sabado }

Cada da de la semana, no es un elemento de la matriz, sino que la expresin dias[7] crea una matriz de siete elementos
como punteros a char.
84 CAPTULO 17. PUNTEROS

17.1.6 Formas de pasar un valor por referencia

Un puntero constante a un dato no constante


#include <iostream> void sumoeldoble( int * ); // prototipo void main () { int X = 15; sumoeldoble( &X ); // Pasa la
direccin de memoria de X . std::cout << X; } void sumoeldoble( int *ptrX ) { // Toma el valor de X mediante el opera-
dor de indireccin // La funcion no devuelve nada porque modica el valor por referencia. *ptrX = *ptrX + ( *ptrX * 2 ); }

Un puntero no constante a un dato constante


#include <iostream> void imprimeChars( const char * ); // prototipo void main () { char cFrase[] = Hola Mundo"; im-
primeChars( cFrase ); } void imprimeChars( const char *ptrStr ) { for ( ; *ptrStr != '\0'; ptrStr++ ) //Sin inicializacin
std::cout << *ptrStr; }

Un puntero constante a un dato no constante


Un puntero es constante cuando apunta siempre a la misma direccin de memoria y si el dato no es constante entonces el
valor puede ser modicado.
void main () { int foo, bar; int * const ptrFoo = &foo; // Apuntador constante a un entero en la direccin de memoria
de foo *ptrFoo = 53; // Esto devuelve un error porque es un puntero constante. // No se puede alterar la direccin de
memoria a la que apunta. ptrFoo = &bar; }

Un puntero constante a un dato constante


El puntero constante apunta siempre a la misma direccin de memoria y el valor al que apunta dicho puntero no puede
ser modicado mediante el puntero. Este es el metodo en que se debe pasar matrices a funciones que solo leen la matriz
y no la modican.
#include <iostream> using namespace std; int main () { int foo = 3, bar; const int * const ptrFoo = &foo; cout << foo;
*ptrFoo = 53; //Error porque no puede alterarse el valor ptrFoo = &bar; // Error foo = 23; cout << foo; }

17.1.7 Punteros a funciones

Teniendo en cuenta que el nombre de una funcin es en realidad la direccin de memoria donde comienza el cdigo, los
punteros a funciones contienen la direccin de memoria de la funcin a la que apunta, y se pueden pasar y retornar entre
funciones.
#include <iostream> using namespace std; bool functionA( int, int, bool (*)( int ) ); //Prototipo bool functionB( int );
//Prototipo void main() { int x = 113, y = 226; if ( functionA( x, y, functionB ) ) cout << "\nEl resultado es verdadero";
else cout << "\nEl resultado es falso"; } bool functionA( int param1, int param2, bool (*vericar)( int ) ) { if ( ( (*veri-
car)( param1 ) ) && ( (*vericar)( param2 ) ) ) return true; } bool functionB( int param ) { if ( param > 100 ) return
true; else return false; }

En el ejemplo anterior podr ver que en la denicin de los prototipos, la funcin functionA recibe tres parmetros,
siendo el tercer parmetro un puntero a la funcion functionB, practicamente copiando el prototipo sin el nombre. Cuando
se ejecuta functionA, se le pasa como parametro el nombre de la funcin

17.1.8 Ordenamiento burbuja

Ordenamiento burbuja utilizando punteros


#include <iostream> #include <iomanip> using namespace std; void orden( int *, const int ); // prototipo void swap( int
* const, int * const ); // prototipo int main() { const int nSize = 10; int a[ nSize ] = { 3, 9, 14, 27, 18, 154, 8, 6, 74, 33 };
17.1. PUNTEROS 85

cout << "\nElementos a ordenar\n"; for ( int j = 0; j < nSize; j++ ) cout << setw( 5 ) << a[ j ]; orden( a, nSize ); // ordena
el arreglo cout << "\nElementos ordenados\n"; for ( int j = 0; j < nSize; j++ ) cout << setw( 5 ) << a[ j ]; cout << endl;
return 0; // indica terminacin exitosa } void orden( int *matriz, const int nSize ) { for ( int pasada = 0; pasada < nSize - 1;
pasada++ ) { for ( int k = 0; k < nSize - 1; k++ ) { if ( matriz[ k ] > matriz[ k + 1 ] ) { swap( &matriz[ k ], &matriz[ k + 1 ]
); } } } } void swap( int * const ptrElemento1, int * const ptrElemento2 ) { int mantiene = *ptrElemento1; *ptrElemento1
= *ptrElemento2; *ptrElemento2 = mantiene; }

17.1.9 Proyecto de colas para un banco

Por cada 2 clientes normales pasa uno VIP al Cajero


El codigo es muy simple pero muy trabajado y depurado, esta relizado con if/while.... lo mas complicado son los apunta-
dores, pero nada del otro mundo, muestra el perfecto uso de funciones, tiene una clave: jahvi, de todas lo puedes leer en
el codigo. Compila perfecto en DEV C++. Tiene una interfaces muy creativa, ideal para estudiantes que se estn inciando
en el desarrollo de software con programacin estructurada. recuerda copiarlo en un pagina en blanco en dev c++ desde
aqui: (copialo completo y compilalo)
#include <stdio.h> #include <iostream> #include <conio.h> #include <stdlib.h> #include <string.h> using namespa-
ce std; typedef struct nodo { int cedula; struct nodo*siguiente; }tiponodo; typedef tiponodo *pnodo; typedef tipono-
do *cola; cola cola_n=NULL; cola cola_vip=NULL; int solic; void insertar (cola *cola_n) { cola aux, creado; crea-
do=(pnodo)malloc(sizeof(tiponodo)); cout<<" |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||"<<endl; cout<<" ||||||||||||||||||||METRO-
POLI banco universal||||||||||||||||||||"<<endl; cout<<" |||||||||||||||||||||||Le da la bienvenida|||||||||||||||||||||||"<<endl; cout<<" |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
cout<<" Estimado cliente: Introduzca su numero de cedula"<<endl; cin>> creado->cedula; cout<<" Pronto sera atendi-
do"<<endl; if ((*cola_n)==NULL) { creado->siguiente=*cola_n; *cola_n=creado; } else { aux=*cola_n; while(aux!=NULL)
{ if(aux->siguiente==NULL) { creado->siguiente=aux->siguiente; aux->siguiente=creado; aux=aux->siguiente; } aux=aux-
>siguiente; } } } void mostrar_cola (cola cola_n) { cola aux; aux=cola_n; if(aux==NULL) { cout<<"No hay clientes en co-
la"<<endl; } else { while(aux!=NULL) { cout<<"Cedula: "<<aux->cedula<<endl; cout<<""<<endl; aux=aux->siguiente;
cout<<"* Clientes ordenados por orden de llegada((((((SIMCOLAS))))))"<<endl; } } } //modicado hasta ac... falta de
orden por parte del creador void insertar_vip (cola *cola_vip){ cola aux_2, creado_vip; creado_vip=(pnodo)malloc(sizeof(tiponodo));
cout<<" |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||"<<endl;//62 cout<<" ||||||||||||||||||||METROPOLI banco universal||||||||||||||||||||"<<endl;
cout<<" |||||||||||||||||||||||Le da la bienvenida|||||||||||||||||||||||"<<endl; cout<<" |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||"<<endl; cout<<"
Estimado cliente: Introduzca su numero de cedula"<<endl; cin>> creado_vip->cedula; cout<<" Pronto sera atendi-
do"<<endl; if ((*cola_vip)==NULL){ creado_vip->siguiente=*cola_vip; *cola_vip=creado_vip; }else{ aux_2=*cola_vip;
while(aux_2!=NULL){ if(aux_2->siguiente==NULL){ creado_vip->siguiente=aux_2->siguiente; aux_2->siguiente=creado_vip;
aux_2=aux_2->siguiente; } aux_2=aux_2->siguiente; } } } void mostrar_cola_vip (cola cola_vip){ cola aux_2; aux_2=cola_vip;
if(aux_2==NULL){ cout<<"No hay clientes V.I.P en cola"<<endl; }else{ while(aux_2!=NULL){ cout<<"Cedula: "<<aux_2-
>cedula<<endl; cout<<""<<endl; cout<<"* Clientes ordenados por orden de llegada((((((SIMCOLAS))))))"<<endl; aux_2=aux_2-
>siguiente; } } } void pop (cola *cola_n){ if((*cola_n)==NULL){ cout<<"No hay clientes en cola ((((((SIMCOLAS))))))"<<endl;
cout<<""<<endl; } if((*cola_n)!=NULL){ cout<<"Cliente: "<<(*cola_n)->cedula; cout<<" es su turno"<<endl; *cola_n=(*cola_n)-
>siguiente; free(cola_n); system(PAUSE); cout<<""<<endl; } if((*cola_n)==NULL){ cout<<"No hay clientes en cola
((((((SIMCOLAS))))))"<<endl; cout<<""<<endl; }else{ cout<<"Cliente: "<<(*cola_n)->cedula; cout<<" es su turno"<<endl;
*cola_n=(*cola_n)->siguiente; free(cola_n); system(PAUSE); cout<<""<<endl; } } void pop_vip(cola *cola_vip){ if(cola_vip!=NULL){
cout<<"Cliente: "<<(*cola_vip)->cedula; cout<<" es su turno"<<endl; *cola_vip=(*cola_vip)->siguiente; free(cola_vip);
}else{ cout<<"No hay usuarios V.I.P en cola ((((((SIMCOLAS))))))"<<endl; } } void menu(); void Insertar(); void in-
sertar_vip(); void mostrar_cola(); void mostrar_colavip(); void solicitar_numero(); void salir(); struct perly{ char uxer[];
}data; struct perly *p = &data; int main(int argc, char *argv[]) { system(color 0a); cout<<" * * * "<<endl; cout<<" * *
"<<endl; cout<<" * | * * * * "<<endl; cout<<" * -+- * "<<endl; cout<<" __ | * * * * "<<endl; cout<<" ___| | * _|_ * ____
____ "<<endl; cout<<" [_]||__ _| |_ * | |....| |....| * * "<<endl; cout<<" | | _|[] []|_ ___ | |....| | ___|__ "<<endl; cout<<"
[_]| |__|_ ______| | | |_| |....| || -- - | * * "<<endl; cout<<" |________ |__ | |# #| |_| |....| || - -- | _____ "<<endl; cout<<" [_]|
_____ | |__[]| | | |_| |....|__|| -- - | |* * *| "<<endl; cout<<" | [___] | |__ |# #| |_| |....|__|| -- -_|_|* * *| "<<endl; cout<<" [_]| |
| | |__| |__|....| || ___|* *|* * *| "<<endl; cout<<" | _____ | |__ #|_______|....| | |* *|* *|* * *| "<<endl; cout<<" [_]| [___] |
|__|_______|__ | | |* *|* *|* * *| "<<endl; cout<<" | | __|_____________|__ |* *|* *|* * *| "<<endl; cout<<" [_]| _____
| _|___________________|_ |* *|* *|* * *| "<<endl; cout<<" | [___] | |_______________________| ______________
86 CAPTULO 17. PUNTEROS

"<<endl; cout<<" __|_______|_________|_______________________| _________________ "<<endl; cout<<" |____________________


cout<<" |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||"<<endl;//62 cout<<" ||||||||||||||||||||METROPOLI banco universal||||||||||||||||||||"<<endl;
cout<<" ||||||||||||Sistema Integral para el Manejo de COLAS|||||||||||||"<<endl; cout<<" |||||||||||||||||||||||||||||SIMCOLAS||||||||||||||||||||||||||||"<<endl;
cout<<" |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||"<<endl; cout<<" |||||||||Creado por: Javier Rodriguez Caracas-Venezuela||||||||||"<<endl;
cout<<" |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||"<<endl; cout<<" Login: "; char usuario[6] = jahvi"; cin.getline(p->uxer,6); if
(strcmp(p->uxer,usuario) == 0) { cout<<"Bienvenid@ al sistema "<<endl; menu(); cout<<""<<endl; }else{ cout<<"Acceso
Denegado. Consulte al Admisnistrador "<<endl; } system(PAUSE); return EXIT_SUCCESS; } void menu() { short a;
do { cout<<""<<endl; cout<< 0 - Agregar Cliente"<<endl; cout<< 1 - Agregar Cliente V.I.P"<<endl; cout<< 2 - So-
licitar Numero"<<endl; cout<< 3 - Mostrar Cola"<<endl; cout<< 4 - Mostrar Cola V.I.P"<<endl; cout<< 5 - Salir
del Sistema"<<endl; cout<<""<<endl; ush(stdout); cout<<"Opcion #:"; cin>>a; cout<<""<<endl; if (a == 5) exit(1);
switch(a){ case 0 : Insertar(); break; case 1 : insertar_vip(); break; case 2 : solicitar_numero(); break; case 3 : mos-
trar_cola(); break; case 4 : mostrar_colavip(); break; case 5 : salir(); break; default : puts(Usted no ha seleccionado nada,
porfavor seleccione algo\n); break; }; } while (1); } void Insertar(){ insertar(&cola_n); } void insertar_vip(){ inser-
tar(&cola_vip); } void mostrar_cola(){ mostrar_cola(cola_n); } void mostrar_colavip(){ mostrar_cola_vip(cola_vip); }
void solicitar_numero(){ pop(&cola_n); if(cola_vip!=NULL){ pop_vip(&cola_vip); }else{ cout<<"No hay cliente V.I.P
en cola ((((((SIMCOLAS))))))"<<endl; } } void salir(){ }
Captulo 18

Estructuras II

Editores:
Oscar E. Palacios

87
Captulo 19

Introduccin

Algunos autores comienzan por denir los conceptos de estructura de datos a raiz de estructuras conocidas como listas.
En el mismo contexto, suele suceder que a dichas listas tambin se les conoce como secuencias y/o colecciones de datos.
Hay que decir que dichos autores estn (en parte) en lo correcto, ya que una lista (de cualquier tipo) es una estructura
ideada con el propsito de albergar datos agrupados bajo un mismo nombre. Al respecto, podemos pensar que las listas son
como arreglos de datos, es decir, para hacer una introduccin al manejo y programacin de listas encadenadas podemos
tomar como punto de partida a los arreglos estticos. Es as como en esta secccin se descubrir la forma de operacin
de tres tipos comnes de listas conocidas como: PILAS, COLAS Y DOBLE COLA (STACK, QUEUE, DQUEUE).
En programacin, el uso de listas es una prctica tan extendida que lenguajes tales como (por ejemplo) Java, Python y
C++ soportan los mecanismos necesarios para trabajar con estructuras de: Vectores, Pilas, Colas, Listas, etc. En C++,
los programadores que usen Dev-Cpp ( Bloodshed.software -Dev-C++ ) pueden aprovechar las ventajas que ofrecen las
STL (Standard Templates Libraries) dentro de la cual se pueden encontrar plantillas para la manipulacin de listas tales
como: Vectores, Listas, Sets, Maps, etc. Por otro lado, los usuarios de Borland C++ ( Turbo C++ version 1.01 ) pueden
hacer uso de la CLASSLIB, misma que posee las librerias para los propsitos mencionados.
Nota: En las siguientes secciones se presentarn seis programas, tres para simular listas basadas en arreglos estticos y tres
para simular listas por medio de enlaces dinmicos (punteros). En cuanto al material incluido se debe hacer las siguientes
declaraciones:

1. Puesto que el material es puramente didctico, cada programa se escribe en un mismo archivo. La idea es no perder
de vista el objetivo. Los entendidos sabrn que normalmente se deben escribir archivos de cabecera, archivos de
implementacion y archivos de prueba por separado.

2. Para cada una de las clases creadas en los programas se han elegido nombres en ingles. La idea es que gran parte de
la documentacin e implementacin referente a listas est en dicho idioma, as, se le da al estudiante la idea bsica
de como operar con las libreras soportadas por los compiladores Dev-Cpp, Borlan C++, y otros.

3. Igual, se debe observar que los mtodos de las clases tienen nombres en ingles, y que con el objetivo de establecer
cierta estandarizacin todas las clases poseen los mismos mtodos, aunque cada una de ellas implementa los mismos
a su manera.

19.1 Pilas o Stacks


Una PILA es una estructuras en donde cada elemento es insertado y retirado del tope de la misma, y debido a esto el
comportamiento de un una pila se conoce como LIFO (ltimo en entrar, primero en salir ).
Un ejemplo de pila o stack se puede observar en el mismo procesador, es decir, cada vez que en los programas aparece
una llamada a una funcin el microprocesador guarda el estado de ciertos registros en un segmento de memoria conocido
como Stack Segment, mismos que sern recuperados al regreso de la funcin.

88
19.1. PILAS O STACKS 89

19.1.1 Pila en arreglo esttico

En el programa que se ver en seguida, se simula el comportamiento de una estructura de pila. Aunque en el mismo se
usa un arreglo esttico de tamao jo se debe mencionar que normalmente las implementaciones hechas por fabricantes
y/o terceras personas se basan en listas dinmicas o enlazadas.
Para la implementacin de la clase Stack se han elegido los mtodos:
put(), poner un elemento en la pila get(), retirar un elemento de la pila empty(), regresa 1 (TRUE) si la pila esta vacia
size(), nmero de elementos en la pila El atributo SP de la clase Stack es el puntero de lectura/escritura, es decir, el SP
indica la posicin dentro de la pila en donde la funcin put() insertar el siguiente dato, y la posicin dentro de la pila de
donde la funcin get() leer el siguiente dato. Cada vez que put() inserta un elemento el SP se decrementa. Cada vez que
get() retira un elemento el SP se incrementa.
En el siguente ejemplo se analiza lo que sucede con el SP (puntero de pila) cuando se guardan en la pila uno por uno los
caracteres 'A', 'B', 'C' y 'D'. Observe que al principio el SP es igual al tamao de la pila.
Llenando la pila.
SP | +---+---+---+---+---+ | | | | | | al principio (lista vacia) +---+---+---+---+---+ SP | +---+---+---+---+---+ push('A'); | | |
| | A | despus de haber agregado el primer elemento +---+---+---+---+---+
...
SP | +---+---+---+---+---+ | | D | C | B | A | despus de haber agregado cuatro elementos +---+---+---+---+---+
Vaciando la pila.
SP | +---+---+---+---+---+ pop(); | | D | C | B | A | despus de haber retirado un elemento +---+---+---+---+---+
...
SP | +---+---+---+---+---+ | | D | C | B | A | despus de haber retirado todos los elementos +---+---+---+---+---+ Nota:
observe que al nal la lista est vacia, y que dicho estado se debe a que el puntero est al nal de la pila y no al hecho de
borrar fsicamente cada elemento de la pila.
Ejemplo: Pila basada en un arreglo esttico
#include <iostream> using namespace std; #dene STACK_SIZE 256 /* capacidad mxima */ typedef char arreglo[STACK_SIZE];
class Stack { int sp; /* puntero de lectura/escritura */ int items; /* nmero de elementos en lista */ int itemsize; /* tamao
del elemento */ arreglo pila; /* el arreglo */ public: // constructor Stack() { sp = STACK_SIZE-1; items = 0; itemsize =
1; } // destructor ~Stack() {}; /* regresa el nmero de elementos en lista */ int size() { return items; } /* regresa 1 si no
hay elementos en la lista, o sea, si la lista est vacia */ int empty() { return items == 0; } /* insertar elemento a la lista
*/ int put(char d) { if ( sp >= 0) { pila[sp] = d; sp --; items ++; } return d; } /* retirar elemento de la lista */ int get() {
if ( ! empty() ) { sp ++; items --; } return pila[sp]; } }; // n de clase Stack // probando la pila. // Nota: obseve cmo los
elementos se ingresan en orden desde la A hasta la Z, // y como los mismos se recupern en orden inverso. int main() {
int d; Stack s; // s es un objeto (instancia) de la clase Stack // llenando la pila for (d='A'; d<='Z'; d++) s.put(d); cout <<
Items =" << s.size() << endl; // vaciando la pila while ( s.size() ) cout << (char)s.get() << " "; cout << "\nPara terminar
oprima <Enter>..."; cin.get(); return 0; } }

19.1.2 Pila dinmica

En el siguiente programa se presenta una implementacin de una estructura dinmica tipo pila o stack. Es importante
hacer notar que, a diferencia de una pila basada en un arreglo esttico, una pila enlazadada dinmicamente no posee de
forma natural el mecanismo de acceso por ndices, en ese sentido, el programador puede crear los algoritmos necesarios
para permitir tal comportamiento. En la clase que presentaremos en el ejemplo no se ha implementado el mecanismo de
acceso por ndices, ya que la misma se presenta como una alternativa para la simulacin de una pila o stack.
Uno de los puntos ms destacables en cuando al uso de listas enlazadas dinmicamente es el hecho de crear estructuras
conocidas como nodos. Un nodo es una especie de eslabn ( similar al de una cadena de bicicleta ), es decir, cada nodo
90 CAPTULO 19. INTRODUCCIN

se enlaza con otro a travs de un puntero que apunta a una estructura del mismo tipo que el nodo. Por ejemplo, para crear
una estructura de nodo para almacenar enteros y a la vez para apuntar a otro posible nodo podemos emplear la sintaxis:
struct nodo { int data; nodo *siguiente; };

observe que con la declaracin anterior estamos creando el tipo estructurado nodo, mismo que posee a los miembros: data
para guardar valores enteros, y siguiente para apuntar o enlazar a un supuesto siguiente nodo.
Ya que las listas dinmicas inicialmente se encuentran vacias, y ms an, una lista dinmica no posee una direccin esta-
blecida en tiempo de compilacin ya que las direccin de memoria que ocupar cada uno de los elementos se establecer
en tiempo de ejecucin, entonces cmo determinar la condicin de vacio ?. En nuestro ejemplo usaremos un contador
( ITEMS ) que dicho sea de paso, si ITEMS = 0, entonces la lista est vacia. ( la condicin de vacio tambin podra
determinarse al vericar el SP, es decir, si el SP = NULL, signica que la lista no posee elementos ).
Al hacer un anlisis previo de los eventos que acontecern en la pila y su puntero de lectura y escritura (SP, que en esta
ocasin es una estructura tipo nodo), se tiene lo siguiente:

1) Al principio la lista est vacia, en ese caso el SP es igual a NULL y, en consecuencia, el puntero next
tambin es NULL.

SP = NULL +------+------+ | ???? | next |--> NULL +------+------+

2) Despus de agregar el primer elemento la situacin se vera as:

SP = asignado 1 +------+------+ | data | next |--> NULL +------+------+

3) Despus de agregar otro elemento la situacin se vera as:

SP = asignado 2 1 +------+------+ +------+------+ | data | next |--> | data | next |--> NULL +------+------+ +------+------+
Ejemplo: Pila basada en un arreglo dinmico
/*---------------------------------------------------------------+ + ejemplo de una pila ( STACK ) enlazada dinmicamente +
+ + + Autor: Oscar E. Palacios + + email: oscarpalacios1@yahoo.com.mx + + + + Maniesto: + + Este programa pue-
de distribuirse, copiarse y modicarse de + + forma libre. + +---------------------------------------------------------------*/
#include <iostream> //#include <conio.h> using namespace std; /* tipo de dato que contendr la lista */ typedef char
DATA_TYPE; // declaracin de estructura nodo struct nodo { DATA_TYPE data; nodo *next; }; class StackDin { // atri-
butos int ITEMS; /* nmero de elementos en la lista */ int ITEMSIZE; /* tamao de cada elemento */ nodo *SP; /* puntero
de lectura/escritura */ public: // constructor StackDin() : SP(NULL), ITEMS(0), ITEMSIZE(sizeof(DATA_TYPE)) {}
// destructor ~StackDin() {} /* agregar componente a la lista */ DATA_TYPE put(DATA_TYPE valor) { nodo *temp;
temp = new nodo; if (temp == NULL) return 1; temp->data = valor; temp->next = SP; SP = temp; ITEMS ++; re-
turn valor; } int empty() { return ITEMS == 0; } /* retirar elemento de la lista */ DATA_TYPE get() { nodo *temp;
DATA_TYPE d; if ( empty() ) return 1; d = SP->data; temp = SP->next; if (SP) delete SP; SP = temp; ITEMS --;
return d; } }; // n de la clase StackDin /* punto de prueba para la clase StackDin */ int main() { //clrscr(); StackDin s;
DATA_TYPE d; for (d='A'; d<='Z'; d++) s.put(d); while ( ! s.empty() ) cout << (DATA_TYPE)s.get() << " "; cout <<
"\nPara terminar presione <Enter>..."; cin.get(); return 0; }

19.2 Colas o Queues


Una cola sencilla es una estructura en donde cada elemento es insertado inmediatamente despus del ltimo elemento
insertado; y donde los elementos se retiran siempre por el frente de la misma, debido a esto el comportamiento de un una
cola se conoce como FIFO (primero en entrar, primero en salir).
Un ejemplo a citar de cola es el comportamiento del buer del teclado.
19.2. COLAS O QUEUES 91

Cuando en el teclado se oprime una tecla, el cdigo del carcter ingresado es trasladado y depositado en una rea de
memoria intermedia conocida como el buer del teclado, para esto el microprocedador llama a una rutina especca.
Luego, para leer el carcter depositado en el buer existe otra funcin, es decir, hay una rutina para escribir y otra para
leer los caracteres del buer cada una de las cuales posee un puntero; uno para saber en donde dentro del buer se escribir
el siguiente cdigo y otro para saber de donde dentro del buer se leer el siguiente cdigo.

19.2.1 Cola en un arreglo esttico

En el programa que se ve en seguida, se simula el comportamiento de una estructura de cola simple. Aunque en el mismo se
usa un arreglo esttico de tamaoo jo se debe mencionar que normalmente las implementaciones hechas por fabricantes
y/o terceras personas se basan en listas dinmicas o dinamicamente enlazadas.
Para la implementacin de la clase Queue se han elegido los mtodos:
put(), poner un elemento en la cola get(), retirar un elemento de la cola empty(), regresa 1 (TRUE) si la cola est vacia
size(), nmero de elementos en la cola El atributo cabeza de la clase Queue es el puntero de lectura. El atributo cola de
la clase Queue es el puntero de escritura.
Es decir, la cola indica la posicin dentro de la lista en donde la funcin put() insertar el siguiente dato, y la cabeza indica
la posicin dentro de la lista de donde la funcin get() leer el siguiente dato.
Cada vez que put() inserta un elemento la cola se incrementa. Cada vez que get() retira un elemento la cabeza se incre-
menta.
En el siguente ejemplo se analiza lo que sucede con la cola y la cabeza (punteros de escritura y de lectura de la Lista)
cuando se guardan en la cola uno por uno los caracteres 'A', 'B', 'C' y 'D'. Observe que al principio: cola = cabeza = cero.
Llenando la cola.
cola | +---+---+---+---+---+ | | | | | | al principio +---+---+---+---+---+ | cabeza cola | +---+---+---+---+---+ put('A'); | A | |
| | | despus de haber agregado el primer elemento +---+---+---+---+---+ | cabeza
...
cola | +---+---+---+---+---+ | A | B | C | D | | despus de haber agregado cuatro elementos +---+---+---+---+---+ | cabeza
Vaciando la cola.
cabeza | +---+---+---+---+---+ | A | B | C | D | | antes de haber retirado elementos +---+---+---+---+---+ cabeza | +---+---
+---+---+---+ get(); | A | B | C | D | | despus de haber retirado un elemento +---+---+---+---+---+
...
cabeza | +---+---+---+---+---+ al nal | A | B | C | D | | despus de haber retirado todos los elementos +---+---+---+---+---+
| cola
Observese que al nal el cabeza apunta hacia el mismo elemento que la cola, es decir, la cola vuelve a estar vacia. Puesto
que la cola que estamos proyectando reside en un arreglo esttico los componentes del arreglo an estn dentro de la
misma, salvo que para su recuperacin se debera escribir otro mtodo. En una cola dinmica (como se demostrar ms
adelante) los elementos retirados de la misma se eliminan de la memoria y podra no ser posible su recuperacin posterior.
Nota: En el programa que aparece en seguida, al tipo de lista implementado por la clase Queue se le conoce como lista
circular debido al comportamiento de sus punteros. Es decir si los mtodos para escribir o leer detectan que el puntero
correspondiente ha sobrepasado el tamao mximo de elementos permitidos dentro de la cola, ste es puesto a cero.
Ejemplo: cola en un arreglo esttico
/*---------------------------------------------------------------+ + ejemplo de una cola (QUEUE) basada en un arreglo esttico
+ + + + Autor: Oscar E. Palacios + + email: oscarpalacios1@yahoo.com.mx + + + + Maniesto: + + Este programa
puede distribuirse, copiarse y modicarse de + + forma libre. + +---------------------------------------------------------------
*/ #include <iostream.h> #dene MAX_SIZE 256 /* capacidad mxima */ typedef char almacen[MAX_SIZE]; class
Queue { int cabeza; /* puntero de lectura */ int cola; /* puntero de escritura */ int ITEMS; /* nmero de elementos en
la lista */ int ITEMSIZE; /* tamao de cada elemento */ almacen alma; /* el almacen */ public: // constructor Queue()
92 CAPTULO 19. INTRODUCCIN

{ cabeza = 0; cola = 0; ITEMS = 0; ITEMSIZE = 1; } // destructor ~Queue() {} // regresa 1 (true) si la lista est vacia
int empty() { return ITEMS == 0; } // insertar elemento a la lista int put(int d) { if ( ITEMS == MAX_SIZE) return
1; if ( cola >= MAX_SIZE) { cola = 0; } alma[cola] = d; cola ++; ITEMS ++; return d; } // retirar elemento de la
lista int get() { char d; if ( empty() ) return 1; if ( cabeza >= MAX_SIZE ) { cabeza = 0; } d = alma[cabeza]; cabe-
za ++; ITEMS --; return d; } // regresa el nmero de elementos en lista int size() { return ITEMS; } }; // n de la clase
Queue // probando la cola int main() { int d; Queue q; for (d='A'; d<='Z'; d++) q.put(d); cout << Items = " << q.size() <<
endl; while ( q.size() ) { cout << (char)q.get() << " "; } cout << "\nPara terminar oprima <Enter> ..."; cin .get(); return 0; }

Ejemplo: cola en un arreglo dinmico


/*---------------------------------------------------------------+ + ejemplo de una cola (QUEUE) basada en un arreglo dinmi-
co + + + + Autor: Oscar E. Palacios + + email: oscarpalacios1@yahoo.com.mx + + + + Maniesto: + + Este programa
puede distribuirse, copiarse y modicarse de + + forma libre. + +---------------------------------------------------------------*/
#include <iostream> using namespace std; typedef char DATA_TYPE; struct nodo { DATA_TYPE data; nodo *next;
}; class QueueDin { // atributos int ITEMS, ITEMSIZE; nodo *cola, *cabeza; public: // constructor QueueDin() : co-
la(NULL), cabeza(NULL), ITEMS(0), ITEMSIZE(sizeof(DATA_TYPE)) {} // destructor ~QueueDin() {} /* agregar
componente a la lista */ DATA_TYPE put(DATA_TYPE valor) { nodo *temp; temp = new nodo; if (temp == NULL)
return 1; ITEMS ++; temp->data = valor; temp->next = NULL; if (cabeza == NULL) { cabeza = temp; cola = temp; }
else { cola->next = temp; cola = temp; } return valor; } // regresa 1 (true) si la lista est vacia int empty() { return ITEMS
== 0; } /* retirar elemento de la lista */ DATA_TYPE get() { nodo *temp; DATA_TYPE d; if ( empty() ) return 1; d
= cabeza->data; temp = cabeza->next; if (cabeza) delete cabeza; cabeza = temp; ITEMS --; return d; } }; // n de la clase
QueueDin /* punto de prueba */ int main() { QueueDin s; DATA_TYPE d; // llenando la cola for (d='A'; d<='Z'; d++)
{ s.put(d); cout << d << " "; } cout << endl; // vaciando la cola while ( ! s.empty() ) cout << (DATA_TYPE)s.get() << "
"; cout << "\nPara terminar presione <Enter>..."; cin.get(); return 0; }

19.3 Colas de doble enlace


Una cola doble es una estructuras en donde cada elemento puede ser insertado y recuperado por la parte del frente (cabeza)
o por la parte de atras (cola) de la lista. A diferencia de una cola sencilla, en donde solo se necesitan un mtodo para leer
y otro para escribir componentes en la lista, en una doble cola debe haber dos mtodos para leer ( uno para leer por el
frente y uno para leer por atras ) y dos mtodos para escribir ( uno para escribir por el frente y uno para escribir por atras
).
En el programa que se ver en seguida, se simula el comportamiento de una estructura de cola doble con base en un
arreglo esttico. En dicho programa se declara e implementa la clase SDQueue con los siguientes mtodos:
put_front(), poner un elemento en el frente de la cola put_back(), poner un elemento en la parte tracera de la cola
get_front(), retirar un elemento de la parte frontal de la cola get_back(), retirar un elemento de la parte tracera de la cola
empty(), regresa 1 (TRUE) si la cola est vacia size(), nmero de elementos en la cola
Nota: observe que para los mtodos put_front() y get_front() se hace uso de la funcin memmove(), esto es necesario
debido al hecho de que put_front() tiene que mover una posicin hacia atras todos los elementos en la lista antes de
insertar el componente indicado; por otro lado, la funcin get_front() tiene que mover una posicin hacia adelante a todos
los elementos que le siguen al primer elemento.
Ejemplo: doble cola en un arreglo esttico
/*------------------------------------------------------------------+ + ejemplo de una cola doble (DQUEUE) basada en un arre-
glo est tico + + + + Autor: Oscar E. Palacios + + email: oscarpalacios1@yahoo.com.mx + + + + Maniesto: + + Este
programa puede distribuirse, copiarse y modicarse de + + forma libre. + +----------------------------------------------------
--------------*/ #include <iostream.h> #include <mem.h> // por memmove // using namespace std; #dene MAX_SIZE
256 #dene t_error 1; typedef int DATA_TYPE; // mximo nmero de elementos typedef int almacen[MAX_SIZE];
class SDQueue { // atributos int itemsize; // tamao de cada elemento int items; // nmero de elementos int cola, cabeza; //
punteros de lectura y escritura almacen alma; // el almacen o arreglo public: // constructor SDQueue() : cola(0), cabeza(0),
items(0), itemsize(sizeof(DATA_TYPE)) {} // destructor ~SDQueue() {} int empty() { return items == 0; } int size() {
19.3. COLAS DE DOBLE ENLACE 93

return items; } /* agregar componente en la parte tracera de la lista */ DATA_TYPE put_back(DATA_TYPE valor) { if
(items == MAX_SIZE) return t_error; alma[cola] = valor; items ++; cola ++; return valor; } /* agregar componente en
la parte delantera de la lista */ DATA_TYPE put_front(DATA_TYPE valor) { if (items == MAX_SIZE) return t_error;
memmove((void *)&alma[cabeza+1], (void*)&alma[cabeza], items*itemsize); alma[cabeza] = valor; items ++; cola ++;
return valor; } /* retirar elemento de la parte frontal de la lista */ DATA_TYPE get_front() { DATA_TYPE d; if ( em-
pty() ) return t_error; items --; cola --; d = alma[cabeza]; memmove((void*)&alma[cabeza], (void*)&alma[cabeza+1],
items*itemsize); return d; } /* retirar elemento de la parte tracera de la lista */ DATA_TYPE get_back() { DATA_TYPE
d; if ( empty() ) return t_error; items--; cola --; d = alma[cola]; return d; } }; // n de la clase SDQueue /* punto de prue-
ba */ int main() { SDQueue s; DATA_TYPE d; for (d='A'; d<='Z'; d++) s.put_back(d); while ( ! s.empty() ) cout <<
(char)s.get_front() << " "; cout << "\nPara terminar presione <Enter>..."; cin.get(); return 0; }

Una cola doblemente encadenada es una estructuras en donde cada elemento puede ser insertado y recuperado por la parte
del frente (cabeza) o por la parte de atras (cola) de la lista. A diferencia de una cola sencilla, en donde solo se necesita un
puntero a un siguiente elemento, la estructura del nodo para una doble cola debe poseer un puntero a un posible siguiente
elemento y un puntero a otro posible anterior elemento. Por ejemplo, para crear una estructura de nodo con doble enlace
para coleccionar nmeros enteros podemos usar la sintaxis:
struct nodo { int data; nodo *next, *prev; };
Grcamente podemos imaginar la estructura anterior como:
+------+------+------+ <--| prev | data | next |--> +------+------+------+
En el programa que se ver en seguida, se simula el comportamiento de una estructura de cola de doble enlace. Para la
implementacin de la clase DDqueue en el programa se han elegido los mtodos:
put_front(), poner un elemento en el frente de la cola put_back(), poner un elemento en la parte tracera de la cola
get_front(), retirar un elemento de la parte frontal de la cola get_back(), retirar un elemento de la parte tracera de la cola
empty(), regresa 1 (TRUE) si la cola est vacia size(), nmero de elementos en la cola
/*---------------------------------------------------------------+ + ejemplo de una cola doblemente enlazada (Dqueue) basada en
un + + arreglo dinmico + + + + Autor: Oscar E. Palacios + + email: oscarpalacios1@yahoo.com.mx + + + + Maniesto:
+ + Este programa puede distribuirse, copiarse y modicarse de + + forma libre. + +---------------------------------------
------------------------*/ #include <iostream.h> #include <conio.h> // using namespace std; typedef char DATA_TYPE;
struct nodo { DATA_TYPE data; nodo *next, *prev; }; class DDqueue { int itemsize, items; nodo *cola, *cabeza; pu-
blic: // constructor DDqueue() : cola(NULL), cabeza(NULL), items(0), itemsize(sizeof(DATA_TYPE)) {} // destructor
~DDqueue() {} /* agregar componente en la parte tracera de la lista */ DATA_TYPE put_back(DATA_TYPE valor) {
nodo *temp; temp = new nodo; if (temp == NULL) return 1; temp->data = valor; items ++; if (cabeza == NULL ) {
temp->next = NULL; temp->prev = NULL; cabeza = temp; cola = temp; } else { cola->next = temp; temp->prev = cola;
cola = temp; cola->next = NULL; } return valor; } /* agregar componente en la parte frontal de la lista */ DATA_TYPE
put_front(DATA_TYPE valor) { nodo *temp; temp = new nodo; if (temp == NULL) return 1; temp->data = valor;
items ++; if (cabeza == NULL ) { temp->next = NULL; temp->prev = NULL; cabeza = temp; cola = temp; } else {
cabeza->prev = temp; temp->next = cabeza; cabeza = temp; cabeza->prev = NULL; } return valor; } // regresa true si la
lista est vacia int empty() { return items == 0; } /* retirar elemento de la parte frontal lista */ DATA_TYPE get_front()
{ nodo *temp; DATA_TYPE d; if ( empty() ) return 1; items --; d = cabeza->data; temp = cabeza->next; if (cabeza)
delete cabeza; cabeza = temp; return d; } /* retirar elemento de la parte tracera de la lista */ DATA_TYPE get_back() {
nodo *temp; DATA_TYPE d; if ( empty() ) return 1; items--; d = cola->data; temp = cola->prev; if (cola) delete cola;
cola = temp; return d; } }; // n de la clase DDqueue /* punto de prueba */ int main() { clrscr(); DDqueue s; DATA_TYPE
d; // insertando elementos en la parte tracera for (d='A'; d<='Z'; d++) s.put_back(d); // insertando en la parte delantera
for (d=9; d>=0; d--)s.put_front(d+'0'); // vaciando la lista while ( ! s.empty() ) cout << (DATA_TYPE)s.get_front() <<
" "; cout << "\nPara terminar presione <Enter>..."; cin.get(); return 0; }
Captulo 20

Plantillas

Editores:
Oscar E. Palacios

94
Captulo 21

Plantillas

21.1 Introduccin
Con el objeto de explicar la razn de la necesidad de la existencia de las plantillas debemos reexionar sobre tres pa-
radgmas de programacin anteriores, estas son: programacin clsica o procedimental, programacin estructurada y
programacin orientada al objeto POO.

Programacin clsica
En el tipo de programacin conocida como clsica existe una clara diferenciacin entre los datos y su manipulacin,
es decir, entre los datos y el conjunto de algoritmos para manejarlos. Los datos son tipos muy simples y generalmente
los algoritmos se agrupan en funciones orientadas de forma muy especca a los datos que deben manejar. Por ejem-
plo, si se escribe una funcin ( sort ) para ordenar en forma ascendente o descendente los nmeros contenidos en un
arreglo de nmeros enteros, dicha funcin puede aplicarse a cualquier arreglo de enteros ms no a arreglos de otro tipo.
An as, la programacin clsica provee el soporte necesario para la reutilizacin de cdigo ya que el cdigo de la fun-
cin se escribe solamente una vez y su reutilizacin se da por medio de un mecanismo conocido como llamada de funcin.

Programacin estructurada
En la medida en que los datos que haba de manipular se iban haciendo cada vez ms complejos se busco la forma de
agruparlos de alguna manera bajo un mismo nombre, es as como surjen las estructuras de datos. Muchos autores se
reeren a la programacin estructura como a la suma de funciones y/o procedimientos ms estructura de datos.

Programacin Orientada al Objeto


La Programacin Orientada al Objeto ( POO ) introduce nuevas facilidades y se extiende el concepto de dato, permitien-
do que existan tipos ms complejos, es decir, si la programacin estructurada establece las bases para la manipulacin de
funciones y datos estructurados, la POO establece las bases para manipular datos y funciones como un solo objeto. Esta
nueva habilidad viene acompaada por ciertas mejoras adicionales: la posibilidad de ocultacin de determinados detalles
internos irrelevantes para el usuario y la capacidad de herencia simple o mltiple.

Notas: El ocultamiento de cdigo as como la herencia estn presentes ( en una forma simple ) en la
programacin estructurada, y los mismos adquieren mucha ms relevancia en la POO. Por ejemplo, en la
programacin estructurada si usted escribe una librera de funciones, al usuario de dicha librera solamente
le informar de la existencia de tal o cual funcin, as como el objetivo y la forma correcta para comunicarse
con estas, pero no es necesario que se le explique el funcionamiento interno de las mismas. Por otro lado,
es bien conocido que lenguajes tales como Pascal y C, por ejemplo, dan el soporte para la creacin de datos
estructurados ( Record en Pascal, y struct en C ), y que dichas estructuras pueden contener a otras estructuras
previamente denidas. De tal manera vemos como a los usuarios de las funciones se les oculta el cdigo de

95
96 CAPTULO 21. PLANTILLAS

las mismas, y que una estructura que contiene a otra hereda los miembros de la estructura contenida.

Programacin genrica
La programacin genrica est mucho ms centrada en los algoritmos que en los datos, y su postulado fundamental puede
sintetizarse en una palabra: generalizacin. Signica que, en la medida de lo posible, los algoritmos deben ser parametri-
zados al mximo y expresados de la forma ms independiente posible de detalles concretos, permitiendo as que puedan
servir para la mayor variedad posible de tipos y estructuras de datos.

Con el objetivo de mostrar de una manera practica las diferencias entre los tres tipos de programacin mencionados arriba
tomaremos como base el modelo de una vieja estructura de datos amiga de los programadores, me reero a un array,
tambin conocida por muchos como arreglo, tabla o lista. Para no entrar en polemicas de estndarizacin de nombrado
en este captulo diremos que la estructura modelo con la que trabajaremos es un vector ( arreglo unidimensional ).
Pues bien, dado un vector cualquiera se presenta la necesidad de crear un cierto nmero de funciones que sean las encar-
gadas de la manipulacin de los datos dentro del vector. Para comenzar, podemos pensar en las funciones:

1. mostrar, para desplegar o imprimir los elementos del vector.

2. ordenar, para ordenar los elementos del vector.

3. buscar, para determinar la presencia o ausencia de un determinado elemento dentro del vector.

4. insertar, para agregar componentes al vector.

5. eliminar, para quitar componentes del vector.

6. capacidad, para obtener la capacidad mxima del vector.

7. cuenta, para obtener el nmero de elementos actuales contenidos por el vector.

Al hacer un anlisis muy detallado del problema planteado y al tratar de resolver el mismo mediante la programacin
clsica, veremos que, si bien es cierto que se puede llegar a la solucin, para lograrlo se tendran que establecer una serie
de medidas que nos permitiern controlar de alguna manera detalles tales como: el total de elementos soportados por el
vector y el nmero de elementos actuales contenidos por el vector. Por ejemplo, con la instruccin:
int vector[120];
se crea un arreglo con capacidad para 120 componentes de tipo entero . Ahora bien, el compilador sabe perfectamente
que deber reservar un espacio de 120 enteros para la memoria del vector, pero no se garantiza que cualquier funcin trate
de leer o escribir datos fuera de los limites del vector. Es decir, nuestro programa no podra saber el tamao del vector
a menos que usaramos algn truco, por ejemplo usar el primer elemento del vector para contener el tamao del mismo.
Otra forma de alcanzar una solucin, sera que a cada una de las funciones que tengan que ver con el vector se le pasar
los parmetros: puntero de vector y nmero de elementos en el vector, de tal manera que, por ejemplo, la funcin mostrar
podra declararse como:
int mostrar(int *vector, int cuenta);
21.2. UN PASO HACIA ADELANTE 97

21.2 Un paso hacia adelante

Si continuamos en el mbito de la programacin clsica podemos dar un paso ms si es que nos valemos de tipos es-
tructurados ms elaborados. Por ejemplo, podemos denir una estructura llamada vector la cual posea los miembros:
capacidad, cuenta y data como se muestra en seguida:
typedef struct vector { int capacidad; int cuenta; int *data; };

De tal manera que podriamos escribir funciones que operen sobre un solo parmetro del tipo estructurado vector. Por
ejemplo, la funcin mostrar podra declararse como:
int mostrar(vector *v);
En este punto tendramos que detenernos y pensar en lo siguiente:
La estructura vector ( denida arriba con typedef ) es solamente un nuevo tipo de dato, es decir, podemos declarar tantas
copias ( variables ) de la misma como sean necesarias, pero carece de un mtodo constructor adecuado. Por ejemplo, la
declaracin:
vector nombre_var;
es vlida siempre y cuando que el tipo vector ya haya sido denido, pero la variable nombre_var contendr slo basura
hasta que no se establezcan los valores adecuados para cada uno de sus miembros ( capacidad, cuenta y data ).

21.3 Una mejor solucin

Para el tipo de problemas como en el ejemplo vector y otros similares, surge la POO, misma que facilita en gran medida
la solucin del mismo, pero an queda pendiente la resolucin a otro problema, es decir, hasta aqu hemos mencionado la
estructura vector como un contenedor de nmeros enteros, pero un vector podra contener caracteres, nmeros de punto
otante y otros tipos estructurados; y los mismos algoritmos usados para ( mostrar, ordenar, buscar, etc. ) empleados en
un vector de enteros, tendran su aplicacin sobre vectores de cualquier tipo. Es as como surge lo que se conoce como
PLANTILLAS o lo que es lo mismo, la programacin genrica.

21.4 La clase vector desde la perspectiva de la POO

Con el objetivo de mostrar en la prctica los conceptos que hemos venido mencionando presentaremos un pequea im-
plementacin de la clase vector. Se debe aclarar que la implementacin de la misma se har para vectores contenedores
de datos tipo int solamente. Para simplicar el ejemplo, para la clase vector solamente se implementarn los mtodos
mostrar(), ordenar() e insertar(), as como un mtodo constructor base y un mtodo destructor.
#include <iostream> #include <cstdio> #include <ctime> using namespace std; #dene VECTOR_DEFAULT_SIZE 128
class vector { // atributos int capacidad; int cuenta; int *data; public: // constructor base vector() { capacidad = VEC-
TOR_DEFAULT_SIZE; cuenta = 0; data = new int[VECTOR_DEFAULT_SIZE]; } // destructor ~vector() { delete[]
data; } // despliega todos los elementos contenidos por el vector void mostrar() { for (int t = 0; t < cuenta; t++) cout
<< elemento " << t << ", valor " << data[t] << endl; } // inserta un elemento al vector int insertar(int d) { if (cuenta
== capacidad) return 1; data[cuenta] = d; cuenta ++; return cuenta; } // ordena en forma ascendente los elementos del
vector void ordenar() { int i, j, temp; int n = cuenta; i = 0; while (i < n ) { for ( j = i ; j < n-1; j++) if ( data[j] >
data[j+1] ) { temp = data[j]; data[j] = data[j+1]; data[j+1] = temp; } n --; } } }; #dene MAX 10 int main() { vector
v; srand( time(NULL) ); for (int r = 0; r < MAX; r++) v.insertar( rand() % 100); cout << "\nvector v sin ordenar\n";
v.mostrar(); v.ordenar(); cout << "\nvector v ordenado\n"; v.mostrar(); getchar(); return 0; }
98 CAPTULO 21. PLANTILLAS

21.5 Una plantilla para la clase vector


Una vez que se llegado al entendimiento de la programacin estructurada as como de la programacin orientada al objeto,
se puede observar que, si bien es cierto que ambas ofrecen soluciones a problemas fundamentales tambin es cierto que
las soluciones se presentan como casos especializados. Esta ltima armacin la podemos ilustrar si nos jamos en el
caso del programa presentado anteriormente, en dicho programa se presenta la clase vector como un contenedor especial
para nmeros enteros (int), ahora bien, si prestamos an aun ms atencin podemos llegar a la conclusin de que todos
los algoritmos o funciones aplicadas en la clase vector pueden operar con cualquier otro tipo de datos y, por lo tanto, la
nica diferencia sera el tipo de datos contenidos por el vector. De tal manera que aparece lo que se llama generalizacin o
programacin genrica y esta, a su vez, nos permite la creacin de plantillas basadas en una lgica operatoria previamente
concebida.
Con el objetivo de mostrar un ejemplo prctico retomaremos la clase vector del programa de la seccin anterior y crea-
remos, a raiz del mismo, una plantilla. La plantilla resultante tendr la capacidad para crear vectores de cualquier tipo.
#include <iostream> #include <cstdio> #include <ctime> using namespace std; #dene VECTOR_DEFAULT_SIZE
128 template <class T> class vector { // atributos int capacidad; int cuenta; T *data; public: // constructor base vector()
{ capacidad = VECTOR_DEFAULT_SIZE; cuenta = 0; data = new T[VECTOR_DEFAULT_SIZE]; } // destructor
~vector() { delete[] data; } void mostrar(); int insertar(T d); void ordenar(); }; // implementacin del mtodo mostrar
template <class T> void vector<T>::mostrar() { for (int t = 0; t < cuenta; t++) cout << elemento " << t << ", valor "
<< data[t] << endl; } // implementacin del mtodo insertar template <class T> int vector<T>::insertar(T d) { if (cuenta
== capacidad) return 1; data[cuenta] = d; cuenta ++; return cuenta; } // implementacin del mtodo ordenar template
<class T> void vector<T>::ordenar() { T temp; int i, j, n = cuenta; i = 0; while (i < n ) { for ( j = i ; j < n-1; j++) if
( data[j] > data[j+1] ) { temp = data[j]; data[j] = data[j+1]; data[j+1] = temp; } n --; } } #dene TEST 10 int main()
{ // prueba de un vector de nmeros de punto otante vector<double> v; srand( time(NULL) ); for (int r = 0; r < TEST;
r++) v.insertar( (rand() % 10) * 0.5); cout << "\nvector v sin ordenar\n"; v.mostrar(); v.ordenar(); cout << "\nvector v
ordenado\n"; v.mostrar(); // prueba de un vector de nmeros long int vector<long int> v2; srand( time(NULL) ); for (int
r = 0; r < TEST; r++) v2.insertar( (rand() % 100) ); cout << "\nvector v2 sin ordenar\n"; v2.mostrar(); v2.ordenar(); cout
<< "\nvector v2 ordenado\n"; v2.mostrar(); getchar(); return 0; }
Captulo 22

Excepciones

99
Captulo 23

Excepciones. Motivacin histrica

Primeramente, antes de entrar en el tema de las excepciones en programacin, se ha de matizar en el concepto de qu son
las excepciones, vistas desde un punto de vista fuera y dentro del mundo de la programacin.
En el lenguaje humano, una excepcin es un elemento excluyente de una regla, y de forma convencional se ha extendido
esta denicin. En el lenguaje mquina, una excepcin se trata, de forma general, de algo que no se espera que ocurra,
pero que puede ocurrir, similar al tratamiento de errores, pero de los errores en tiempo de ejecucin.
A veces estas excepciones, para una mquina, no son casos que no deberan estar contemplados, tal y como un programador
se lo asigna, sino que pueden ser indicadores para comprobar que realmente todo est marchando bien o no.
En los programas de ordenador hechos en C existi durante mucho tiempo la costumbre de usar el comando goto
(tambin implementada en C++), pero ste se ha ido eliminando progresivamente de casi todos y cada uno de los cdigos
y programas que han ido surgiendo. El signicado de la funcin 'goto' no forma parte del libro actual, pero se pueden
buscar referencias por internet donde se especique con ms detalle qu es.
Como una de las formas de control de errores ms usuales era con goto, se usaron otras variantes, como las aserciones de
cdigo (assertions, en ingls) o, con la llegada de la programacin orientada a objetos, de los comandos try, catch y throw.

100
Captulo 24

Conformacin de los bloques try y catch

Por norma general, los comandos try y catch conforman bloques de cdigo.
Cada uno de estos bloques se recomienda, aunque sea de una nica lnea, envolverlos en llaves, como muestra el siguiente
ejemplo:
// ...cdigo previo... try { // bloque de cdigo a comprobar } catch( tipo ) // Ha ocurrido un suceso en el try que se ha
terminado //la ejecucin del bloque y catch recoge y analiza lo sucedido { // bloque de cdigo que analiza lo que ha
lanzado el try } // ...cdigo posterior...

Generalmente entre el try y el catch no se suele insertar cdigo, pero se insta al lector a que lo intente con su compilador
habitual y que compruebe si hay errores o no de compilacin.

101
Captulo 25

Control de excepciones

Una excepcin es un error que puede ocurrir debido a una mala entrada por parte del usuario, un mal funcionamiento en
el hardware, un argumento invlido para un clculo matemtico, etc. Para remediar esto, el programador debe estar atento
y escribir los algoritmos necesarios para evitar a toda costa que un error de excepcin pueda hacer que el programa se
interrumpa de manera inesperada. C++ soporta una forma ms directa y fcil de ver tanto para el programador como para
los revisores del cdigo en el manejo de excepciones que su smil en el C estndar y esta consiste, tratndose del lenguaje
C++, en el mecanismo try, throw y catch.
La lgica del mecanismo mencionado consiste en:

1. Dentro de un bloque try se pretende evaluar una o ms expresiones y si dentro de dicho bloque se produce un algo
que no se espera se lanza por medio de throw una excepcin, la misma que deber ser capturada por un catch
especco.

2. Puesto que desde un bloque try pueden ser lanzados diferentes tipos de errores de excepcin es que puede haber
ms de un catch para capturar a cada uno de los mismos.

3. Si desde un try se lanza una excepcin y no existe el mecanismo catch para tratar dicha excepcin el programa
se interumpir abruptamente despues de haber pasado por todos los catchs que se hayan denido y de no haber
encontrado el adecuado.

4. Los tipos de excepciones lazados pueden ser de un tipo primitivo tal como: int, oat, char, etc. aunque normal-
mente las exepciones son lanzadas por alguna clase escrita por el usuario o por una clase de las que vienen incluidas
con el compilador.

En el programa que se listar a continuacin muestra un ejemplo de como lanzar una excepcin de tipo int dentro del
bloque try, y cmo capturar la excepcin por medio de catch.
Ejemplo

// Demostracin de los comandos try, throw y catch #include <iostream> // Funcin: main // Recibe: void // Devuelve:
int // En la funcin principal se tratarn los comandos try, throw y catch int main(void) { try // Se intenta hacer el si-
guiente cdigo { // Aqu puede ir ms cdigo... throw 125; //...aunque directamente en este caso se lanza una excepcin.
} catch(int) // Se captura con un catch de enteros (podra usarse long o char, por ejemplo) { std::cout << Ha surgido
una excepcin de tipo entero << std::endl; // y se muestra por pantalla } std::cin.get(); // Y el programa naliza. return 0; }

102
25.1. EXCEPCIONES GENRICAS 103

25.1 Excepciones genricas


Como ya se ha mencionado, los errores pueden deberse a una multitud de situaciones muchas veces inesperadas, por
tal motivo, en C++ existe una forma de manejar excepciones desconocidas ( genricas ) y es buena idea que si se est
escribiendo un controlador de excepciones incluya un catch para capturar excepciones inesperadas. Por ejemplo, en el
siguiente progama se escribe un catch que tratar de capturar cualquier excepcin inesperada.
// Demostracin: try, throw y catch #include <iostream> using namespace std; int main() { try { throw 125; } catch(...) {
cout << Ha ocurrido un error inesperado... << endl; } cin.get(); return 0; }

25.2 Excepciones de clases


Si usted est usando una clase escrita por terceras personas o de las que se incluyen con el compilador y desea utilizar
el mecanismo try, deber conocer el tipo de excepcin lanzado por dicha clase para as poder escribir el catch corres-
pondiente para el tratamiento de dicho error. Por ejemplo, la funcin at() de la clase string ( que ser estudiada ms
adelante ) lanza una excepcin cuando se trata de leer o escribir un componente fuera de rango. En tales casos usted
puede proceder a capturar el error como se muestra en el siguiente programa.

// Compilado y probado exitosamente con Dev-C++ // Demostracin: excepcin de la clase string #include <iostream>
#include <string> using namespace std; int main() { string s = Hola"; try { cout << s.at(100) << endl; } catch(exception&
e) { cout << e.what() << endl; } cin.get(); return 0; }

En el programa anterior el mtodo at de la clase string lanzar ( throw-up ) un error de excepcin, ya que la instruccin
s.at(100) trata de acceder a un elemento fuera de los limites del objeto ( s ).

25.3 La clase exception


Tal como se mostr en el programa anterior, los errorres generados por las libreras estndar de C++ pueden ser capturados
por un catch que tome un parmetro tipo exception. Realmente, exception es una clase base de donde usted puede derivar
las suyas y sobrescribir los mtodos para el tratamiento de excepciones. La clase exception est incluida en la libreria
<exception> y su estructura es la siguiente:
class exception { public: exception() throw() { } virtual ~exception() throw(); virtual const char* what() const throw(); };
En muchos casos bastar con sobrescribir el mtodo what() en la clase derivada de exception, ya que dicho mtodo es el
encargado de generar el mensaje que trata de explicar la naturaleza del error ocurrido. En el programa que veremos en
seguida, se da un ejemplo sencillo de cmo crear una clase derivada de la clase exception con el objetivo de sobrescribir
el mtodo what().
// Demostracin: sobrescritura del mtodo what() #include <iostream> #include <cstdlib> #include <exception> using
namespace std; class div_cero : public exception { public: const char* what() const throw() { return Error: divisin por
cero..."; } }; int main(int argc, char *argv[]) { double N, D; cout << Probando divisin << endl; cout << Ingrese el
numerador :"; cin >> N; cin.clear(); cout << Ingrese el denominador :"; cin >> D; cin.clear(); try { if (D == 0) throw
div_cero(); cout << N << " / " << D << " = " << N/D << endl; } catch(exception& e) { cout << e.what() << endl; }
system(PAUSE); return 0; }

Siguiendo la misma metodologa mostrada por el programa anterior, usted puede crear clases independientes para cap-
turar errores de excepciones especcas. Por ejemplo, si se desea crear una serie de funciones matemticas se deben
considerar los sucesos de errores tales como: Divisin por cero, Error de dominio, Error de rango, etc. As, el siguiente
programa puede ser un buen ejemplo para que usted escriba sus propios controladores de mensajes de error. Observe
cmo en el programa se crea la clase ErrorMat y dentro de la misma la funcin porque() la cual se encargar de des-
104 CAPTULO 25. CONTROL DE EXCEPCIONES

plegar el mensaje de error. Aunque ErrorMat solo ha sido pensada para tratar los posibles errores de rango y errores de
dominio, la misma puede rescribirse para capturar todos los errores posibles que puedan resultar a raiz de operaciones
matemticas.
Nota: No deje de observar tambin, cmo la funcin independiente logaritmo() verica si el parmetro pasado a la
misma es 0 o menor que 0 y en tales circunstancias se lanzara (throw) un error de excepcin del tipo ErrorMat ya que
el dominio para la funcin log() es el de los nmeros positivos y el logaritmo de cero no est denido en los nmeros reales.

// Demostracin: try, throw y catch #include <iostream> #include <cmath> using namespace std; static const int EDO-
MINIO=100; static const int ERANGO=101; class ErrorMat { public: ErrorMat() : motivo(0) {}; ErrorMat(int m) :
motivo(m) {}; const char* porque() const throw(); private: int motivo; }; const char* ErrorMat::porque() const throw() {
switch (motivo) { case EDOMINIO: return Error de Dominio ";break; case ERANGO: return Error de Rango ";break;
default: return Error Desconocido"; //En rigor no debera ocurrir } } double logaritmo(const double n) { try { if (n < 0)
throw(ErrorMat(EDOMINIO) ); if (n == 0) throw(ErrorMat(ERANGO) ); return log(n); } catch(ErrorMat& e) { cout
<< e.porque(); } return 0; } int main() { double r = 100; cout << log(" << r << ") = " << logaritmo(r) << endl; cout <<
log(-" << r << ") = " << logaritmo(-r) << endl; cin.get(); return 0; }
Captulo 26

Librera Estndar de Plantillas

Editores:
Oscar E. Palacios

26.1 Biblioteca estndar de plantillas


La STL (Standard Template Library) de C++ es una coleccin genrica de plantillas de clases y algoritmos que permite a
los programadores implementar fcilmente estructuras estndar de datos como colas (queues), listas (lists), y pilas (stacks).
La STL de C++ provee a los programadores con lo constructores siguientes, agrupados en tres categorias:
Secuencias (sequences)

1. C++ Vectors
2. C++ Lists
3. C++ Double-Ended Queues

Adaptadores de contenedor (Container Adapters)

1. C++ Stacks
2. C++ Queues
3. C++ Priority Queues

Contenedores asociativos (Associative Containers)

1. C++ Bitsets
2. C++ Maps
3. C++ Multimaps
4. C++ Sets
5. C++ Multisets

105
106 CAPTULO 26. LIBRERA ESTNDAR DE PLANTILLAS

La idea detras de la STL de C++ es que la parte dicil en el uso de estructuras complejas de datos ya ha sido previamente
completada. Por ejemplo, si un programador desea usar un stack de enteros, todo lo que tiene que hacer es escribir el
cdigo:
stack<int> myStack;

Con un minimo de esfuerzo, l o ella puede usar la funcin push() para ingresar enteros al stack; y la funcin pop() para
retirar enteros del stack. A travez de la magia de las plantillas de C++, se puede especicar cualquier tipo de dato, no slo
enteros. La clase Stack de la STL provee la funcionalidad genrica de un stack, sin importar el tipo de dato en el stack.

26.2 Iteradores
El termino iterar signica (en palabras sencillas) el mtodo o forma que se usa para poder navegar sobre los elementos de
una lista especca. Luego, un iterador es como una especie de puntero especial que nos permite leer o escribir valores
sobre cada uno de los elementos en una lista. Los iteradores pueden ser comparados con los ndices que se emplean para
leer o escribir valores sobre los elementos de una lista. Igual a lo que sucede con los tipos de datos dentro de un arreglo
primitivo en donde para leer o escribir valores sobre los elementos se tiene que prestar atencin al tipo de dato, un iterador
tiene que ser del tipo de dato que posee la lista. En ese sentido, si tenemos por ejemplo un vector que posee datos de tipo
entero (int) entonces el iterador tendr que ser de tipo entero; si por el contrario se tiene un vector cuyos datos son del
tipo cadena (string) entonces el iterador tiene que ser del tipo cadena.
Sintaxis
La sintaxis general para la declaracin de un iterador es:
NombrePlantilla<tipo>::iterator varid;
Donde,
Por ejemplo, para obtener un iterador a un vector de tipo int podemos usar la instruccion:
vector <int>::iterator el_iterador;

A manera de ejemplo, vamos a mostrar un programa en el que se usar un iterador de tipo char.

Nota: en el programa se hace uso de los mtodos begin para obtener un iterador hacia el inicio
del vector, y end para obtener un iterador hacia el nal del mismo.

// Demostracion del uso de iteradores // probado en: Dev-C++ 4.9.9.2 #include <cstdlib> #include <iostream> #inclu-
de <vector> using namespace std; int main(int argc, char *argv[]) { vector<char> v; for (int x = 'A'; x <= 'Z'; x++)
v.push_back(x); // obtenemos un iterator del tipo char vector<char>::iterator it; // lectura y despliegue de datos cout <<
"\ndesplegando datos << endl; for( it = v.begin(); it != v.end(); it++ ) cout << *it << endl; system(PAUSE); return
EXIT_SUCCESS; }

26.3 Iteradores reversos


La STL de C++ permite que los elementos de las listas creadas puedan ser navegados en orden normal, es decir desde
el primero hasta el ltimo de los elementos agregados a la lista, y para dichas tareas se usa un iterador normal. Tambin,
los elementos pueden navegarse en orden reverso y en tales casos se usa un iterador en reverso ( reverse_iterator ). Un
ejemplo de reverse_iterator se muestra en el siguiente programa.
Notas:

Se debe de observar que para obtener iteradores normales se usan (generalmente) los mtodos begin() y end(),
mientras que que para obtener iteradores reversos se emplean los mtodos rbegin() y rend().
26.3. ITERADORES REVERSOS 107

Los iteradores reversos se comportan de manera inversa. Por ejemplo, para obtener un iterador que vaya desde
el nal hasta el inicio de los elementos, se usa la funcin rbegin() la cual regresar un iterador para procesar la
lista desde el ltimo hasta el primero de los elementos. En contraparte, la funcin begin() regresa un iterador para
procesar la lista desde el primero hasta el ltimo de los elementos. Es decir, begin() regresa una referencia hacia el
primer elemento, mientras que rbegin() regresa una referencia hacia el ltimo elemento.

// Demostracion del uso de iteradores reversos // probado en: Dev-C++ 4.9.9.2 #include <cstdlib> #include <iostream>
#include <list> using namespace std; int test() { list<char> v; for (int x = 'A'; x <= 'Z'; x++) v.push_back(x); cout << endl;
cout << orden original << endl; // creamos un iterator normal list<char>::iterator i = v.begin(); while(i != v.end() ) { cout
<< *i++ << " "; } cout << endl; cout << orden inverso << endl; // creamos un iterator reverso list<char>::reverse_iterator
ri = v.rbegin(); while(ri != v.rend() ) { cout << *ri++ << " "; } cout << endl; return 0; } int main(int argc, char *argv[])
{ test(); system(PAUSE); return EXIT_SUCCESS; }
Captulo 27

Biblioteca Estndar de Plantillas

Editores:
Oscar E. Palacios

27.1 Biblioteca estndar de plantillas


La STL (Standard Template Library) de C++ es una coleccin genrica de plantillas de clases y algoritmos que permite a
los programadores implementar fcilmente estructuras estndar de datos como colas (queues), listas (lists), y pilas (stacks).
La STL de C++ provee a los programadores con lo constructores siguientes, agrupados en tres categorias:
Secuencias (sequences)

1. C++ Vectors
2. C++ Lists
3. C++ Double-Ended Queues

Adaptadores de contenedor (Container Adapters)

1. C++ Stacks
2. C++ Queues
3. C++ Priority Queues

Contenedores asociativos (Associative Containers)

1. C++ Bitsets
2. C++ Maps
3. C++ Multimaps
4. C++ Sets
5. C++ Multisets

108
27.2. ITERADORES 109

La idea detras de la STL de C++ es que la parte dicil en el uso de estructuras complejas de datos ya ha sido previamente
completada. Por ejemplo, si un programador desea usar un stack de enteros, todo lo que tiene que hacer es escribir el
cdigo:
stack<int> myStack;

Con un minimo de esfuerzo, l o ella puede usar la funcin push() para ingresar enteros al stack; y la funcin pop() para
retirar enteros del stack. A travs de la magia de las plantillas de C++, se puede especicar cualquier tipo de dato, no slo
enteros. La clase Stack de la STL provee la funcionalidad genrica de un stack, sin importar el tipo de dato en el stack.

27.2 Iteradores
El termino iterar signica (en palabras sencillas) el mtodo o forma que se usa para poder navegar sobre los elementos de
una lista especca. Luego, un iterador es como una especie de puntero especial que nos permite leer o escribir valores
sobre cada uno de los elementos en una lista. Los iteradores pueden ser comparados con los ndices que se emplean para
leer o escribir valores sobre los elementos de una lista. Igual a lo que sucede con los tipos de datos dentro de un arreglo
primitivo en donde para leer o escribir valores sobre los elementos se tiene que prestar atencin al tipo de dato, un iterador
tiene que ser del tipo de dato que posee la lista. En ese sentido, si tenemos por ejemplo un vector que posee datos de tipo
entero (int) entonces el iterador tendr que ser de tipo entero; si por el contrario se tiene un vector cuyos datos son del
tipo cadena (string) entonces el iterador tiene que ser del tipo cadena.
Sintaxis
La sintaxis general para la declaracin de un iterador es:
NombrePlantilla<tipo>::iterator varid;
Donde,
Por ejemplo, para obtener un iterador a un vector de tipo int podemos usar la instruccion:
vector <int>::iterator el_iterador;

A manera de ejemplo, vamos a mostrar un programa en el que se usar un iterador de tipo char.

Nota: en el programa se hace uso de los mtodos begin para obtener un iterador hacia el inicio
del vector, y end para obtener un iterador hacia el nal del mismo.

// Demostracion del uso de iteradores // probado en: Dev-C++ 4.9.9.2 #include <cstdlib> #include <iostream> #inclu-
de <vector> using namespace std; int main(int argc, char *argv[]) { vector<char> v; for (int x = 'A'; x <= 'Z'; x++)
v.push_back(x); // obtenemos un iterator del tipo char vector<char>::iterator it; // lectura y despliegue de datos cout <<
"\ndesplegando datos << endl; for( it = v.begin(); it != v.end(); it++ ) cout << *it << endl; system(PAUSE); return
EXIT_SUCCESS; }

27.3 Iteradores reversos


La STL de C++ permite que los elementos de las listas creadas puedan ser navegados en orden normal, es decir desde
el primero hasta el ltimo de los elementos agregados a la lista, y para dichas tareas se usa un iterador normal. Tambin,
los elementos pueden navegarse en orden reverso y en tales casos se usa un iterador en reverso ( reverse_iterator ). Un
ejemplo de reverse_iterator se muestra en el siguiente programa.
Notas:

Se debe de observar que para obtener iteradores normales se usan (generalmente) los mtodos begin() y end(),
mientras que que para obtener iteradores reversos se emplean los mtodos rbegin() y rend().
110 CAPTULO 27. BIBLIOTECA ESTNDAR DE PLANTILLAS

Los iteradores reversos se comportan de manera inversa. Por ejemplo, para obtener un iterador que vaya desde
el nal hasta el inicio de los elementos, se usa la funcin rbegin() la cual regresar un iterador para procesar la
lista desde el ltimo hasta el primero de los elementos. En contraparte, la funcin begin() regresa un iterador para
procesar la lista desde el primero hasta el ltimo de los elementos. Es decir, begin() regresa una referencia hacia el
primer elemento, mientras que rbegin() regresa una referencia hacia el ltimo elemento.

// Demostracion del uso de iteradores reversos // probado en: Dev-C++ 4.9.9.2 #include <cstdlib> #include <iostream>
#include <list> using namespace std; int test() { list<char> v; for (int x = 'A'; x <= 'Z'; x++) v.push_back(x); cout << endl;
out << orden original << endl; // creamos un iterator normal list<char>::iterator i = v.begin(); while(i != v.end() ) { cout
<< *i++ << " "; } cout << endl; cout << orden inverso << endl; // creamos un iterator reverso list<char>::reverse_iterator
ri = v.rbegin(); while(ri != v.rend() ) { cout << *ri++ << " "; } cout << endl; return 0; } int main(int argc, char *argv[])
{ test(); system(PAUSE); return EXIT_SUCCESS; }
Captulo 28

Vectores

Editores:
Oscar E. Palacios

28.1 C++ vector estndar


La estructura de la clase vector est pensada para operar con arreglos unidimensionales de datos, los elementos de un
vector pueden ser manipulados de la misma manera en que se hace con las estructuras de arreglos (arrays) tradicionales
en C, C++; es decir, los componentes de un vector pueden ser referenciados a travs de un ndice numrico, de la misma
manera que en un arreglo cualquiera. Por ejemplo, si A es un objeto de vector, entonces la instruccin: A[0]; se reere
al componente 0 (primer elemento) de A. El resultado de todo esto es que usted puede navegar o iterar a travs de los
componentes de una lista haciendo uso de ndices, o si lo preere a travs de punteros iteradores. Si usted desea ver una
lista completa de los mtodos asociados a la clase vector siga ste enlace ( Tabla de mtodos ), pero recuerde que no todos
ellos sern cubiertos aqu.
Para comenzar, vamos a presentar un ejemplo sencillo, el cual consistir en crear un vector de nmeros de punto otante.
Al vector creado le agregaremos una serie de valores los cuales posteriormente sern sumados y desplegados en la pantalla
del monitor. Para nuestro ejemplo vamos a emplear los mtodos push_back (para agregar los nmeros), size (para obtener
el nmero de componentes en el vector), e iteraremos por medio de ndices numricos. Veamos.
// Demostracion del uso de un vector // probado en: Dev-C++ 4.9.9.2 #include <cstdlib> #include <iostream> #inclu-
de <vector> using namespace std; int main(int argc, char *argv[]) { char buer[80]; double suma; vector<double> v;
v.push_back(999.25); v.push_back(888.50); v.push_back(777.25); suma = 0; for(int i = 0; i < v.size(); i++) { suma +=
v[i]; sprintf(buer, "%10.2f, v[i]); cout << buer << endl; } cout << "----------" << endl; sprintf(buer, "%10.2f, su-
ma); cout << buer << endl; cin.get(); return EXIT_SUCCESS; }

De acuerdo con la referencia de ayuda de Dev-C++ es ms seguro emplear el mtodo at() en lugar el eperador [] para
leer o escribir componentes en un vector, ya que at() no permite ndices fuera del vector, y el operador [] s. Por ejemplo,
si V es un vector cuyo nmero de componentes es de 3, entonces la instruccin V[5]; es sumamente peligrosa ya que el
ndice 5 est fuera del rango (0 a 2 ) de los componentes de V; por otro lado, la instruccin V.at(5); tambin est fuera
de rango, salvo que at() en lugar de leer o escribir el componente referenciado lanzar (throw) un error de excepcin, de
tal manera que en el programa se pueda controlar la condicin de error por medio de un catch. El mtodo at() actua de
manera parecida al operador [], y para ver un ejemplo de su uso compile y ejecute el siguiente programa.
// Demostracion del uso del mtodo at() // probado en: Dev-C++ 4.9.9.2 #include <cstdlib> #include <iostream> #include
<vector> using namespace std; int main(int argc, char *argv[]) { vector<char> v; // llenamos el vector v con valores desde
la 'A' hasta la 'Z' for (int x = 'A'; x <= 'Z'; x++) v.push_back(x); // despliegue de los elementos del vector v // mediante
el operador []. for(int i = 0; i < v.size(); i++) cout << v[i] << " "; // despliegue de los elementos del vector v // mediante
el mtodo at(). for(int i = 0; i < v.size(); i++) cout << v.at(i) << " "; cout << endl; cin.get(); return EXIT_SUCCESS; }

111
112 CAPTULO 28. VECTORES

28.2 Colas de doble n ( deque )


Las Colas de doble n son como los vectores, excepto que en stas la insercin y borrado de elementos es ms rapida,
adems de permitir insertar y borrar elementos al principio as como al nal del contenedor. La mayora de los mtodos
aplicables a la clase vector son aplicables a la clase deque, pero la clase deque posee ademas los mtodos push_front y
pop_front para insertar y borrar elementos al principio. En orden de ver un ejemplo modicaremos el programa anterior,
en el mismo usaremos una clase deque.
// Demostracion de la clase deque. // probado en: Dev-C++ 4.9.9.2 #include <cstdlib> #include <iostream> #include
<deque> using namespace std; int main(int argc, char *argv[]) { deque<char> v; char x; // Metemos en la cola v valores
desde la 'A' hasta la 'Z' // mediante el mtodo push_back. for (x = 'A'; x <= 'Z'; x++) v.push_back(x); // Metemos en
la cola v valores desde la '0' hasta la '9' // mediante el mtodo push_front. for (x = 0; x <= 9; x++) v.push_front(x); //
despliegue de los elementos de la cola // mediante el operador []. for(int i = 0; i < v.size(); i++) cout << v[i] << " "; //
despliegue de los elementos del vector v // mediante el mtodo at(). for(int i = 0; i < v.size(); i++) cout << v.at(i) << " ";
cout << endl; cin.get(); return EXIT_SUCCESS; }

28.3 Tabla de mtodos


Captulo 29

Pilas

#include <iostream> #include <stack> using namespace std; main() { stack < int> s; s.push(1); s.push(2); s.push(3); whi-
le(!s.empty()){ cout<<" " <<s.top()<<endl; s.pop(); } cout<<" "; system(pause); }

113
Captulo 30

Colas

#include <iostream> #include <queue> using namespace std; main() { queue < int> s; s.push(1); s.push(2); s.push(3);
while(!s.empty()){ cout<<" " <<s.front()<<endl; s.pop(); } cout<<" "; system(pause); /*tomado como ejemplo http:
//es.wikibooks.org/wiki/Programaci%C3%B3n_en_C%2B%2B/Biblioteca_Est%C3%A1ndar_de_Plantillas/Pilas*/ }

114
30.1. ORIGEN DEL TEXTO Y LAS IMGENES, COLABORADORES Y LICENCIAS 115

30.1 Origen del texto y las imgenes, colaboradores y licencias


30.1.1 Texto
Programacin en C++/Introduccin Fuente: https://es.wikibooks.org/wiki/Programaci%C3%B3n_en_C%2B%2B/Introducci%C3%B3n?
oldid=297122 Colaboradores: ManuelGR, CaStarCo~eswikibooks, Davidcanar, Ciencia Al Poder, Ruly, Alephcero, Oleinad, El lobo, Rey-
nard~eswikibooks, Jarisleif, Morza, MarcoAurelio, Drinibot, Ortisa, Eliluminado, Savh, Narutoeshacker15, Invadibot, The Fantasy Club,
Allanbot, Razr Nation, LlamaAl, 0zkr, Syum90, Koichi bikuta y Annimos: 44
Programacin en C++/Lo ms bsico Fuente: https://es.wikibooks.org/wiki/Programaci%C3%B3n_en_C%2B%2B/Lo_m%C3%A1s_b%
C3%A1sico?oldid=300127 Colaboradores: ManuelGR, Davidcanar, Pirenne, Taichi, LadyInGrey, Martinaguilar, Ruly, Oleinad, El lobo, Jaris-
leif, Morza, MarcoAurelio, Cvmontuy, Julian.caba, Einsteinm2, Savh, Jcaraballo, Narutoeshacker15, Igna, The Fantasy Club, Allanbot, Alan,
Vale Espin, Leitoxx, Matiia, Syum90, Sevaldes y Annimos: 87
Programacin en C++/Iteraciones y decisiones Fuente: https://es.wikibooks.org/wiki/Programaci%C3%B3n_en_C%2B%2B/Iteraciones_
y_decisiones?oldid=295022 Colaboradores: Oleinad, El lobo, Marioamendez, Drinibot, Nagual95, Julian.caba, Savh, Allanbot, Matiia, Rayo-
distancia y Annimos: 24
Programacin en C++/Estructuras Fuente: https://es.wikibooks.org/wiki/Programaci%C3%B3n_en_C%2B%2B/Estructuras?oldid=292555
Colaboradores: Oleinad, El lobo, Jordaker, J.delanoy, Gabrielsnake, Savh, RadiX, Lemures~eswikibooks, UBravo, Allanbot, Razr Nation, Lla-
maAl, Syum90, Frankofuego y Annimos: 46
Programacin en C++/Funciones Fuente: https://es.wikibooks.org/wiki/Programaci%C3%B3n_en_C%2B%2B/Funciones?oldid=289140
Colaboradores: Rutrus, Nsmlinux~eswikibooks, Zzzu, Muro de Aguas, Oleinad, El lobo, Morza, MarcoAurelio, 67wkii, Jhalvico, Savh, Allan-
bot, Alan, LlamaAl, Aldnonymous, Syum90, Tropicalkitty y Annimos: 52
Programacin en C++/Streams Fuente: https://es.wikibooks.org/wiki/Programaci%C3%B3n_en_C%2B%2B/Streams?oldid=244832 Cola-
boradores: Muro de Aguas, Oleinad, El lobo, Drinibot, Der Knstler, Defender, Tegel, ByDSA, Allanbot, Jorgelrm, Ixfd64, LlamaAl, Syum90,
Koichi bikuta y Annimos: 22
Programacin en C++/Arrays y cadenas de texto Fuente: https://es.wikibooks.org/wiki/Programaci%C3%B3n_en_C%2B%2B/Arrays_y_
cadenas_de_texto?oldid=238282 Colaboradores: Oleinad, El lobo, MarcoAurelio, Victor.spain, Defender, Savh, RadiX, Invadibot, Allanbot y
Annimos: 34
Programacin en C++/Desarrollo Orientado a Objetos Fuente: https://es.wikibooks.org/wiki/Programaci%C3%B3n_en_C%2B%2B/Desarrollo_
Orientado_a_Objetos?oldid=197580 Colaboradores: Manolo.wiki, Oleinad, El lobo, Jarisleif, Allanbot y Annimos: 3
Programacin en C++/Objetos y Clases Fuente: https://es.wikibooks.org/wiki/Programaci%C3%B3n_en_C%2B%2B/Objetos_y_Clases?
oldid=302890 Colaboradores: Oleinad, El lobo, Morza, Albmont, Luckas Blade, Drinibot, Victor.spain, Ortisa, Guanucoluis, Defender, Pe-
trohsW, Savh, Mjbmrbot, RadiX, GermanX, Invadibot, Allanbot, LlamaAl, Matiia, Syum90, William mejia78 y Annimos: 41
Programacin en C++/Sobrecarga de Operadores Fuente: https://es.wikibooks.org/wiki/Programaci%C3%B3n_en_C%2B%2B/Sobrecarga_
de_Operadores?oldid=298012 Colaboradores: El lobo, David0811, Groucho Marx, Allanbot, Matiia y Annimos: 25
Programacin en C++/Herencia Fuente: https://es.wikibooks.org/wiki/Programaci%C3%B3n_en_C%2B%2B/Herencia?oldid=298057 Co-
laboradores: El lobo, Morza, Cvmontuy, Wiki13, The Fantasy Club, Allanbot, Syum90 y Annimos: 24
Programacin en C++/Funciones virtuales Fuente: https://es.wikibooks.org/wiki/Programaci%C3%B3n_en_C%2B%2B/Funciones_virtuales?
oldid=284050 Colaboradores: El lobo, Morza, Ezarate, 67wkii, Allanbot y Annimos: 26
Programacin en C++/Punteros Fuente: https://es.wikibooks.org/wiki/Programaci%C3%B3n_en_C%2B%2B/Punteros?oldid=293614 Co-
laboradores: ManuelGR, Martinaguilar, XemDenots, Muro de Aguas, El lobo, Morza, Albmont, MarcoAurelio, Savh, Ajraddatz, Jcaraballo,
Wiki13, Allanbot, Polyglottos, Matiia, Syum90, Asarova y Annimos: 53
Programacin en C++/Estructuras II Fuente: https://es.wikibooks.org/wiki/Programaci%C3%B3n_en_C%2B%2B/Estructuras_II?oldid=
276899 Colaboradores: El lobo, Morza, MarcoAurelio, Drinibot, Allanbot y Annimos: 6
Programacin en C++/Plantillas Fuente: https://es.wikibooks.org/wiki/Programaci%C3%B3n_en_C%2B%2B/Plantillas?oldid=197626 Co-
laboradores: El lobo, Ranzel, Drinibot, Allanbot y Annimos: 1
Programacin en C++/Excepciones Fuente: https://es.wikibooks.org/wiki/Programaci%C3%B3n_en_C%2B%2B/Excepciones?oldid=237012
Colaboradores: El lobo, Jarisleif, Ranzel, The Fantasy Club, Allanbot, Syum90 y Annimos: 5
Programacin en C++/Librera Estndar de Plantillas Fuente: https://es.wikibooks.org/wiki/Programaci%C3%B3n_en_C%2B%2B/Librer%
C3%ADa_Est%C3%A1ndar_de_Plantillas?oldid=197602 Colaboradores: ManuelGR, El lobo, Allanbot y Annimos: 2
Programacin en C++/Biblioteca Estndar de Plantillas Fuente: https://es.wikibooks.org/wiki/Programaci%C3%B3n_en_C%2B%2B/
Biblioteca_Est%C3%A1ndar_de_Plantillas?oldid=197576 Colaboradores: Allanbot y Annimos: 2
Programacin en C++/Biblioteca Estndar de Plantillas/Vectores Fuente: https://es.wikibooks.org/wiki/Programaci%C3%B3n_en_C%
2B%2B/Biblioteca_Est%C3%A1ndar_de_Plantillas/Vectores?oldid=232374 Colaboradores: ManuelGR, El lobo, Shooke, Allanbot y Anni-
mos: 11
Programacin en C++/Biblioteca Estndar de Plantillas/Pilas Fuente: https://es.wikibooks.org/wiki/Programaci%C3%B3n_en_C%2B%
2B/Biblioteca_Est%C3%A1ndar_de_Plantillas/Pilas?oldid=159758 Colaboradores: Annimos: 2
Programacin en C++/Biblioteca Estndar de Plantillas/Colas Fuente: https://es.wikibooks.org/wiki/Programaci%C3%B3n_en_C%2B%
2B/Biblioteca_Est%C3%A1ndar_de_Plantillas/Colas?oldid=181353 Colaboradores: Chepe263
116 CAPTULO 30. COLAS

30.1.2 Imgenes
Archivo:25%.svg Fuente: https://upload.wikimedia.org/wikipedia/commons/3/34/25%25.svg Licencia: Public domain Colaboradores: Image:
25%.png redone in svg. Artista original: Karl Wick
Archivo:50%.svg Fuente: https://upload.wikimedia.org/wikipedia/commons/c/c2/50%25.svg Licencia: Public domain Colaboradores: Based
on the XML code of Image:25%.svg Artista original: Siebrand
Archivo:75%.svg Fuente: https://upload.wikimedia.org/wikipedia/commons/4/49/75%25.svg Licencia: Public domain Colaboradores: Based
on the XML code of Image:25%.svg Artista original: Siebrand
Archivo:Crystal_Clear_app_kedit.png Fuente: https://upload.wikimedia.org/wikipedia/commons/7/70/Crystal_Clear_app_kedit.png Licen-
cia: LGPL Colaboradores: All Crystal icons were posted by the author as LGPL on kde-look Artista original: Everaldo Coelho and YellowIcon
Archivo:Desicion.png Fuente: https://upload.wikimedia.org/wikipedia/commons/9/90/Desicion.png Licencia: GFDL Colaboradores: Traba-
jo propio Artista original: MyName (El lobo)
Archivo:Funciones.png Fuente: https://upload.wikimedia.org/wikipedia/commons/2/2c/Funciones.png Licencia: GFDL Colaboradores: Tra-
bajo propio Artista original: El lobo
Archivo:Herencia01.png Fuente: https://upload.wikimedia.org/wikipedia/commons/6/64/Herencia01.png Licencia: CC-BY-SA-3.0 Colabo-
radores: Trabajo propio Artista original: El lobo
Archivo:Struct01.gif Fuente: https://upload.wikimedia.org/wikipedia/commons/9/9e/Struct01.gif Licencia: GFDL Colaboradores: Trabajo
propio Artista original: El lobo

30.1.3 Licencia del contenido


Creative Commons Attribution-Share Alike 3.0

Potrebbero piacerti anche