Está en la página 1de 86

Introducción a la

Programación en C++

Con ejemplos e ilustraciones

Primera Edición

Semestre 2020A - EPN


Índice general

1. Introducción a C++ 1
1.1. Qué es programar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2. Lenguaje de programación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.3. Qué es un IDE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3.1. CodeBlocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3.2. Geany . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.3.3. Dev C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.3.4. OnlineGBD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.4. Instrucciones básicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.5. Mi primer programa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.5.1. Estructura del código fuente . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.6. Comentarios en C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.7. Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.7.1. Tipos de variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.8. Entrada y salida de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.9. Operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.9.1. Operadores aritméticos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.9.2. Operadores de asignación . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.10. Constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.11. Conversión de tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

2. Sentencias condicionales y repetitivas 15


2.1. Expresiones lógicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.1.1. Expresiones lógicas compuestas . . . . . . . . . . . . . . . . . . . . . . . . 15
2.2. Sentencias condicionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.2.1. Sentencia if-else . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.2.2. Sentencia switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.3. Sentencias repetitivas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.3.1. Sentencia while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.3.2. Sentencia do - while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.3.3. Sentencia for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2.4. Ámbito de las variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

3. Datos estructurados 27
3.1. Vectores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
3.1.1. Inicialización de vectores . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
3.2. Matrices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
3.2.1. Inicialización de matrices . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.3. Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
3.4. Estructuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.4.1. Estructuras Anidadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

2
4. Números aleatorios 36
4.1. Generación de números aleatorios enteros . . . . . . . . . . . . . . . . . . . . . . 36
4.2. Generación de números aleatorios decimales . . . . . . . . . . . . . . . . . . . . . 38
4.3. Evaluación de integrales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

5. Funciones 42
5.1. Definición de función . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
5.2. Parámetros por defecto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
5.3. Tipos de datos que devuelve una función . . . . . . . . . . . . . . . . . . . . . . . 45
5.4. Funciones void . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
5.5. Almacenamiento de variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
5.5.1. La Pila . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
5.5.2. Estado de la pila . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
5.6. Funciones recursivas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
5.7. Paso por variable o referencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
5.7.1. Referencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
5.7.2. Paso por referencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
5.8. Paso de parámetros tipo estructura . . . . . . . . . . . . . . . . . . . . . . . . . . 56
5.9. Sobrecarga de funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
5.10. Paso de parámetros de tipo vector . . . . . . . . . . . . . . . . . . . . . . . . . . 58
5.11. Paso de parámetros de tipo array . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

6. Punteros 63
6.1. Qué es un puntero ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
6.2. Puntero nulo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
6.3. Parámetros por referencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
6.4. Aritmética de punteros y vectores . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

7. Librería STL 70
7.1. Vectores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
7.1.1. Iteradores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
7.2. Matrices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74

8. Clases y Objetos 77
8.1. Definición de clases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
8.2. Uso de clases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
8.3. Constructores y destructores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
8.4. Sobrecarga de operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
8.5. Sobrecarga de los operadores de entrada y salida . . . . . . . . . . . . . . . . . . 83
8.6. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
1 | Introducción a C++

El lenguaje C++ (se lee C plus plus) fue creado a mediados de los años 80 y es uno de los
lenguajes de programación más utilizados. De hecho muchos de los programas que utilizamos
diariamente están hechos en lenguaje C++, por ejemplo: los sistemas operativos Windows, Linux
y macOS, los programas de Office, el sistema Android, entre muchos otros.

1.1. Qué es programar


Programar es decirle al ordenador lo que tiene que realizar. Como el ordenador no es una per-
sona, sino una máquina, éste no va entender lo que tiene que hacer si no le indicamos de una
manera que pueda entenderlo.

Como ejemplo nos vamos a ubicar sobre un auto de Fórmula 1, si deseamos que el auto acelere
esto se lo diremos presionando el acelerador, o si deseamos que disminuya la velocidad esto se
lo diremos presionando el freno.

Figura 1.1: Auto Fórmula 1

Si deseamos realizar un giro a la derecha o izquierda, esto se lo diremos girando el volante.


En resumen, el auto hará en todo momento lo que le digamos que haga, es decir, seguirá las
instrucciones que le demos mediante las acciones que le indiquemos para ello.

Con los ordenadores sucede lo mismo. Harán lo que les hayamos dicho que hagan. Las instruc-
ciones que damos al ordenador, se pueden dar directamente, en su propio lenguaje, o pueden
estar ya integradas en un paquete que instalemos.

Figura 1.2: Word & Winedt

1
CAPÍTULO 1. INTRODUCCIÓN A C++ 2

De modo general, programar consiste en dar una serie de instrucciones a la máquina para que
esta se comporte de una determinada manera. Estas instrucciones se guardan dentro de un pro-
grama informático o aplicación.

Como los programas pueden guardarse en la memoria del ordenador, el usuario, cada vez que
quiera realizar una tarea que ya está definida en un programa, sólo tiene que abrirlo y ejecutar.

1.2. Lenguaje de programación


Los lenguajes de programación indican al ordenador qué es lo que este tiene que hacer. Para ello
tenemos dos códigos:

El código fuente: El lenguaje (líneas de texto) en el que el programador escribe las


órdenes que debe ejecutar el ordenador.

El código máquina: El lenguaje (señales eléctricas almacenadas como unos y ceros) que
entiende el ordenador, y en el cual debe recibir las órdenes para ejecutarlas.

Figura 1.3: Diferencias en entre el código fuente y código máquina

Estos dos lenguajes no son iguales, por lo que necesitamos un instrumento que pueda traducir
el código fuente a código máquina, si queremos que el ordenador entienda lo que le decimos en
el código fuente. Este instrumento es el compilador.

Figura 1.4: Compilador

Por otra parte, el programador necesita una aplicación en donde poder escribir el código fuente.
CAPÍTULO 1. INTRODUCCIÓN A C++ 3

1.3. Qué es un IDE


Un IDE, llamado también Entorno de Desarrollo Integrado (IDE son sus siglas en inglés), es
un programa o aplicación que consiste en un entorno de trabajo para programar. La aplicación
tiene varias herramientas útiles para el programador, entre ellas:

1. Menú de opciones: Permite crear, guardar, modificar, ejecutar proyectos y archivos.

2. Editor: Permite ingresar el código fuente.

3. Visualización documentos: Ventana en la cual se muestran los archivos sobre los cuales se
están trabajando. Permite identificar la jerarquía de los archivos.

4. Compilador y depurador: A partir del archivo en código fuente, crea un archivo en código
máquina, además, se indican los errores que tiene el código.

Figura 1.5: Ejemplo de IDE

1.3.1. CodeBlocks
Es una de las principales aplicaciones de Entorno de Desarrollo Integrado para C++. Para su
descarga ingresamos a su sitio oficial: http://www.codeblocks.org/downloads/26.

Figura 1.6: Logo CodeBlocks

Allí tenemos varias opciones para descargar la aplicación. Dependiendo del sistema operativo
que disponga el ordenador, iremos a la sección de Windows, Linux 32, Linux 64, o Mac OS X.
CAPÍTULO 1. INTRODUCCIÓN A C++ 4

1.3.2. Geany
Es una aplicación de Entorno de Desarrollo Integrado que se adapta bastante bien a la pro-
gramación con C++. Para su descarga ingresamos a su sitio oficial: http://www.geany.org/
Download/Releases

Figura 1.7: Logo Geany

1.3.3. Dev C++


Es una aplicación de Entorno de Desarrollo Integrado que se puede usar para programar con
C++. Para su descarga iremos a su sitio oficial: http://www.bloodshed.net/dev/devcpp.html

Figura 1.8: Logo Dev C++

La instalación de los IDEs se realiza todo por defecto (a todo le damos siguiente).

1.3.4. OnlineGBD
Es una aplicación online muy utilizada en la actualidad que permite correr códigos en C++,
Java, Phyton, entre otros.

Se puede acceder a la aplicación desde el enlace: https://www.onlinegdb.com

Figura 1.9: Logo OnlineGBD

1.4. Instrucciones básicas


El funcionamiento básico para cualquiera de las IDEs antes comentadas es el siguiente:
1. Abrimos la aplicación y creamos un nuevo archivo (proyecto), o abrimos uno ya existente
para modificarlo. Este archivo será el archivo fuente, y su contenido el código fuente.
2. Escribimos las instrucciones en lenguaje C++ (es decir, el código fuente), en el espacio de
edición de la aplicación.
CAPÍTULO 1. INTRODUCCIÓN A C++ 5

3. Compilamos y ejecutamos. La compilación crea un nuevo archivo (o sustituye al ya exis-


tente). Este nuevo archivo es el archivo ejecutable, y su contenido está escrito en código
máquina. Al ejecutarlo, éste se abre y se realizan las instrucciones que contiene.
A partir de aquí daremos ya por hecho que sabemos hacer estos pasos, tanto de apertura de
archivos, como de compilar y ejecutar, y nos centraremos más en el propio lenguaje C++.

1.5. Mi primer programa


En la gran mayoría de los IDEs al momento de crear un nuevo archivo nos encontraremos con
el siguiente código:

1 # include < iostream >


2 using namespace std ;
3
4 int main () {
5 cout << " Hola Mundo " << endl ;
6 return 0;
7 }

En los siguientes enlaces se comenta a mayor detalle el primer programa en C++:


⋄ http://y2u.be/ld4nzao5XAc
⋄ http://y2u.be/ZJIP_cO6JwM

1.5.1. Estructura del código fuente


Ahora vamos a analizar con detalle las líneas del código anterior:
include <iostream>
Esta línea inicia con almohadilla # y encarga al preprocesador que se incluya una sección
de código C++ estándar (bibliotecas o librerías) que permite realizar operaciones de en-
trada y salida de datos.

Para entender de mejor manera la necesidad de emplear bibliotecas usaremos la siguiente


metáfora:

Imaginemos que tener instalado el programa en el computador es como haber adquirido


un auto nuevo el cual cumple con ciertas funciones principales y no trae contratiempos,
una vez que lo empezamos a rodar nos vamos dando cuenta de la necesidad de mejorar el
rendimiento de ciertos mecanismos ya existentes del auto debido que lo vamos exponien-
do a diferentes ambientes y condiciones. Lo anterior pasa exactamente con C++, cuando
descargamos e instalamos el software contamos con ciertas características y funcionalida-
des básicas pero existen varias bibliotecas adicionales que pueden ser agregadas para lograr
mejorar su funcionalidad y a la vez realizar cosas estupendas.

En el siguiente enlace se enlistan las principales bibliotecas o librerías:


⋄ http://www.cplusplus.com/reference/
CAPÍTULO 1. INTRODUCCIÓN A C++ 6

using namespace std;


Esta línea declara un namespace o espacio de nombres. En concreto el namespace std o
estándar. Dentro del namespace std están declarados todos los elementos de las bibliotecas
estándar.
Mediante esta línea podemos usar los elementos de la biblioteca estándar sin necesidad de
tener que declararlos cada vez que se usan.

Si no se utiliza la instrucción using namespace std, entonces los nombres cout y endl del
código anterior se deberían escribir como std::cout y std::endl.

int main( )
Esta estructura es la función principal que contiene el código. Lo que se escriba dentro de
las llaves de esta función es el código fuente en sí, es decir el programa que se ejecutará.

La primera palabra int, nos dice el tipo del valor de retorno de la función, en este caso
un número entero. La función main siempre devuelve un entero. La segunda palabra es el
nombre de la función, en general será el nombre que se usa para realizar el llamado a la
función.

cout << Hola Mundo << endl;


Esta sentencia tiene varias partes: En primer lugar cout, que identifica el dispositivo de
salida de caracteres estándar (por lo general, la pantalla del ordenador).

En segundo lugar, el operador de inserción (<<), que indica que lo que sigue debe inser-
tarse (mostrarse en pantalla).

Por último, una frase entre comillas (Hola Mundo), que es el contenido insertado.

Estas dos últimas partes se repiten para volver a insertar otro elemento, es decir repetimos
el operador de inserción (<<) y luego indicamos lo que debe insertarse, en este caso es un
salto de línea que se indica mediante la instrucción endl.

return 0;
La palabra return es reservada propia de C++. Indica al programa que debe abandonar la
ejecución de la función y continuar a partir del punto en que se la llamó. El 0 es el valor de
retorno de nuestra función. Cuando main retorna el valor 0 indica que todo ha marchado
bien.

Cada instrucción en el programa debe terminar con un ";".

1.6. Comentarios en C++


Un comentario es un texto que sirve para que el programador pueda documentar su código. Los
comentarios no son analizados por el compilador. Existen dos tipos de comentarios:

Comentario en bloque: Texto encerrado entre /* y */, puede ocupar más de una línea.
No se pueden anidar, es decir, no se puede colocar un comentario dentro de otro.
CAPÍTULO 1. INTRODUCCIÓN A C++ 7

Comentario de línea: Texto que inicia con //. Una vez escritos estos dos caracteres lo
que queda de línea se considera un comentario.

1 # include < iostream >


2 using namespace std ;
3
4 int main () {
5 // Este es un comentario en linea
6 cout << " Mi segundo programa en C ++ " << endl ;
7 /*
8 Este es un comentario en bloque
9 !!!
10 */
11
12 return 0;
13 }

1.7. Variables
Lo contrario de una salida de datos (mensajes en pantalla) es una entrada de datos. En la entrada
de datos el usuario escribe un dato por teclado, y éste dato es recogido por el ordenador.

Figura 1.10: Esquema variable

Sin embargo, para poder recoger los datos necesitamos antes tener un recipiente en donde guar-
darlo, y éste será la variable. A continuación, se muestra un programa que emplea variables para
almacenar un texto y un número.

1 # include < iostream >


2 using namespace std ;
3
4 int main (){
5 // Definimos las variables ...
6 string frase = " Codigo Internacional Telefonico para Ecuador " ;
7 int numero = 593;
8
9 // Salida en pantalla de las variables ...
10 cout << frase << " : " << numero << endl ;
11
12 return 0;
13 }

Una variable es una parte de la memoria del ordenador en donde se almacena un dato.
CAPÍTULO 1. INTRODUCCIÓN A C++ 8

Cada variable necesita un nombre que la identifique, y que la diferencie de las demás. Adicio-
nalmente, en C++ debemos indicar el tipo de dato que queremos almacenar (número, cadena
de letras, etc.).

Al dato que se almacena en la variable se le llama valor. Podemos cambiar el valor (el dato que
se almacena) de una variable, siempre que sea del tipo indicado.

1.7.1. Tipos de variables


Existen varios tipos de variables, y cada uno corresponde a un tamaño máximo de un número,
un carácter o cadena de caracteres. Cuanto mayor sea el número que pueda admitir, más espacio
en memoria ocupará. Los principales tipos de variables se enlistan a continuación:

1. bool: Las variables de este tipo sólo pueden tomar dos valores true o false. Sirven para
evaluar expresiones lógicas.
Por lo general utiliza 1 byte de memoria, valores: true o false.

2. char: Es el tipo básico alfanumérico, es decir que puede contener un carácter, un dígito
numérico o un signo de puntuación.
Utiliza generalmente 1 byte de memoria, permite almacenar un carácter, valores; 256
caracteres.

3. unsigned short int: Las variables enteras almacenan números enteros dentro de los límites
de su tamaño, a su vez, ese tamaño depende de la plataforma del compilador, y del número
de bits que use por palabra de memoria
Utiliza generalmente 2 bytes de memoria, valores: de 0 a 65535.

4. unsigned long int:


Utiliza generalmente 4 bytes de memoria, valores: de 0 a 4294967295.

5. int:
Utiliza generalmente 4 bytes de memoria, valores: de -2147483648 a 2147483647.

6. float: Las variables de este tipo almacenan números en formato de coma flotante y expo-
nente, para entendernos, son números con decimales.
Utiliza generalmente 4 bytes de memoria, valores: de 1,2 × 10−38 a 3,4 × 10+38 .

7. double: Las variables de este tipo almacenan números en formato de coma flotante y
exponente, al igual que float, pero usan mayor precisión.
Utiliza generalmente 8 bytes de memoria, valores: de 2,3 × 10−308 a 1,8 × 10+308 .

A continuación, un programa que muestra algunos límites numéricos:


1 # include < iostream >
2 # include < limits >
3 using namespace std ;
4
5 int main () {
6 cout << " Maximo entero : " << numeric_limits < int >:: max () << endl ;
7 cout << " Minimo entero : " << numeric_limits < int >:: min () << endl ;
8 cout << " Maximo float : " << numeric_limits < float >:: max () << endl ;
9 cout << " Minimo float : " << numeric_limits < float >:: min () << endl ;
10
CAPÍTULO 1. INTRODUCCIÓN A C++ 9

11 return 0;
12 }

En el siguiente enlace se muestran todos los tipos de variables que admite C++:
⋄ http://www.cplusplus.com/doc/tutorial/variables/

1.8. Entrada y salida de datos


En C++ se puede enviar información a la salida estándar, que como se explicó previamente está
conectada por defecto al monitor o pantalla, mediante la instrucción cout. La sintaxis básica es
la siguiente:
1 cout << expresion 1 << expresion 2 << ... << endl ;

La instrucción anterior se encarga de convertir el resultado de cada expresión en una cadena de


caracteres y de enviar dicha cadena a la salida estándar.

Para la lectura de datos se emplea la instrucción cin. La sintaxis básica es la siguiente:


1 cin >> variable 1 >> variable 2 >> ... >> variable n ;

donde: variable 1, variable 2 y variable n son variables. Esta instrucción provoca la lectura de
datos desde la entrada estándar, que por defecto está asociada al teclado, y su almacenamiento
en las variables especificadas en la instrucción.

Los nombres de las instrucciones anteriores provienen de Character OUTput y Character INput.

A continuación, un programa que muestra la entrada y salida de datos:


1 # include < iostream >
2 using namespace std ;
3
4 int main () {
5 // Definimos dos variables a y b del tipo double
6 double a ;
7 double b ;
8 // Se imprime un mensaje al usuario
9 cout << " Ingrese dos numeros " << endl ;
10 // Se leen dos numeros ingresados a traves del teclado
11 cin >> a ;
12 cin >> b ;
13 // Se imprime un mensaje y la suma de los dos numeros
14 cout << " La suma de los numeros ingresados es : " << a + b << endl ;
15
16 return 0;
17 }

1.9. Operadores
Los operadores son elementos que disparan ciertos cálculos cuando son aplicados a variables u
otros objetos en una expresión. Primero revisemos algunas definiciones importantes:
CAPÍTULO 1. INTRODUCCIÓN A C++ 10

Operando: cada una de las cantidades, constantes o variables o expresiones que intervie-
nen en una expresión.
Expresión: según el diccionario, “Conjunto de términos que representan una cantidad”,
entre nosotros es cualquier conjunto de operadores y operandos, que dan como resultado
una cantidad.
Existe una división en los operadores atendiendo al número de operandos que afectan (unitarios,
binarios o ternarios).

1.9.1. Operadores aritméticos


Son usados para crear expresiones matemáticas. Existen dos operadores aritméticos unitarios +
y - que tienen la siguiente sintaxis:
+ <expresión>
- <expresión>
Asignan valores positivos o negativos a la expresión a la que se aplican.
1 # include < iostream >
2 using namespace std ;
3
4 int main (){
5 int a =5;
6 cout << " Numero positivo : " << + a << endl ;
7 cout << " Numero negativo : " << -a << endl ;
8
9 return 0;
10 }

En cuanto a los operadores binarios existen varios: ’+’, ’-’, ’*’ y ’/’, tienen un comportamiento
análogo, ya que admiten variables enteras y de coma flotante.
<expresión> + <expresión>
<expresión> - <expresión>
<expresión> * <expresión>
<expresión> / <expresión>
Evidentemente se trata de las conocidísimas operaciones aritméticas de suma, resta, multiplica-
ción y división.
1 # include < iostream >
2 using namespace std ;
3
4 int main (){
5 int a , b ;
6 cout << " Ingrese dos numeros enteros positivos : " << endl ;
7 cin >> a >> b ;
8 // Empleamos operadores binarios
9 cout << " Suma : " << a + b << endl ;
10 cout << " Resta : " << a - b << endl ;
11 cout << " Multiplicacion : " << a * b << endl ;
12 cout << " Division : " << a / b << endl ;
13
14 return 0;
15 }
CAPÍTULO 1. INTRODUCCIÓN A C++ 11

El operador de módulo %, devuelve el resto de la división entera del primer operando entre el
segundo. Por esta razón, no puede ser aplicado a operandos en coma flotante.

<expresión> % <expresión>

Otras operaciones como la potencia y radicación se encuentran definidas dentro de la biblio-


teca cmath, por lo cual, para utilizar dichas operaciones se debe incluir la siguiente sentencia:
#include <cmath>.

Función Descripción
sqrt(x) Raíz cuadrada
exp(x) Potencia euleriana
log(x) Logaritmo neperiano
log10(x) Logaritmo en base 10
ceil(x) Redondea al entero superior más próximo
floor(x) Redondea al entero inferior más cercano
fabs(x) Valor absoluto
cos(x) Coseno
sin(x) Seno
tan(x) Tangente

Cuadro 1.1: Algunas funciones de la biblioteca cmath

A continuación, un programa que realiza las operaciones de potencia y radicación:


1 # include < iostream >
2 # include < cmath >
3 using namespace std ;
4
5 int main (){
6 int a ;
7 cout << " Ingrese un numero positivo " << endl ;
8 cin >> a ;
9
10 cout << " La raiz cuadrada es : " << sqrt ( a ) << endl ;
11 cout << " El cubo del numero es : " << pow (a ,3) << endl ;
12 cout << " Logaritmo neperiano : " << log ( a ) << endl ;
13 cout << " El resto de dividir para 2 es : " << a %2 << endl ;
14
15 return 0;
16 }

Por último, otros dos operadores unitarios un tanto especiales, ya que sólo pueden trabajar sobre
variables, pues realizan una asignación (semiautomática). Se trata de los operadores ’++’y ’- -’.
El primero de ellos incrementa el valor del operando y el segundo lo decrementa, ambos en una
unidad.

<variable> ++ (post-incremento)
++ <variable> (pre-incremento)
<variable>-- (post-decremento)
-- <variable> (pre-decremento)

A continuación, un programa que facilita el entendimiento de los operadores de incremento y


decremento:
CAPÍTULO 1. INTRODUCCIÓN A C++ 12

1 # include < iostream >


2 using namespace std ;
3
4 int main (){
5 int a =1 , b =2 , c =3;
6 cout << " a ++ : " << a ++ << endl ;
7 cout << " ++ b * 2 : " << ++ b * 2 << endl ;
8 cout << " a : " << a << " y b : " << b << endl ;
9 cout << "b - - : " << b - - << endl ;
10 cout << " ++ c * 3: " << ++ c * 3 << endl ;
11 cout << " c ++ : " << c ++ << endl ;
12 cout << " a ++ + --b + c - - : " << a ++ + --b + c - - << endl ;
13
14 return 0;
15 }

1.9.2. Operadores de asignación


Existen varios operadores de asignación, el más evidente y el más usado es el =, pero no es el
único. El operador de asignación se utiliza para asignar un valor a una variable. Su sintaxis es
la siguiente:

<variable> <operador de asignación> <expresión>

Si se desea escribir una expresión como a = a+8, en la que se incrementa el valor de una va-
riable en una cantidad, entonces se puede utilizar el operador += de la siguiente forma: a += 8.
Esta última expresión incrementa la variable que aparece a la izquierda del operador += en una
cantidad dada.

A continuación, un programa que emplea varios operadores de asignación:


1 # include < iostream >
2 using namespace std ;
3
4 int main () {
5 int a , b , c ;
6 cout << " Ingrese tres numeros " << endl ;
7 cin >> a >> b >> c ;
8 a += 8;
9 cout << " El primer numero incrementado en 8 unidades : " <<
10 a << endl ;
11 b *= 3;
12 cout << " El triple del segundo numero es : " << b << endl ;
13 c *= c ;
14 cout << " El cuadrado del tercer numero es : " << c << endl ;
15
16 return 0;
17 }

Los operadores -=, *=, /= y %= tienen un comportamiento análogo a += pero aplicados a la resta,
multiplicación, división y módulo, respectivamente.
CAPÍTULO 1. INTRODUCCIÓN A C++ 13

Forma larga Forma corta


a = a + b; a += b;
a = a - b; a -= b;
a = a * b; a *= b;
a = a / b; a /= b;
a = a % b; a %= b;

Cuadro 1.2: Operadores de asignación

En los siguientes enlaces se muestran otros ejemplos sobre operadores:


⋄ http://y2u.be/dKNZKgsv9og
⋄ http://y2u.be/Y0n-P5qZ1d0
⋄ http://y2u.be/nrmfgM5t2go
⋄ http://y2u.be/D_QLgW0KnmI

1.10. Constantes
Una constante es una variable que almacena un valor que no varía a lo largo de la ejecución
de un programa. En C++ se puede definir una constante anteponiendo al tipo de variable la
palabra reservada const.

Por convención, los nombres de las constantes se escriben en mayúsculas y se ubican en las
primeras líneas del código.

A continuación, un programa que emplea constantes:


1 # include < iostream >
2 using namespace std ;
3
4 int main () {
5 const double IVA = 0.12;
6 const double PROPINA = 0.05;
7 double precio ;
8 cout << " Ingrese el valor del consumo " << endl ;
9 cin >> precio ;
10 precio
11 cout << " Su factura con IVA es de : " << precio + precio * IVA
12 << " dolares " << endl ;
13 cout << " Su factura incluida la propina es de : "
14 << precio + precio * ( IVA + PROPINA ) << " dolares "
15 << endl ;
16 cout << " Gracias por su consumo " << endl ;
17
18 return 0;
19 }
CAPÍTULO 1. INTRODUCCIÓN A C++ 14

1.11. Conversión de tipos


Cuando se realizan operaciones aritméticas con operandos de distinto tipo, C++ realiza una
conversión implícita del operando cuyo tipo tiene un rango de representación menor al tipo del
operando cuyo rango de representación es mayor; de esta forma se puede realizar la operación.

En el caso de una operación entre un número entero y un número real el entero se convierte
implícitamente a real, realizándose la operación entre reales.

A continuación, un programa que ejemplifica la conversión de tipos:


1 # include < iostream >
2 using namespace std ;
3
4 int main () {
5 int a = 32 ,
6 double b = 10.5;
7 // Suma de entero y real
8 cout << a + b << endl :
9 // Division de entero a entero
10 cout << a /5 << endl ;
11 // Division entero a real
12 cout << a /5.0 << endl :
13 // Conversion explicita
14 cout << ( float ) a /5 << endl ;
15
16 return 0;
17 }

En la línea 14 se utiliza el operador de conversión explícita de tipos. Éste es un operador unitario


cuya sintaxis es:

(tipo) operando

El efecto del operador es convertir el operando al tipo tipo.


2 | Sentencias condicionales y repe-
titivas

2.1. Expresiones lógicas


Una expresión lógica es aquella que produce un resultado de verdadero o falso. Se puede construir
una expresión lógica simple utilizando un literal de tipo lógico, es decir, true o false, o combi-
nado dos expresiones aritméticas mediante un operador relacional. Los principales operadores
relacionales de C++ son:

Operador Significado
< menor que
> mayor que
<= menor o igual que
>= mayor o igual que
== igual que
!= distinto de

A continuación, un programa que verifica si los dos números enteros ingresados por el usuario
son iguales:

1 # include < iostream >


2 using namespace std ;
3
4 int main (){
5 int a , b ;
6 bool res ;
7 cout << " Ingrese dos numeros enteros " << endl ;
8 cin >> a >> b ;
9
10 // Expresion logica simple
11 res = a == b ;
12 cout << " Resultado : " << res << endl ;
13
14 return 0;
15 }

2.1.1. Expresiones lógicas compuestas


Una expresión lógica compuesta es aquella formada por expresiones lógicas combinadas mediante
operadores lógicos. Los principales operadores lógicos de C++ son:

15
CAPÍTULO 2. SENTENCIAS CONDICIONALES Y REPETITIVAS 16

Operador Significado
&& conjunción
|| disyunción
! negación

Una expresión lógica compuesta puede contener operadores aritméticos, relacionales y lógicos.

Se recomienda emplear paréntesis en las expresiones lógicas compuestas, con el fin de separar e
identificar cada una de las expresiones lógicas simples.

A continuación, un programa que verifica si los dos números ingresados por el usuario son iguales
y pares a la vez:
1 # include < iostream >
2 using namespace std ;
3
4 int main (){
5 int a , b ;
6 bool res ;
7 cout << " Ingrese dos numeros enteros " << endl ;
8 cin >> a >> b ;
9
10 // Expresion logica compuesta
11 res = ( a == b ) && ( a %2 == 0);
12 cout << " Resultado : " << res << endl ;
13
14 return 0;
15 }

En los siguientes enlaces se presentan a mayor detalle los operadores lógicos y relacionales:
http://y2u.be/7rnxBgZsWQ0
http://y2u.be/MUznvpgtn9g

2.2. Sentencias condicionales


Las sentencias condicionales permiten seleccionar qué codigo ejecutar en función del resultado
de evaluar ciertas expresiones lógicas.

2.2.1. Sentencia if-else


La sentencia if-else permite ejecutar un código de manera condicional en función del resultado
de evaluar una condición lógica. Su sintaxis es la siguiente:

if(expresión lógica){
bloque de instrucciones 1
} else {
bloque de instrucciones 2
}
CAPÍTULO 2. SENTENCIAS CONDICIONALES Y REPETITIVAS 17

Figura 2.1: Flujograma sentencia if - else

La sentencia if-else primero evalúa la expresión lógica (condición). Si la expresión es verdadera,


entonces se ejecuta el bloque de instrucciones 1, en caso de ser falsa se ejecuta el bloque de
instrucciones 2.

Sobre la sintaxis de la sentencia if-else se debe tener en cuenta lo siguiente:

La expresión lógica debe aparecer encerrada entre paréntesis.

Los bloques de instrucciones se encierran entre llaves.

La parte else es opcional.

Se recomienda escribir los bloques de instrucciones un poco a la derecha (con sangrado o


indentados), pues esto aumenta la legibilidad del programa.

A continuación, un ejemplo que permite identificar si el número entero ingresado por el usuario
es par o impar:
1 # include < iostream >
2 using namespace std ;
3
4 int main (){
5 int a ;
6 cout << " Ingrese un numero entero " << endl ;
7 cin >> a ;
8
9 // Sentencia condicional
10 if (( a % 2) == 0){
11 cout << " El numero ingresado es par " << endl ;
12 } else {
13 cout << " El numero ingresado es impar " << endl ;
14 }
15 return 0;
16 }

Las raíces reales de un polinomio de segundo grado de la forma: ax2 + bx + c = 0, pueden ser
obtenidas mediante la expresión:

−b ± b2 − 4ac
x1,2 =
2a
a continuación, se presenta un programa que permite obtener dichas raíces en el caso que existan.
CAPÍTULO 2. SENTENCIAS CONDICIONALES Y REPETITIVAS 18

1 # include < iostream >


2 # include < cmath >
3 using namespace std ;
4
5 int main (){
6 double a , b , c , d ;
7 cout << " Raices del polinomio : ax ^2 + bx + c = 0 " << endl ;
8 cout << " Ingrese los coeficientes : " << endl ;
9 cin >> a >> b >> c ;
10
11 // Calculo del discriminante
12 d = pow (b ,2) - 4* a * c ;
13
14 // Sentencia condicional
15 if ( d < 0){
16 cout << " El polinomio no tiene raices reales " << endl ;
17 } else if ( d == 0) {
18 cout << " Las raices son iguales a : "
19 << ( float ) -b /(2* a ) << endl ;
20 } else {
21 cout << " La primera raiz es : "
22 << ( float ) ( -b - pow (d ,0.5))/(2* a ) << endl ;
23 cout << " La segunda raiz es : "
24 << ( float ) ( - b + pow (d ,0.5))/(2* a ) << endl ;
25 }
26 return 0;
27 }

2.2.2. Sentencia switch


La sentencia switch permite realizar una selección múltiple.

Figura 2.2: Flujograma sentencia switch

La sintaxis de la sentencia switch es la siguiente:


CAPÍTULO 2. SENTENCIAS CONDICIONALES Y REPETITIVAS 19

switch (expresion){
case literal1:
bloque de instrucciones 1
break;
case literal2:
bloque de instrucciones 2
break;
......
case literaln:
bloque de instrucciones n
break;
default:
bloque de instrucciones por defecto
break;
}

La sentencia switch primero evalúa la expresión, misma que aparece en el paréntesis. La expresión
debe producir un resultado de tipo entero, caractér o lógico. A continuación, se compara el
resultado de evaluar la expresión con los literales que aparecen en los casos.

1 # include < iostream >


2 using namespace std ;
3
4 int main (){
5 cout << " Ingrese la opcion a ejecutar : " << endl ;
6 char opcion ;
7 cin >> opcion ;
8
9 switch ( opcion ){
10 case ’a ’:
11 cout << " Ha seleccionado la opcion a " << endl ;
12 break ;
13 case ’b ’:
14 cout << " Ha seleccionado la opcion b " << endl ;
15 break ;
16 case ’c ’:
17 cout << " Ha seleccionado la opcion c " << endl ;
18 break ;
19 default :
20 cout << " Ha ingresado una opcion incorrecta " << endl ;
21 }
22
23 return 0;
24 }

Si el resultado de evaluar la expresión coincide con el literal de algún caso, entonces se ejecuta
el conjunto de instrucciones asociado al caso y el conjunto de instrucciones asociadas al resto de
casos que aparecen con posterioridad hasta que se ejecuta la instrucción break que provoca que
se salga de la ejecución de la instrucción switch.

A continuación, se presenta un programa que convierte centímetros a pulgadas y libras a kilo-


gramos:
CAPÍTULO 2. SENTENCIAS CONDICIONALES Y REPETITIVAS 20

1 # include < iostream >


2 using namespace std ;
3
4 int main () {
5 int x ;
6 float pulgadas , kilo , distancia , peso ;
7 cout << " Que desea convertir , distancia o peso ? " << endl ;
8 cout << " Escriba 1 para distancia o 2 para peso " << endl ;
9 cin >> x ;
10
11 switch ( x ){
12 case 1:
13 cout << " Ingrese la distancia en centimetros : " << endl ;
14 cin >> distancia ;
15 pulgadas = distancia / 2.54;
16 cout << " Las pulgadas son : " << pulgadas << endl ;
17 break ;
18 case 2:
19 cout << " Ingrese el peso en libras : " << endl ;
20 cin >> peso ;
21 kilo = peso * (1 / 2.21);
22 cout << " Los kilogramos son : " << kilo << endl ;
23 break ;
24 default :
25 cout << " Opcion no valida " << endl ;
26 }
27
28 return 0;
29 }

2.3. Sentencias repetitivas


Las sentencias repetitivas o iterativas, también llamadas ciclos o bucles, permiten ejecutar un
conjunto de instrucciones de manera reiterada mientras que se verifique una condición lógica.

2.3.1. Sentencia while


La sentencia while permite ejecutar un bloque de instrucciones mientras la expresión lógica sea
verdadera.

Figura 2.3: Flujograma sentencia while


CAPÍTULO 2. SENTENCIAS CONDICIONALES Y REPETITIVAS 21

La sintaxis es la siguiente:

while(expresión lógica){
bloque de instrucciones
}

En primer lugar, se evalúa la expresión lógica. Si el resultado de la evaluación es el valor falso, en-
tonces termina la ejecución de la instrucción while. Si el resultado de la evaluación es verdadero,
entonces se ejecuta el conjunto de instrucciones asociado a la instrucción while. Una vez ejecu-
tado el conjunto de instrucciones se vuelve a evaluar la expresión lógica y se procede como antes.

A continuación, un programa que solicita un número mayor a 10 para finalizar el proceso:


1 # include < iostream >
2 using namespace std ;
3
4 int main () {
5 int numero ;
6 cout << " Ingrese un numero entero " << endl ;
7 cin >> numero ;
8
9 // Sentencia repetitiva
10 while ( numero <= 10){
11 cout << " Ingrese otro numero : " ;
12 cin >> numero ;
13 }
14 return 0;
15 }

Ahora analicemos un programa que suma todos los números ingresados por el usuario, para
finalizar el proceso es necesario teclear el número 999:
1 # include < iostream >
2 using namespace std ;
3
4 int main () {
5 int num , suma ;
6
7 cout << " ------- Programa Suma ------- " << endl ;
8 num = 0;
9 suma = 0;
10
11 while ( num != 999){
12 suma += num ;
13 cout << " Ingresa un numero ( teclea 999 para terminar ): "
14 << endl ;
15 cin >> num ;
16 }
17 cout << " La suma total es : " << suma << endl ;
18
19 return 0;
20 }
CAPÍTULO 2. SENTENCIAS CONDICIONALES Y REPETITIVAS 22

Finalmente, se muestra un programa que recibe dos números a, x con |x| < 1 y determina el
menor valor de n para el que se cumple la expresión:
a ∑
n
− axi < 10−7
1 − x i=0

1 # include < iostream >


2 # include < cmath >
3 using namespace std ;
4
5 int main () {
6 double a , x ;
7 const double ERROR = pow (10 , -7);
8 cout << " Ingrese los valores de a y x : " << endl ;
9 cin >> a >> x ;
10
11 // Sentencia condicional sobre x
12 if ( x > -1 && x < 1){
13 double sum = 0;
14 int i = 0;
15 // Sentencia repetitiva para hallar el valor de n
16 while ( a /(1 - x ) - sum > ERROR ){
17 sum += a * pow (x , i );
18 i ++;
19 }
20 cout << " El valor de n buscado es : " << i -1 << endl ;
21 } else {
22 cout << " El valor de x no es valido " << endl ;
23 }
24
25 return 0;
26 }

2.3.2. Sentencia do - while


La sentencia do-while es muy parecida a la sentencia while. La principal diferencia con la senten-
cia while es que en la sentencia do-while el conjunto de instrucciones se ejecutará por lo menos
una vez.

Figura 2.4: Flujograma sentencia do - while


CAPÍTULO 2. SENTENCIAS CONDICIONALES Y REPETITIVAS 23

La sintaxis es la siguiente:
do {
bloque de instrucciones
} while (expresión lógica);
Una vez ejecutado el conjunto de instrucciones se evalúa la expresión lógica. Si el resultado de
la evaluación es verdadero se vuelve a ejecutar el conjunto de instrucciones, así hasta que la
expresión lógica sea falsa, en cuyo momento termina la ejecución de la instrucción.

A continuación, un programa que transforma los grados centígrados a fahrenheit y viceversa:


1 # include < iostream >
2 using namespace std ;
3
4 int main (){
5 int opcion ;
6 float centi , fahre ;
7 bool terminar = false ;
8 cout << " --- Conversion de temperaturas ---" << endl ;
9
10 do {
11 cout << " Escoja una opcion : " << endl ;
12 cout << " 1. Convertir Grados centigrados a Fahrenheit : "
13 << endl ;
14 cout << " 2. Convertir Grados Fahrenheit a Centigrados : "
15 << endl ;
16 cout << " 3. Salir del programa " << endl ;
17 cin >> opcion ;
18
19 switch ( opcion ){
20 case 1:
21 cout << " Ingrese los grados centigrados : " << endl ;
22 cin >> centi ;
23 fahre = ( centi *1.8) + 32;
24 cout << " Su equivalente en fahrenheit es : "
25 << fahre << endl ;
26 break ;
27 case 2:
28 cout << " Ingrese los grados fahrenheit : " << endl ;
29 cin >> fahre ;
30 centi = ( fahre - 32)/1.8;
31 cout << " Su equivalente en centigrados es : "
32 << centi << endl ;
33 break ;
34 default :
35 cout << " Gracias por usar el programa " << endl ;
36 terminar = true ;
37 }
38 } while (! terminar );
39
40 return 0;
41 }
CAPÍTULO 2. SENTENCIAS CONDICIONALES Y REPETITIVAS 24

2.3.3. Sentencia for


La sentencia for está pensada principalmente para expresar ciclos que se ejecutan un número fijo
de veces. La sentencia for resulta más legible que las sentencias while y do while, pues permite
expresar la iniciación, incremento y condición lógica asociada a la variable que controla el ciclo
en una única línea al principio del ciclo.

Figura 2.5: Flujograma sentencia for

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


for (inicializacion; expresión lógica; incremento){
conjunto de instrucciones
}
El funcionamiento de la instrucción es el siguiente:
Se ejecuta la inicialización del ciclo, esta expresión se ejecuta una única vez.
A continuación se evalúa la expresión lógica. Mientras que el resultado de evaluar la ex-
presión lógica sea verdadero se ejecuta el conjunto de instrucciones.
Por último, se realiza el incremento o decremento del contador.
A continuación, un programa que calcula el factorial de un número entero:
1 # include < iostream >
2 using namespace std ;
3
4 int main () {
5 int numero , fact = 1;
6 cout << " Introduzca un entero positivo : " << endl ;
7 cin >> numero ;
8 for ( int i = 1; i <= numero ; i ++){
9 fact *= i ;
10 }
11 cout << " El factorial de " << numero << " es " << fact << endl ;
12 return 0;
13 }

Para finalizar esta sección revisemos un programa que determina si un número entero positivo
ingresado por el usuario es primo:
CAPÍTULO 2. SENTENCIAS CONDICIONALES Y REPETITIVAS 25

1 # include < iostream >


2 using namespace std ;
3
4 int main (){
5 int numero , i ;
6 bool primo = false ;
7
8 cout << " Ingrese un entero positivo (0 para terminar ): "
9 << endl ;
10 cin >> numero ;
11
12 while ( numero != 0){
13 for ( i = 2; i < numero ; i ++){
14 if ( numero % i == 0)
15 primo = true ;
16 }
17 if ( primo ){
18 cout << " El numero " << numero << " no es primo "
19 << endl ;
20 } else {
21 cout << " El numero " << numero << " es primo " << endl ;
22 }
23 cout << " Ingreses un entero positivo (0 para terminar ): " ;
24 cin >> numero ;
25 primo = false ;
26 }
27
28 return 0;
29 }

2.4. Ámbito de las variables


C++ proporciona unas reglas muy flexibles para elegir el lugar en el que se define una variable.
En función del lugar de su definición, una variable tiene un ámbito, que es la zona del programa
en la que puede ser usada. A continuación, se enumeran las principales reglas sobre el lugar de
definición y ámbito de las variables:

Una variable puede definirse en cualquier lugar en el que se puede escribir una instrucción.
El ámbito de la variable abarca desde su lugar de definición hasta el final del bloque
más interno en que se ha definido la variable (un bloque es un conjunto de instrucciones
encerradas entre llaves).

Se puede definir una variable en la parte de iniciación de una sentencia for. En ese caso el
ámbito de la variable es el cuerpo y cabecera de la instrucción for.

Se puede definir más de una vez una misma variable, es decir, con el mismo identifica-
dor dentro del cuerpo de una función siempre que las definiciones ocurran en bloques no
solapados o en bloques solapados de distinto nivel de profundidad.

Un código que intenta explicar de mejor modo el ámbito de aplicación de las variables:
CAPÍTULO 2. SENTENCIAS CONDICIONALES Y REPETITIVAS 26

1 # include < iostream >


2 using namespace std ;
3
4 int main (){
5 int z =3;
6 int x =8;
7
8 if ( true ){
9 int x = 0;
10 cout << x << endl ;
11 }
12
13 for ( int x = 1; x < 11; x ++){
14 cout << x << endl ;
15 }
16 cout << x << endl ;
17
18 if ( true ){
19 int z =4;
20 cout << z << endl ;
21 }
22 cout << z << endl ;
23
24 return 0;
25 }

Una aplicación importante del ámbito de las variables se presenta en la sentencia switch, al
poner entre llaves los bloques de instrucciones y el break; de cada caso. Al hacer esto si definimos
variables dentro de las llaves solamente serán efectivas para ese caso, pero si no hacemos uso
de ellas las variables definidas dentro de un caso serán validas para todos los casos dentro de la
sentencia switch.

C++ permite la escritura de valores numéricos en notación científica, para ello emplearemos la
letra e como potencia 10. Es decir:
3.000.000 = 3.0e6
0.0000567 = 5.67e-5
3 | Datos estructurados

En los primeros capítulos del curso se han estudiado los tipos de datos primitivos de C++, es
decir, los tipos de datos numéricos, lógicos y carácter. Ahora, analizaremos los tipos de datos
estructurados, estos se utilizan para almacenar colecciones de datos del mismo tipo o diferentes
tales como: vectores, estructuras y cadenas de caracteres.

3.1. Vectores
Un vector es una estructura unidimensional que almacena una secuencia de datos del mismo
tipo. Los elementos de un vector se almacenan en zonas contiguas de memoria y se puede acceder
a ellos de manera directa mediante un índice o posición.

Figura 3.1: Estructura de un vector

Para definir un vector se utiliza la siguiente sintaxis:

tipo nombre[tamaño];

donde tipo es el tipo de los elementos del vector, nombre es el nombre de la variable que sirve
para referenciar al vector y tamaño indica el número de elementos del vector.

En C++ los índices de los vectores empiezan en cero.

El tamaño debe expresarse mediante una expresión constante de tipo entero.

No es válido definir el tamaño de un vector a partir de una variable ingresada por el usuario.

int x; // Variable
cout << "Ingrese un entero" << endl;
cin >> x;
double vector[x]; // No valido

27
CAPÍTULO 3. DATOS ESTRUCTURADOS 28

Para acceder a los elementos de un vector se utiliza la sintaxis:

nombre[posicion];

donde nombre es el nombre de la variable de tipo vector y posicion es una expresión entera que
indica el índice del elemento del vector al que se quiere acceder.

La utilidad de los vectores es que permiten crear múltiples datos de un mismo tipo con una
única sentencia de definición y una única variable. Además, el acceso a los datos almacenados
en el vector es muy eficiente y sencillo a partir de su posición.

A continuación, un programa que almacena las componentes de dos vectores de R3 y obtiene la


suma vectorial y el producto interno:
1 # include < iostream >
2 using namespace std ;
3
4 int main (){
5 float vector1 [3];
6 float vector2 [3];
7 float Suma [3];
8 float Producto = 0;
9 // Datos del primer vector
10 for ( int i =0; i <3; i ++){
11 cout << " Introduce la componente " << i + 1
12 << " del primer vector : " ;
13 cin >> vector1 [ i ];
14 }
15 // Datos del segundo vector
16 for ( int i =0; i <3; i ++){
17 cout << " Introduce la componente " << i + 1
18 << " del segundo vector : " ;
19 cin >> vector2 [ i ];
20 }
21 // Suma de vectores
22 for ( int i =0; i <3; i ++){
23 Suma [ i ] = vector1 [ i ] + vector2 [ i ];
24 }
25 // Producto interno
26 for ( int i =0; i <3; i ++){
27 Producto += vector1 [ i ] * vector2 [ i ];
28 }
29 // Mostramos el resultado
30 cout << " El vector suma es " ;
31 for ( int i =0; i <3; i ++)
32 cout << Suma [ i ] << " " ;
33 cout << endl ;
34
35 cout << " El producto interno es : " << Producto << endl ;
36
37 return 0;
38 }
CAPÍTULO 3. DATOS ESTRUCTURADOS 29

3.1.1. Inicialización de vectores


Se puede inicializar un vector en la misma definición de la variable. Para realizar esto hay que
encerrar entre llaves la lista de valores con que se inicia el vector, separando los valores entre sí
mediante comas.

A continuación, un programa que obtiene el total de gastos de una familia X:


1 # include < iostream >
2 using namespace std ;
3
4 int main (){
5 int gastos [6] = {200 , 150 , 100 , 250 , 300 , 285};
6 int suma ;
7
8 suma = 0;
9 for ( int i =0; i <=4; i ++){
10 suma += gastos [ i ];
11 };
12
13 cout << " El gasto total es : " << suma ;
14
15 return 0;
16 }

Si inicializamos un vector, no será necesario que indiquemos su tamaño, porque el compilador


lo puede saber contando los valores ingresados en la lista.

1 # include < iostream >


2 using namespace std ;
3
4 int main (){
5 int diasMes [] = {31 , 28 , 31 , 30 , 31 , 30 ,
6 31 , 31 , 30 , 31 , 30 , 31};
7
8 string Mes [] = { " Enero " , " Febrero " , " Marzo " , " Abril " , " Mayo " ,
9 " Junio " , " Julio " , " Agosto " , " Septiembre " , " Octubre " ,
10 " Noviembre " , " Diciembre " };
11
12 for ( int i =0; i <12; i ++){
13 cout << " El mes de " << Mes [ i ] << " tiene " << diasMes [ i ]
14 << " dias " << endl ;
15 };
16
17 return 0;
18 }

3.2. Matrices
Una matriz es una estructura bidimensional que almacena una secuencia de datos del mismo tipo
en filas y columnas. Los elementos de una matriz se almacenan en zonas contiguas de memoria
y se puede acceder a ellos de manera directa mediante la posición (fila y columna).
CAPÍTULO 3. DATOS ESTRUCTURADOS 30

Figura 3.2: Estructura de una matriz

La sintaxis para declarar una matriz en C++ es la siguiente:

tipo nombreMatriz[filas][columnas];

donde tipo corresponde al tipo de elementos que se van almacenar, nombreMatriz corresponde al
nombre de la estructura matriz, filas y columnas son expresiones enteras que indican el número
de filas y columnas, respectivamente, que tendrá la estructura matriz.

Al igual que en los vectores, los índices de las filas y columnas de una matriz empiezan en cero.

A continuación, un programa que suma dos matrices de orden 2 × 2:


1 # include < iostream >
2 using namespace std ;
3
4 int main (){
5 double matriz1 [2][2];
6 double matriz2 [2][2];
7 double Suma [2][2];
8
9 // Ingreso valores matriz 1
10 cout << " Matriz 1 " << endl ;
11 for ( int i =0; i <2; i ++){
12 for ( int j =0; j <2; j ++){
13 cout << " Ingrese el elemento " << i +1 << j +1 << " : " ;
14 cin >> matriz1 [ i ][ j ];
15 }
16 }
17
18 // Ingreso valores matriz 2
19 cout << " Matriz 2 " << endl ;
20 for ( int i =0; i <2; i ++){
21 for ( int j =0; j <2; j ++){
22 cout << " Ingrese el elemento " << i +1 << j +1 << " : " ;
23 cin >> matriz2 [ i ][ j ];
24 }
25 }
26
27 // Suma de matrices
28 for ( int i =0; i <2; i ++){
29 for ( int j =0; j <2; j ++){
30 Suma [ i ][ j ] = matriz1 [ i ][ j ] + matriz2 [ i ][ j ];
31 }
CAPÍTULO 3. DATOS ESTRUCTURADOS 31

32 }
33
34 // Resultado
35 cout << " El resultado es : " << endl ;
36 for ( int i =0; i <2; i ++){
37 for ( int j =0; j <2; j ++){
38 cout << Suma [ i ][ j ] << " " ;
39 }
40 cout << endl ;
41 }
42 return 0;
43 }

En cuanto tenemos declarado una matriz, es posible asignarle valores a cada una de sus casillas,
evidentemente estos valores deben coincidir con el tipo de dato que le asignamos a dicha matriz.

3.2.1. Inicialización de matrices


Se puede inicializar una matriz en la misma definición de la variable. Para realizar esto hay que
encerrar entre llaves la lista de valores de cada fila con que se inicia la matriz, separando los
valores entre sí mediante comas.

A continuación, un programa que almacena la notas del examen de dos grupos de 10 alumnos
cada uno y compara el promedio de cada curso:
1 # include < iostream >
2 using namespace std ;
3
4 int main (){
5 int Notas [][10] = {{17 , 20 , 13 , 14 , 15 , 19 , 17 , 18 , 19 , 10} ,
6 {18 , 12 , 19 , 14 , 15 , 16 , 17 , 13 , 19 , 20 }};
7
8 // Promedio por curso
9 double prom1 =0 , prom2 =0;
10 for ( int i =0; i <10; i ++){
11 prom1 += 0.1* Notas [0][ i ];
12 prom2 += 0.1* Notas [1][ i ];
13 }
14
15 // Comparacion promedios
16 if ( prom1 > prom2 ){
17 cout << " El primer curso tiene mejor promedio " << endl ;
18 } else if ( prom1 < prom2 ){
19 cout << " El segundo curso tiene mejor promedio " << endl ;
20 } else {
21 cout << " Ambos cursos tienen igual promedio " << endl ;
22 }
23
24 return 0;
25 }
CAPÍTULO 3. DATOS ESTRUCTURADOS 32

Es posible inicializar matrices únicamente especificando el número de columnas. Por default, el


compilador analiza la lista de datos y contabiliza el número de filas.

3.3. Arrays
C++ permite trabajar con arrays multidimensionales, es decir, de varias dimensiones.

Figura 3.3: Estructura de un array

Para definir un array multidimensional de dimensión n se utiliza la siguiente sintaxis:

tipo nombreArray[tam1][tam2]...[tamn];

donde tipo es el tipo de los elementos del array, nombreArray es el nombre de la variable asocia-
da al array y tam1, tam2, . . . , tamn son expresiones constantes de tipo entero que indican los
tamaños de las distintas dimensiones del array.

A continuación, un programa que almacena las ventas de 4 tipos de productos de golosinas en


3 diferentes ciudades durante 2 días y calcula el total de ventas por producto:
1 # include < iostream >
2 using namespace std ;
3
4 int main (){
5 int Ventas [4][3][2] = {12 , 20 , 13 , 34 , 15 , 24 , 16 , 28 , 11 , 18 ,
6 31 , 25 , 23 , 34 , 21 , 10 , 18 , 28 , 31 , 17 ,
7 15 , 19 , 28 , 34};
8
9 // Vista Array
10 for ( int k =0; k <2; k ++){
11 for ( int i =0; i <4; i ++){
12 for ( int j =0; j <3; j ++){
13 cout << Ventas [ i ][ j ][ k ] << " " ;
14 }
15 cout << endl ;
16 }
17 cout << endl ;
18 cout << endl ;
19 };
20
21 // Total Ventas por producto
22 int TPro [4]; // Vector que almacena los totales
23 for ( int i =0; i <4; i ++){
24 TPro [ i ] = 0; // Inicializamos la celda
CAPÍTULO 3. DATOS ESTRUCTURADOS 33

25 for ( int j =0; j <3; j ++){


26 for ( int k =0; k <2; k ++){
27 TPro [ i ] += Ventas [ i ][ j ][ k ];
28 }
29 }
30 };
31
32 // Resultados por producto
33 for ( int i =0; i <4; i ++){
34 cout << " Total Producto " << i +1 << " : " << TPro [ i ] << endl ;
35 }
36
37 return 0;
38 }

Se debe tener mucho cuidado con el manejo de los índices en los arrays para no confundir las
dimensiones sobre las cuales se pretende trabajar.

3.4. Estructuras
C++ permite que el programador defina nuevos tipos de datos. Una forma de hacerlo es me-
diante una estructura. Una estructura define un nuevo tipo de dato que está compuesto por una
colección de datos de tipos existentes ya sean tipos básicos o estructurados como arrays, cadenas
de caracteres o estructuras ya definidas. Las estructuras son muy útiles pues permiten definir
tipos de datos que se ajustan con exactitud a las necesidades de representación de cualquier
objeto. Con una estructura se puede representar un alumno, un seguro, un vehículo, etc.
Rojo
Toyota
Modelo 2018
230 CV

Verde
Ford
Modelo 2016
280 CV

Azul
Mercedes Benz
Modelo 2020
350 CV

Figura 3.4: Ejemplo de un estructura

La sintaxis de definición de una estructura es:


struct nombre{
tipo1 campo1;
tipo2 campo2;
...
tipon campon;
};
La definición de una estructura comienza con la palabra reservada struct, seguida del nombre
del nuevo tipo que se está definiendo. A continuación, y encerrados entre llaves, aparecen los
CAPÍTULO 3. DATOS ESTRUCTURADOS 34

datos que componen el nuevo tipo. A cada uno de los datos se le llama campo o miembro. La
definición de cada campo tiene la estructura de la definición de una variable, es decir, en primer
lugar se especifica el tipo del campo y a continuación su nombre.

A continuación, un programa que registra la información de 5 individuos definiendo una nueva


estructura y obtiene la edad promedio:
1 # include < iostream >
2 using namespace std ;
3
4 // Definicion nueva estructura
5 struct persona {
6 char nombre [15];
7 int edad ;
8 char sexo [15];
9 int telefono ;
10 };
11
12 int main (){
13 persona amigo [5];
14 // Ingreso de informacion
15 for ( int i = 0; i < 5; i ++){
16 cout << " Escriba el Nombre " << i +1 << ":";
17 cin >> amigo [ i ]. nombre ;
18 cout << " \ nEscriba la Edad de " << i +1 << " : " ;
19 cin >> amigo [ i ]. edad ;
20 cout << " \ nEscriba el sexo " << i +1 << ":";
21 cin >> amigo [ i ]. sexo ;
22 cout << " \ nEscriba el Telefono de " << i +1 << " : " ;
23 cin >> amigo [ i ]. telefono ;
24 cout << endl ;
25 };
26
27 // Impresion registros
28 cout << " El registro de personas que se introdujeron es :\ n \ n " ;
29 for ( int i = 0; i < 5; i ++){
30 cout << " \ t " << amigo [ i ]. nombre ;
31 cout << " \ t " << amigo [ i ]. edad ;
32 cout << " \ t " << amigo [ i ]. sexo ;
33 cout << " \ t " << amigo [ i ]. telefono << " \ n \ n " ;
34 };
35
36 // Edad promedio
37 double EProm = 0;
38 for ( int i = 0; i < 5; i ++){
39 EProm += 0.2* amigo [ i ]. edad ;
40 };
41
42 cout << " La edad promedio es : " << EProm << endl ;
43
44 return 0;
45 }
CAPÍTULO 3. DATOS ESTRUCTURADOS 35

3.4.1. Estructuras Anidadas


C++ permite que se incluya un struct dentro de otro, de este modo, podemos encontrarnos con
un registro que tenga varios datos, y a su vez ocurra que uno de esos datos se encuentre formado
por varios datos más sencillos.

A continuación, un ejemplo en el que se emplea una estructura anidada:


1 # include < iostream >
2 # include < string >
3 using namespace std ;
4
5 struct FechaNac {
6 int anio , mes , dia ;
7 };
8
9 struct estudiante {
10 string nombre , genero ;
11 FechaNac fecha ;
12 };
13
14 int main (){
15 estudiante E1 = { " Luis " , " Masculino " , {1997 , 10 , 29}};
16
17 cout << " Nombre del estudiante : " << E1 . nombre << endl ;
18 cout << " Genero : " << E1 . genero << endl ;
19 cout << " Fecha de nacimiento : " << E1 . fecha . dia << " / "
20 << E1 . fecha . mes << " / " << E1 . fecha . anio << endl ;
21 return 0;
22 }
4 | Números aleatorios

Una característica importante en la gran mayoría de lenguajes de programación es la posibilidad


de generar números aleatorios.

Los números aleatorios son pieza clave en la programación de juegos de azar, simulaciones,
cifrado de datos, en modelos estadísticos, etc. En esta capítulo aprenderemos a generar números
aleatorios para automatizar el proceso de llenado de arreglos y más.

4.1. Generación de números aleatorios enteros


Uno de los métodos más comunes para generar números aleatorios, llamado método congruen-
cial multiplicativo, comienza con un valor inicial x0 , llamado semilla, y luego se calcula de
manera recursiva los valores sucesivos xn , n ≥ 1, haciendo:

xn = axn−1 mód m

donde a y m son enteros positivos dados y lo anterior significa que axn−1 se divide entre m y el
residuo se considera como el valor de xn .
xn
Así, cada xn toma valores entre: 0, 1, . . . m − 1 y la expresión
(llamada número pseudoalea-
m
torio) se considera como una aproximación del valor de una variable aleatoria uniforme en el
intervalo (0, 1).

Figura 4.1: Números aleatorios

La generación de números aleatorios en C++ se realiza por medio de la función rand(), mis-
ma que se encuentra definida en la librería cstdlib. La función rand() devuelve un número
entero aleatorio en el rango de 0 a RAND_MAX, donde RAND_MAX es el límite superior y es-
te último depende de la capacidad computacional del ordenador, así como del sistema operativo.

A continuación, se muestra un programa que genera y almacena 20 números enteros aleatorios


entre 0 y RAND_MAX:

36
CAPÍTULO 4. NÚMEROS ALEATORIOS 37

1 # include < iostream >


2 # include < cstdlib >
3 using namespace std ;
4
5 int main (){
6 int vec [20];
7
8 // Generamos valores entre 0 y RAND_MAX
9 for ( int i =0; i <20; i ++){
10 vec [ i ] = rand ();
11 cout << vec [ i ] << " " ;
12 }
13
14 // Limite superior permitido por el equipo
15 cout << " \ n El RAND_MAX para este equipo es : " << RAND_MAX ;
16
17 return 0;
18 }

Si ejecutamos varias veces el programa anterior se puede observar que los números obtenidos
en cada ejecución coinciden. Esto se debe principalmente a que la semilla es exactamente la
misma en cada ejecución. Para solventar este problema se recomienda resetear la semilla en
cada llamado del código por medio del siguiente comando:

srand(time(NULL));

donde srand accede al valor de la semilla y time reemplaza el valor de la semilla por la fecha y
hora de ejecución.

Siempre que se desee replicar un experimento o análisis lo recomendado es fijar una semilla.

A continuación, un programa que genera 5 números aleatorios enteros distintos en cada ejecución:
1 # include < iostream >
2 # include < cstdlib >
3 using namespace std ;
4
5 int main (){
6 int vec [5];
7
8 // Semilla aleatoria
9 srand ( time ( NULL ));
10
11 // Generamos valores entre 0 y RAND_MAX
12 for ( int i =0; i <5; i ++){
13 vec [ i ] = rand ();
14 cout << vec [ i ] << " " ;
15 };
16
17 return 0;
18 }
CAPÍTULO 4. NÚMEROS ALEATORIOS 38

En ocasiones, se hace necesario generar números aleatorios enteros comprendidos en el rango


[0, M ], con M ∈ N. Para ello, definiremos un nuevo límite superior empleado la notación del
módulo ( %):

rand() % (Limite superior + 1)

A continuación, un programa que genera 100 números aleatorios entre [0, 20] y obtiene la suma
de dichos números:
1 # include < iostream >
2 # include < cstdlib >
3 using namespace std ;
4
5 int main (){
6 int vec [100];
7
8 // Semilla aleatoria
9 srand ( time ( NULL ));
10
11 // Valores aleatorios
12 cout << " Los valores generados son : " << endl ;
13 for ( int i =0; i <100; i ++){
14 vec [ i ] = rand () % 21;
15 };
16
17 // Calculo suma
18 int suma = 0;
19 for ( int i =0; i <100; i ++){
20 cout << vec [ i ] << endl ;
21 suma += vec [ i ];
22 };
23
24 cout << " La suma de los valores generados : " << endl ;
25 cout << suma << endl ;
26
27 return 0;
28 }

4.2. Generación de números aleatorios decimales


Ahora nos centraremos en la generación de números aleatorios decimales, para ello basta recordar
xn
que el número pseudoaleatorio yn = se distribuye uniformemente en el intervalo (0, 1). Es
m
decir,
yn ∼ U [0, 1]
por tanto,
b · yn ∼ U [0, b], para b ∈ R+
y además,
a + b · yn ∼ U [a, a + b], para a ∈ R y b ∈ R+
A continuación, se muestra un programa que genera 10 números aleatorios con 4 decimales en
el intervalo [1, 4].
CAPÍTULO 4. NÚMEROS ALEATORIOS 39

1 # include < iostream >


2 # include < cstdlib >
3 using namespace std ;
4
5 int main (){
6 double vec [10];
7
8 // Semilla aleatoria
9 srand ( time ( NULL ));
10
11 // Generamos valores entre 0 y RAND_MAX
12 for ( int i =0; i <10; i ++){
13 vec [ i ] = 1 + ( double ) ( rand () % 30001)/10000;
14 cout << vec [ i ] << " " ;
15 };
16
17 return 0;
18 }

4.3. Evaluación de integrales


Una de las primeras aplicaciones de los números aleatorios fue el cálculo de integrales. Sea g(x)
una función y supongamos que queremos calcular θ, donde:
∫ 1
θ= g(x)dx
0

Para calcular el valor de θ note que si U está distribuida uniformemente en (0, 1), entonces
podemos expresar θ como:
θ = E[g(U )]
Si U1 , U2 , . . . , Uk son variables aleatorias independientes y uniformes en (0, 1), esto implica que
g(U1 ), g(U2 ), . . . , g(Uk ) son variables aleatorias independientes e idénticamente distribuidas con
media θ.

Por lo tanto, por la ley fuerte de los grandes números, se tiene, con probabilidad igual a 1 que:


k
g(Ui )
→ E[g(U )] = θ, cuando k → ∞
i=1
k

De lo anterior, podemos aproximar θ generando un gran número de números aleatorios ui y con-


siderando como nuestra aproximación a θ el valor medio de g(ui ). Este método de aproximación
de integrales es conocido como el método de Monte Carlo.

A continuación, se muestra un programa que aproxima la integral:


∫ 1 x
e −1
θ= dx
0 e−1
mediante el método de Monte Carlo.
CAPÍTULO 4. NÚMEROS ALEATORIOS 40

1 # include < iostream >


2 # include < cmath >
3 # include < cstdlib >
4 using namespace std ;
5
6 int main (){
7 int k ;
8 double u , sum =0;
9
10 cout << " Ingrese el valor k " << endl ;
11 cin >> k ;
12
13 // Semilla aleatoria
14 srand ( time ( NULL ));
15
16 // Calculo de la integral
17 for ( int i =0; i < k ; i ++){
18 u = ( double ) (1 + rand () % 10000)/10000;
19 sum += ( double ) ( exp ( u ) -1)/( exp (1) -1);
20 };
21
22 // Impresion resultado
23 cout << " Valor integral : " << ( double ) sum / k << endl ;
24
25 return 0;
26 }

Si quisiéramos calcular la integral:


∫ b
θ= g(x)dx
a
entonces, al hacer la sustitución:
x−a dx
y= y dy =
b−a b−a
vemos que:
∫ 1
θ= g (a + [b − a]y) (b − a)dy
0
∫ 1
= h(y)dy
0

donde h(y) = (b − a)g (a + [b − a]y). Así, podemos aproximar θ al generar de manera continua
números aleatorios y luego considerar el valor promedio de h evaluada en estos números aleato-
rios.

A continuación, un programa que calcula la integral definida:


∫ 5
x
θ= dx
2 x2 −1
mediante el método de Monte Carlo.
CAPÍTULO 4. NÚMEROS ALEATORIOS 41

1 # include < iostream >


2 # include < cmath >
3 # include < cstdlib >
4 using namespace std ;
5
6 int main (){
7 int k , a =2 , b =5;
8 double u , z , sum =0;
9
10 cout << " Ingrese el valor k " << endl ;
11 cin >> k ;
12
13 // Semilla aleatoria
14 srand ( time ( NULL ));
15
16 // Calculo integral
17 for ( int i =0; i < k ; i ++){
18 u = ( double ) (1 + rand () % 10000)/10000;
19 z = a + (b - a )* u ;
20 sum += (b - a )*(( double ) z /( pow (z ,2) -1));
21 }
22
23 // Impresion resultado
24 cout << " Valor integral : " << ( double ) sum / k << endl ;
25
26 return 0;
27 }

De manera análoga, si queremos el valor de


∫ ∞
θ= g(x)dx
0

podríamos aplicar la sustitución


1 −dx
y= , dy = = −y 2 dx
x+1 (x + 1)2

para obtener la identidad ∫ 1


θ= h(y)dy
0
donde ( )
g 1
y −1
h(y) =
y2
5 | Funciones

Una función es un fragmento de código que permite ejecutar una serie de instrucciones para-
metrizadas. Las funciones son de gran utilidad porque permiten estructurar el código de un
programa. Una función realiza una tarea concreta y puede ser diseñada, implementada y depu-
rada de manera independiente al resto del código.

5.1. Definición de función


La sintaxis básica para la definición de una función en C++ es la siguiente:

tipo nombre(tipo1 var1, tipo2 var2, ... , tipon, varn){


.....
bloque de instrucciones
.....
return expresion;
}

A la primera línea se le llama signatura de la función e incluye el tipo del valor devuelto por la
función o valor de retorno, el nombre de la función y la lista de parámetros formales de la fun-
ción que consiste en un listado de variables separadas por comas y encerradas entre paréntesis.
Cada variable de la lista de parámetros formales viene precedida por su tipo, a continuación, y
delimitado por llaves, viene el bloque de instrucciones asociado a la función, también llamado
cuerpo de la función.

El efecto de la sentencia return es evaluar su expresión asociada y terminar la ejecución de la


función devolviendo el resultado de evaluar la expresión. Se permite una expresión de un tipo
que admita una conversión implícita al tipo de retorno de la función.

Las funciones devuelven únicamente tipos básicos: int, double, float, string, etc.

A continuación, un programa que recibe el valor de los catetos de un triángulo rectángulo y


retorna el valor de la hipotenusa:
1 # include < iostream >
2 # include < cmath >
3 using namespace std ;
4
5 double hipotenusa ( double cateto1 , double cateto2 ){
6 return sqrt ( pow ( cateto1 , 2) + pow ( cateto2 , 2));
7 };
8
9 int main (){

42
CAPÍTULO 5. FUNCIONES 43

10 double c1 , c2 ;
11 cout << " Ingrese los valores de los catetos : " << endl ;
12 cin >> c1 ;
13 cin >> c2 ;
14 cout << " La hipotenusa es : " << hipotenusa ( c1 , c2 ) << endl ;
15
16 return 0;
17 }

Nótese que la función hipotenusa devuelve únicamente un valor real.

A continuación, un programa que recibe una calificación numérica y devuelve una etiqueta:

Calificación Etiqueta
Menor a 5 Reprobado
Entre 5 y 9 Aprobado
Mayor a 9 Sobresaliente

1 # include < iostream >


2 using namespace std ;
3
4 string escala ( double n ){
5 string etiqueta ;
6 if ( n < 5){
7 etiqueta = " Reprobado " ;
8 } else if ( n <= 9){
9 etiqueta = " Aprobado " ;
10 } else {
11 etiqueta = " Sobresaliente " ;
12 }
13 return etiqueta ;
14 };
15
16 int main (){
17 double nota ;
18 cout << " Ingrese la nota obtenida en el examen : " << endl ;
19 cin >> nota ;
20 cout << " La escala alcanzada es : " << escala ( nota ) << endl ;
21
22 return 0;
23 }

Nótese ahora que la función escala devuelve únicamente un texto.

Una función puede contener más de una instrucción return, en ese caso la ejecución de la función
terminará cuando se ejecute por primera vez una instrucción return.

A continuación, se presenta un programa que obtiene el máximo entre dos números que ingresa
el usuario por pantalla:
CAPÍTULO 5. FUNCIONES 44

1 # include < iostream >


2 using namespace std ;
3
4 double maximo ( double x , double y ){
5 if (x > y )
6 return x ;
7 return y ;
8 }
9
10 int main (){
11 double a , b ;
12 cout << " Ingrese dos números " << endl ;
13 cin >> a >> b ;
14 // Se imprime el máximo de los dos numeros
15 cout << " El máximo es : " << maximo (a , b ) << endl ;
16
17 return 0;
18 }

5.2. Parámetros por defecto


El uso de parámetros por defecto es otra característica propia de C++ que no está presente en
algunos lenguajes de programación. Un parámetro por defecto es un parámetro que no tiene por
qué ser especificado al invocar a una función. Si en la invocación de la función no se especifica
un parámetro por defecto entonces se le asigna un valor por defecto al parámetro.

A continuación, un programa que calcula el logaritmo en diferentes bases (por defecto se mantiene
la base 2):

1 # include < iostream >


2 # include < cmath >
3 using namespace std ;
4
5 double logn ( double valor , double base =2){
6 return log ( valor )/ log ( base );
7 }
8
9 int main (){
10 double x , n ;
11 cout << " Ingrese un valor y la base : " << endl ;
12 cin >> x >> n ;
13
14 cout << " El logaritmo de " << x << " en base " << n << " es : "
15 << logn (x , n ) << endl ;
16 cout << " El logaritmo de " << x << " en base 2 es : "
17 << logn ( x ) << endl ;
18
19 return 0;
20 }
CAPÍTULO 5. FUNCIONES 45

5.3. Tipos de datos que devuelve una función


En C++, una función puede devolver valores de aquellos tipos de datos para los que la asigna-
ción está definida, la asignación a un tipo de dato está definida si es posible asignar valores a
las variables de dicho tipo con el operador de asignación (=).

De los tipos de datos que hemos visto los únicos para los que la asignación no está definida son
los tipos vector y array. Para el resto de tipos: tipos básicos, estructuras y cadenas de caracteres
la asignación está definida y, por tanto, una función puede devolver un dato de tipo básico, de
tipo string o de tipo estructura, pero no puede devolver un vector.

A continuación, un programa que devuelve una estructura:


1 # include < iostream >
2 using namespace std ;
3
4 // Definimos una nueva estructura que almacena dos valores
5 struct ValMM {
6 double max , min ;
7 };
8
9 // Definimos una función que devuelve la estructura anterior
10 ValMM MaxMin ( double x , double y ){
11 ValMM num ;
12 if ( x > y ){
13 num . max = x ;
14 num . min = y ;
15 } else {
16 num . max = y ;
17 num . min = x ;
18 }
19 return num ;
20 };
21
22 int main (){
23 double a , b ;
24 ValMM numeros ;
25 cout << " Ingrese dos valores : " << endl ;
26 cin >> a >> b ;
27
28 // Llamado a la funcion
29 numeros = MaxMin (a , b );
30 cout << " El valor máximo es : " << numeros . max << endl ;
31 cout << " El valor mínimo es : " << numeros . min << endl ;
32
33 return 0;
34 }
CAPÍTULO 5. FUNCIONES 46

5.4. Funciones void


Se suele utilizar el término función void para designar a una función que no tiene valor de re-
torno. Puesto que la sintaxis de C++ obliga a indicar un tipo de retorno, las funciones void
utilizan el tipo especial void como tipo de retorno.

A continuación, el programa de Hola mundo mejorado:


1 # include < iostream >
2 using namespace std ;
3
4 void saludo (){
5 cout << " Hola Mundo \ n " ;
6 }
7
8 int main (){
9 saludo ();
10 return 0;
11 }

Aunque una función no tenga valor de retorno, es decir, aunque sea una función void es posible
utilizar la sentencia return en su cuerpo para terminar la ejecución de la función. En una función
void la sentencia return debe utilizarse sin expresión asociada, es decir:

return ;

Si una función void no utiliza ninguna sentencia return, la ejecución de la función termina cuando
se ejecuta la última instrucción de la función

Las funciones void deben invocarse mediante una instrucción que incluya únicamente la llamada
a la función.

A continuación, un programa que calcula una el valor de pi con un error de 0.00001 entre sus
aproximaciones:

π ∑ (−1)n

4 i=0
2n + 1

1 # include < iostream >


2 # include < cmath >
3 using namespace std ;
4
5 double termino ( int n ){
6 return ( float ) pow ( -1 , n )/(2* n +1);
7 }
8
9 void pi (){
10 float error = 0.00001 , suma = 0 , xn , xm ;
11 int i =0;
12 do {
13 xn = termino ( i );
14 xm = termino ( i +1);
CAPÍTULO 5. FUNCIONES 47

15 suma += xn ;
16 i ++;
17 } while ( abs ( xn - xm ) >= error );
18
19 cout << " Valor aproximado de pi es : " << 4* suma << endl ;
20 cout << " Numero de iteraciones : " << i << endl ;
21
22 return ;
23 }
24
25 int main (){
26 cout << " Aproximacion de pi \ n " << endl ;
27
28 // Llamado a la funcion void
29 pi ();
30
31 return 0;
32 }

5.5. Almacenamiento de variables


A las variables definidas en una función se les llama variables locales, en contraste con las varia-
bles definidas fuera de las funciones que son llamadas variables globales.

El ámbito de una variable global abarca desde el lugar de su definición hasta el final del fichero
en que se ha definido, pudiendo ser utilizada en cualquier función situada en dicho ámbito.

Una variable local con el mismo nombre que una variable global oculta a la variable global.

A continuación, un programa en el que una variable local oculta a la variable global:


1 # include < iostream >
2 using namespace std ;
3
4 int fun ( int anio ){
5 // Variable local anio
6 anio = 2019;
7
8 return anio ;
9 }
10
11 int main (){
12 // Variable global anio
13 int anio = 2000;
14 cout << " Nos encontramos en el anio " << fun ( anio ) << endl ;
15 cout << " Naci en el anio " << anio << endl ;
16
17 return 0;
18 }
CAPÍTULO 5. FUNCIONES 48

El ámbito de una variable local abarca desde su lugar de definición en la función hasta el final
del bloque más interno en que ha sido definida. Los parámetros formales de una función son
variables locales de la función y, a efecto de ámbito, se encuentran definidas en el bloque más
externo de la función, es decir, entre las llaves que delimitan el cuerpo de la función.

El uso de variables globales para implementar la comunicación entre funciones presenta algunos
inconvenientes:

1. Una función que se comunica con el resto mediante variables globales no es reutilizable,
salvo en un entorno en el que se utilicen las mismas variables globales. Se dice que la
función está fuertemente acoplada al resto del código.

2. El uso de variables globales puede producir los llamados efectos colaterales. Esto ocurre
cuando una variable global es ocultada por una variable local inadvertidamente.

Las variables globales se almacenan en una zona de memoria denominada memoria global. Una
variable global permanece almacenada en la memoria global durante todo el tiempo que dure la
ejecución de un programa lo cual no es óptimo ya que delimita los recursos disponibles.

5.5.1. La Pila
La pila es una zona de memoria contigua donde se almacenan las variables locales de las funciones
y alguna otra información, como la dirección de memoria de retorno de una función. El tamaño
de la pila varía dinámicamente durante la ejecución de un programa; cuando se invoca a una
función la pila crece hacia direcciones superiores de memoria para albergar a sus variables locales,
se dice que las variables se apilan; cuando una función termina la pila decrece y sus variables
locales dejan de ser accesibles, se dice que las variables se desapilan.

Figura 5.1: Esquema de una pila

Una variable almacenada en la pila tiene un tiempo de existencia limitado al tiempo en que se
ejecuta el código asociado al ámbito de la variable.

El abuso en la utilización de variables puede acarrear problemas en el rendimiento del computador


(tiempo de ejecución) por el excesivo espacio de memoria empleado.

5.5.2. Estado de la pila


Para analizar el estado de una pila durante algunos instantes de una ejecución se analizará el
siguiente programa:
CAPÍTULO 5. FUNCIONES 49

1 # include < iostream >


2 using namespace std ;
3
4 double maximo ( double x , double y ){
5 double m ;
6 if (x > y ){
7 m = x;
8 } else {
9 m = y;
10 }
11 return m ;
12 }
13
14 int main (){
15 double a , b ;
16 cout << " Ingrese dos números " << endl ;
17 cin >> a >> b ;
18
19 // Se imprime el máximo de los dos numeros
20 cout << " El máximo es : " << maximo (a , b ) << endl ;
21
22 // Se vuelve a invocar a maximo
23 cout << " El máximo de a y 10 es : " << maximo (a , 10) << endl ;
24 a = 8;
25 double z = 3 + maximo (a , a -1);
26 cout << " z vale : " << z << endl ;
27
28 return 0;
29 }

Ejecutamos el programa anterior considerando a = 6 y b = 8.

Figura 5.2: Estado de la pila durante la ejecución del programa


CAPÍTULO 5. FUNCIONES 50

5.6. Funciones recursivas


Se dice que una función es recursiva cuando dentro de su bloque de sentencias hay una llamada
a la propia función.

En principio cualquier función puede hacerse una llamada a sí misma, sin embargo, tiene que
estar especialmente diseñada para que funcione correctamente, ya que al llamarse a sí misma
provoca que ésta se vuelva a ejecutar, y dentro de sus instrucciones se vuelve a llamar, creando
un bucle que si no está controlado se vuelve infinito.

Por lo tanto, una función recursiva forma un bucle al ser llamada a si misma. Este bucle debe
ser controlado de alguna manera para poder salir de él y no caer en estructuras indeseadas como
bucles infinitos o estructuras que se autorreplican indefinidamente.

A continuación, un programa que calcula el factorial de un número entero positivo:


1 # include < iostream >
2 using namespace std ;
3
4 long int factorial ( int n ){
5 if ( n == 1){
6 return 1;
7 } else {
8 return n * factorial (n -1);
9 }
10 };
11
12 int main (){
13 long int n ;
14 cout << " Ingrese un numero entero positivo : " << endl ;
15 cin >> n ;
16 cout << n << " ! = " << factorial ( n ) << endl ;
17
18 return 0;
19 }

La recursividad facilita la escritura de código que trabaja con estructuras de datos o problemas
que tienen una naturaleza recursiva como la estructura de datos árbol. Además, produce un
código más legible para este tipo de problemas.

La serie de Fibonacci es una sucesión infinita de números naturales:

0, 1, 1, 2, 3, 5, 8, 13, 21, . . .

Sus dos primeros términos son 0 y 1, los restantes términos se obtienen a partir de la suma de
los dos anteriores.

Escriba un programa que permita obtener el n-ésimo elemento de la serie de Fibonacci.


1 # include < iostream >
2 using namespace std ;
3
4 int fibonacci ( int n ){
5 if ( n == 0){
CAPÍTULO 5. FUNCIONES 51

6 return 0;
7 } else if ( n == 1){
8 return 1;
9 } else {
10 return fibonacci (n -1) + fibonacci (n -2);
11 }
12 };
13
14 int main (){
15 long int n ;
16 cout << " Serie de Fibonacci " << endl ;
17 cout << " Ingrese la posicion del elemento a mostrar " << endl ;
18 cin >> n ;
19 cout << " El n - esimo elemento es : " << fibonacci ( n ) << endl ;
20
21 return 0;
22 }

5.7. Paso por variable o referencia


Una función en C++ sólo tiene un valor de retorno. Sin embargo, a veces se necesita que una
función devuelva más de un valor; por ejemplo, podríamos necesitar una función que devuelva
tanto el máximo como el mínimo de los valores recibidos como parámetros. Se puede conseguir
devolver más de un valor utilizando como tipo de retorno una estructura que contenga tantos
campos como valores a devolver. Ver ejemplo de la sección 5.3. Esta solución no es muy elegante
pues implica la definición de una estructura con el único objeto de almacenar los valores de
retorno.

En C++ se pueden utilizar los parámetros formales de una función para que la misma devuelva
datos. En esta sección nos centraremos en estudiar como utilizar variables de tipo referencia.

5.7.1. Referencias
Una referencia es un nombre alternativo para un dato u objeto. La sintaxis T& significa refe-
rencia a un objeto del tipo T.

Una variable de tipo referencia debe iniciarse en su definición con un objeto del mismo tipo que
el tipo asociado a la referencia. Al objeto con que se inicia la referencia lo llamaremos el objeto
referenciado por la referencia.

Una referencia es por así decirlo un alias o etiqueta de una variable.

A continuación, un programa que define una referencia y muestra la dirección de memoria en la


que se almacena una variable:
1 # include < iostream >
2 using namespace std ;
3
4 int main (){
5 int x =4;
CAPÍTULO 5. FUNCIONES 52

6 // Definición e inicialización de la referencia


7 int & ref_x = x ;
8
9 cout << " Valor del objeto referenciado : " << ref_x << endl ;
10 cout << " Direccion del objeto referenciado : " << & x << endl ;
11
12 return 0;
13 }

Figura 5.3: Estado de la pila durante la ejecución del programa

Ahora, analicemos que sucede si se modifica el valor de x:


1 # include < iostream >
2 using namespace std ;
3
4 int main (){
5 int x =4;
6 // Definición e inicialización de la referencia
7 int & ref_x = x ;
8
9 cout << " Valor del objeto referenciado : " << ref_x << endl ;
10 cout << " Direccion del objeto referenciado : " << & x << endl ;
11
12 // Modificamos el valor de x
13 x = 9;
14
15 cout << " Valor del objeto referenciado : " << ref_x << endl ;
16 cout << " Direccion del objeto referenciado : " << & x << endl ;
17
18 return 0;
19 }

Una referencia siempre tiene asociado el mismo objeto referencia, a saber, el objeto con que se ha
iniciado la referencia. Una vez iniciada una referencia, cuando un programa utiliza la referencia
realmente se trabaja con su objeto referenciado.

A continuación, otro programa que intenta mostrar el manejo de referencias:


1 # include < iostream >
CAPÍTULO 5. FUNCIONES 53

2 using namespace std ;


3
4 int main (){
5 int x = 6;
6 int v [] = {1 ,2};
7 int & r1 = x ;
8 int & r2 = v [0];
9
10 cout << x << " \ t " << v [0] << " \ t " << v [1] << endl ;
11 r1 = 4;
12 r2 ++;
13 cout << x << " \ t " << v [0] << " \ t " << v [1] << endl ;
14 cout << r1 << " \ t " << r2 << endl ;
15 return 0;
16 }

Analicemos el estado de la pila en algunos momentos de la ejecución del programa:

Figura 5.4: Estado de la pila durante la ejecución del programa

5.7.2. Paso por referencia


Las referencias se utilizan principalmente para implementar el paso por variable o por referencia.
El paso por variable se puede utilizar para que una función devuelva información mediante un
parámetro.

A continuación, un primer programa que usa referencias:


1 # include < iostream >
2 using namespace std ;
3
4 // Paso por variable
5 void incremento1 ( int a ) {
6 a = a + 1;
7 return ;
8 }
9
10 // Paso por referencia
11 void incremento2 ( int & a ) {
12 a = a + 1;
13 return ;
14 }
CAPÍTULO 5. FUNCIONES 54

15
16 int main () {
17 int x = 3;
18 // Funcion con paso por variable
19 incremento1 ( x );
20 cout << " x tiene el valor : " << x << endl ;
21
22 int y = 3;
23 // Funcion con paso por referencia
24 incremento2 ( y );
25 cout << " y tiene el valor : " << y << endl ;
26
27 return 0;
28 }

Notemos que los cuerpos de las funciones incremento1 e incremento2 son iguales, sin embargo,
los parámetros formales de la función son diferentes, para la primera función se hace un paso
por variable, mientras que la segunda función realiza un paso por referencia.

Para el siguiente programa nos encontramos interesados en realizar la división entre dos números
enteros y obtener dos resultados: el cociente y el residuo.

1 # include < iostream >


2 using namespace std ;
3
4 int division ( int dividendo , int divisor , int & residuo ){
5 residuo = dividendo % divisor ;
6 return dividendo / divisor ;
7 }
8
9 int main () {
10 int num , den , res ;
11 cout << " Ingrese el dividendo y divisor : " << endl ;
12 cin >> num >> den ;
13 int cociente = division ( num , den , res );
14
15 cout << " El cociente es : " << cociente << endl ;
16 cout << " El residuo es : " << res << endl ;
17 cout << " Se verifica que : " << cociente << " * " << den
18 << " + " << res << " = " << num << endl ;
19
20 return 0;
21 }

En el programa anterior a partir de una referencia es posible obtener 2 o más valores resultantes
de la ejecución de una función.

A continuación, retomamos nuestro trillado ejemplo del mínimo y máximo de dos números:
1 # include < iostream >
2 using namespace std ;
3
CAPÍTULO 5. FUNCIONES 55

4 void MaxMin ( double x , double y , double & min , double & max ){
5 if ( a > b ){
6 max = x ;
7 min = y ;
8 } else {
9 max = y ;
10 min = x ;
11 }
12 return ;
13 }
14
15 int main (){
16 double a , b , maximo , minimo ;
17 cout << " Ingrese dos números : " << endl ;
18 cin >> a >> b ;
19 // Invocamos a la función
20 MaxMin (a , b , minimo , maximo );
21 // Imprimos los resultados
22 cout << " El maximo es : " << maximo << endl ;
23 cout << " El minimo es : " << minimo << endl ;
24 return 0;
25 }

Ejecutemos el anterior programa con: a = 3 y b = 7

Figura 5.5: Estado de la pila durante la ejecución del programa

El programa incluye una función, llamada MaxMin, cuyos dos últimos parámetros formales,
max y min, tienen semántica de paso por referencia. Cuando se invoca a la función MaxMin,
max y min se inician con los dos objetos pasados como parámetros reales. Cuando la función
CAPÍTULO 5. FUNCIONES 56

MaxMin realiza alguna modificación o consulta sobre las variables max o min realmente está
modificando o consultando los objetos referenciados por estas dos variables. Esto permite que
tras la ejecución de la función MaxMin, la función main pueda consultar los valores de máximo
y mínimo calculados por la función MaxMin.

Cuando la función MaxMin asigna valores a las referencias max y min los valores asignados se
almacenan en las variables maximo y minimo respectivamente. Esto permite que tras ejecutar
la función MaxMin las variables maximo y minimo contengan los dos valores calculados por
MaxMin.

Cuando un parámetro formal es de tipo referencia, el parámetro real utilizado en la invocación


debe ser una variable, si se usa el calificativo const como calificador del parámetro formal no es
necesario utilizar una variable, de ahí el nombre de paso por variable.

5.8. Paso de parámetros tipo estructura


El paso de parámetros de tipo estructura se realiza de la misma manera que el de un tipo básico.
El paso de una estructura como parámetro por valor de una función puede ser algo ineficiente
pues implica la iniciación de la estructura de todos sus campos. Cuantos más campos tenga la
estructura más costoso resultará su paso por valor.

A continuación, un ejemplo de la suma de números complejos:


1 # include < iostream >
2 using namespace std ;
3
4 struct complejo {
5 double real ;
6 double imaginario ;
7 }
8
9 complejo suma ( complejo x , complejo y ){
10 complejo res ;
11 res . real = x . real + y . real ;
12 res . imaginario = x . imaginario + y . imaginario ;
13 return res ;
14 }
15
16 void impcomplejo ( complejo w ){
17 cout << w . real << " + " << w . imaginario << " . i " << endl ;
18 }
19
20 int main (){
21 complejo x ,y , z ;
22 cout << " Ingrese el primer complejo : " << endl ;
23 cin >> x . real ;
24 cin >> x . imaginario ;
25 cout << " Ingrese el segundo complejo : " << endl ;
26 cin >> y . real ;
27 cin >> y . imaginario ;
28 z = suma (x , y );
29 cout << " El resultado es : " ;
CAPÍTULO 5. FUNCIONES 57

30 impcomplejo ( z );
31
32 return 0;
33 }

5.9. Sobrecarga de funciones


La sobrecarga de funciones es una característica de C++ que no está presente en la gran mayo-
ría de lenguajes de programación. Consiste en poder definir más de una función con el mismo
nombre, siempre que los parámetros de las funciones difieran en número o tipo.

A continuación, un programa que presenta una sobrecarga:


1 # include < iostream >
2 using namespace std ;
3
4 double maximo ( double x , double y ){
5 if ( x > y )
6 return x ;
7 return y ;
8 };
9
10 double maximo ( const double x [] , int dim ){
11 double max = x [0];
12 for ( int i = 1; i < dim ; i ++){
13 if ( x [ i ] > max ){
14 max = x [ i ];
15 }
16 }
17 return max ;
18 };
19
20 int main (){
21 double a , b ;
22 double vec [5];
23 cout << " Ingrese dos números : " << endl ;
24 cin >> a >> b ;
25 cout << " El mayor valor es : " << maximo (a , b ) << endl ;
26 cout << " Ingrese 5 números : " << endl ;
27 for ( int j = 1; j <= 5; j ++){
28 cin >> vec [j -1];
29 }
30 cout << " El máximo valor ingresado es : " << maximo ( vec , 5) << endl ;
31
32 return 0;
33 }
CAPÍTULO 5. FUNCIONES 58

5.10. Paso de parámetros de tipo vector


Un parámetro de tipo vector se especifica de la misma forma que se define una variable de tipo
vector, salvo que no es necesario especificar su tamaño. Como a través de una variable de tipo
vector no es posible saber cuántos elementos almacena el vector, se utiliza otro parámetros para
recibir el número de elementos que almacena el vector.

A continuación, un programa que recibe como parámetro un vector:


1 # include < iostream >
2 # include < cmath >
3 using namespace std ;
4
5 void cuadrado ( double x [] , int dim ){
6 for ( int i = 0; i < dim ; i ++){
7 x [ i ] = pow ( x [ i ] ,2);
8 }
9 };
10
11 void impvector ( double x [] , int dim ){
12 for ( int i = 0; i < dim ; i ++){
13 cout << x [ i ] << " \ t " ;
14 }
15 };
16
17 int main (){
18 double vec [5];
19 cout << " Ingrese 5 números : " << endl ;
20 for ( int j = 1; j <= 5; j ++){
21 cin >> vec [j -1];
22 }
23 cuadrado ( vec , 5);
24 cout << " Los números al cuadrado son : " << endl ;
25 impvector ( vec ,5);
26 return 0;
27 }

Una variable de tipo vector almacena la dirección de memoria a partir de la cual se almacenan
los elementos del vector. Por lo tanto, lo que recibe un parámetro formal de tipo vector es la
dirección de memoria donde se almacena un vector. Al recibir la dirección de memoria donde
se almacena el vector, la función tiene total acceso a los elementos originales del vector y, por
tanto, las modificaciones que realice en los elementos del vector serán visibles al terminar la
ejecución de la función.

A continuación, se muestra un programa que genera estadísticas a partir de la información de


10 estudiantes:
1 # include < iostream >
2 using namespace std ;
3
4 void leer ( double a []){
5 int i ;
6 cout << " Ingrese las estaturas de los alumnos : " << endl ;
CAPÍTULO 5. FUNCIONES 59

7 for ( i =0; i <10; i ++){


8 cout << " Alumno " << i +1 << " : " ;
9 cin >> a [ i ];
10 }
11 }
12
13 double estatura_media ( double a []){
14 double media = 0;
15 int i ;
16 for ( i =0; i <10; i ++){
17 media = media + a [ i ];
18 }
19 media = media / 10;
20 return media ;
21 }
22
23 void max_min ( double a [] , double & max , double & min ){
24 int i ;
25 max = min = a [0];
26 for ( i =1; i <10; i ++){
27 if ( a [ i ] > max ){
28 max = a [ i ];
29 } else {
30 if ( a [ i ] < min )
31 min = a [ i ];
32 }
33
34 }
35 }
36
37 void c u a n t o s _ m a y o r _ m e n o r _ m e d i a ( double a [] , double media ,
38 int & mayores_media , int & menores_media ){
39 int i ;
40 for ( i =0; i <10; i ++){
41 if ( a [ i ] > media )
42 mayores_media ++;
43 else if ( a [ i ] < media )
44 menores_media ++;
45 }
46 }
47
48 void mostr ar _r es ul ta do s ( double a [] , double media , double mayor ,
49 double menor , int mayores_media , int menores_media ){
50 int i ;
51 cout << " Estatura de los alumnos de la clase " << endl ;
52 cout << " - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - " << endl ;
53 for ( i =0; i <10; i ++){
54 cout << " Alumno " << i +1 << " : " << a [ i ] << endl ;
55 }
56 cout << " Mayor estatura : " << mayor << endl ;
57 cout << " Menor estatura : " << menor << endl ;
CAPÍTULO 5. FUNCIONES 60

58 cout << " Estatura media : " << media << endl ;
59 cout << " Hay " << mayores_media << " alumnos con estatura " ;
60 cout << " superior a la media " << endl ;
61 cout << " Hay " << menores_media << " alumnos con estatura " ;
62 cout << " inferior a la media " << endl << endl ;
63 }
64
65 int main (){
66 double estaturas [10] , media , mayor , menor ;
67 int cont_mas = 0 , cont_menos = 0;
68 leer ( estaturas );
69 max_min ( estaturas , mayor , menor );
70 media = estatura_media ( estaturas );
71 cu a n t o s _ m a y o r _ m e n o r _ m e d i a ( estaturas , media , cont_mas , cont_menos );
72 mos tr ar_resultado s ( estaturas , media , mayor , menor , cont_mas ,
73 cont_menos );
74 return 0;
75 }

5.11. Paso de parámetros de tipo array


Para realizar el paso de un array a una función se debe tener en cuenta lo siguiente:

En la función se debe declarar el parámetro como un array del tipo adecuado. En los
arrays unidimensionales no es necesario especificar el tamaño por lo tanto aparecerán los
corchetes vacíos.

En la llamada a la función a la que se envía el array, el nombre del array aparecerá solo,
sin corchetes ni índices

Los arrays en C++ se pasan siempre a las funciones por referencia. El nombre de un array
realmente es un puntero que contiene la dirección de memoria donde comienza el array, o lo
que es lo mismo, la dirección de memoria del primer elemento. Cuando se pasa un array a una
función, la función recibe la dirección de memoria donde se encuentra el primer elemento del
array. Por lo tanto, la función trabaja sobre el array original.

No es necesario utilizar el operador & en el paso de arrays a funciones para indicar que se hace
por referencia ya que siempre pasan por referencia.

A continuación, un programa que pasa un array por una función:


1 # include < iostream >
2 using namespace std ;
3
4 const int MAXFIL = 20;
5 const int MAXCOL = 30;
6
7 void leerMatriz ( int matriz [][ MAXCOL ] , int nf , int nc ){
8 int fila , col ;
9 for ( fila =0; fila < nf ; fila ++ ){
10 cout << " \ nFila : " << fila +1 << endl ;
11 for ( col =0; col < nc ; col ++){
CAPÍTULO 5. FUNCIONES 61

12 cout << " Elemento : " << col +1 <<" : " ;


13 cin >> matriz [ fila ][ col ];
14 }
15 }
16 }
17
18 void sumarMatrices ( int matriz1 [][ MAXCOL ] , int matriz2 [][ MAXCOL ] ,
19 int matriz3 [][ MAXCOL ] , int nf , int nc ){
20 int fila , col ;
21 for ( fila =0; fila < nf ; fila ++ ){
22 for ( col =0; col < nc ; col ++)
23 matriz3 [ fila ][ col ] = matriz1 [ fila ][ col ] + matriz2 [ fila ][ col ];
24 }
25 }
26
27 void mostrarResultado ( int matriz [][ MAXCOL ] , int nf , int nc ){
28 int fila , col ;
29 for ( fila =0; fila < nf ; fila ++ ){
30 for ( col =0; col < nc ; col ++)
31 cout << matriz [ fila ][ col ] << " \ t " ;
32 cout << endl ;
33 }
34 }
35
36 int main (){
37 int nfilas , ncols ;
38 int matriz1 [ MAXFIL ][ MAXCOL ];
39 int matriz2 [ MAXFIL ][ MAXCOL ];
40 int matriz3 [ MAXFIL ][ MAXCOL ];
41
42 do {
43 cout < < " \ nIntroduce numero de filas . Maximo " << MAXFIL << " : " ;
44 cin >> nfilas ;
45 } while ( nfilas <=0 or nfilas > MAXFIL );
46
47 do {
48 cout < < " \ nIntroduce numero de columnas . Maximo " << MAXCOL < < " : " ;
49 cin >> ncols ;
50 } while ( ncols <=0 or ncols > MAXCOL );
51
52 // Lectura de datos de la primera matriz
53 cout << " \ n Introduce datos de la primera matriz " ;
54 leerMatriz ( matriz1 , nfilas , ncols );
55
56 // Lectura de datos de la segunda matriz
57 cout << " \ n Introduce datos de la segunda matriz " ;
58 leerMatriz ( matriz2 , nfilas , ncols );
59
60 // Calcular la suma de las dos matrices
61 sumarMatrices ( matriz1 , matriz2 , matriz3 , nfilas , ncols );
62
CAPÍTULO 5. FUNCIONES 62

63 // Mostrar el resultado por pantalla


64 cout << " \ nmatriz 1: " << endl ;
65 mostrarResultado ( matriz1 , nfilas , ncols );
66
67 cout << " \ nmatriz 2: " << endl ;
68 mostrarResultado ( matriz2 , nfilas , ncols );
69
70 cout << " \ nmatriz 3 = matriz1 + matriz2 " << endl ;
71 mostrarResultado ( matriz3 , nfilas , ncols );
72
73 return 0;
74 }
6 | Punteros

Los punteros son uno de los tipos de datos más temidos en C++. Sin embargo, también son los
más versátiles y los que mayor potencia proporcionan.

Su uso permite el paso de argumentos por referencia, la construcción de estructuras dinámicas


de datos y la mejora en la velocidad de ejecución de algunos algoritmos. Sin embargo, el mal
uso de los punteros puede producir graves errores en la compilación, en algunos casos difíciles
de detectar.

6.1. Qué es un puntero ?


Un puntero es una variable que contiene una dirección de memoria y permite modificar el
contenido de la memoria. Su declaración se realiza de forma análoga al resto de variables, su
sintaxis es la siguiente:

tipo *nombre;

donde nombre es nombre de la variable puntero y tipo es el tipo de objeto cuya dirección se va
a almacenar. Es importante que se conozca de antemano a qué va a apuntar un puntero.

A continuación, un primer programa en el que se declaran variables de tipo puntero:


1 # include < iostream >
2 using namespace std ;
3
4 int main (){
5 int i =5;
6 int *p , * q ;
7 // Se almacena la dirección de i
8 p = &i;
9 cout << " La direccion de p es : " << p << endl ;
10 // Se pasa la dirección almacenada en p
11 q=p;
12 cout << " La direccion de q es : " << q << endl ;
13 return 0;
14 }

A continuación, se muestra un programa que emplea un puntero sobre una función:


1 # include < iostream >
2 using namespace std ;
3
4 int * Par ( int vec [] , int tam ){
5 for ( int j =0; j < tam ; j ++){

63
CAPÍTULO 6. PUNTEROS 64

6 if ( vec [ j ] %2 == 0){
7 return & vec [ j ];
8 }
9 }
10 return 0;
11 }
12
13 int main (){
14 const int dim =10;
15 int v [ dim ];
16 cout << " Ingrese 10 valores enteros " << endl ;
17 for ( int i =0; i < dim ; i ++){
18 cin >> v [ i ];
19 }
20 int * npar = Par (v , dim );
21
22 if ( npar !=0){
23 cout << " El primer elemento par del vector es : "
24 << * npar << endl ;
25 } else {
26 cout << " El vector no tiene valores pares " ;
27 }
28
29 delete [] npar ;
30
31 return 0;
32 }

Después de usar un puntero, especialmente si trabajamos con arreglos o matrices, es recomen-


dable liberar la memoria utilizada con la función delete.

6.2. Puntero nulo


Es posible asignarle el valor cero a un puntero:

int *pun = 0;

En la iniciación previa el literal 0 es un entero que se convierte implícitamente a una dirección


de memoria. Lo interesante es que la conversión implícita produce una dirección de memoria en
la que no puede estar almacenado ningún objeto. Por lo tanto, en C++ se utiliza el valor 0 para
indicar una dirección que no apunta a nada, es decir, a ningún objeto.

En el momento de las declaraciones de variables, si no se puede asignar la dirección exacta,


asignar un valor NULL para la variable puntero es una buena práctica de programación.

int *pun = NULL;

6.3. Parámetros por referencia


Al enviar un parámetro a una función todo lo que se haga con dicho parámetro dentro de la
misma no tiene efecto por fuera. Por ejemplo, si a una función se envía una variable cuyo valor
es 10 y al interior de la función le sumamos un 5, después de la ejecución de la función el valor
CAPÍTULO 6. PUNTEROS 65

de la variable seguirá siendo 10 en lugar de 15. Lo que pasó al interior de la función se quedó
allí. Para solucionar esto, si queremos que el valor cambie definitivamente, usamos punteros para
pasar no el valor del parámetro sino una referencia a éste.

A continuación, una función que emplea punteros para modificar el valor de una variable:
1 # include < iostream >
2 using namespace std ;
3
4 int fun ( int valor ){
5 valor += 5;
6 return valor ;
7 }
8
9 int funPuntero ( int * valor ){
10 * valor += 5;
11 return * valor ;
12 }
13
14 int main (){
15 int numero = 10;
16 cout << " Numero antes de llamar a fun : " << numero << endl ;
17 // Llamado a fun ( se pasa el valor )
18 fun ( numero );
19 // Resultado luego del llamado a fun
20 cout << " Numero luego de llamar a fun : " << numero << endl ;
21
22 cout << " Numero antes de llamar a funPuntero : " << numero << endl ;
23 // Llamado a funPuntero ( se pasa la dirección de memoria )
24 funPuntero (& numero );
25 // Resultado luego del llamado a funPuntero
26 cout << " Numero luego de llamar a funPuntero : " << numero << endl ;
27
28 return 0;
29 }

A continuación, nuestra función del máximo y mínimo en la versión con punteros:


1 # include < iostream >
2 using namespace std ;
3
4 void MaxMin ( double x , double y , double * max , double * min ){
5 if (x > y ){
6 * max = x ;
7 * min = y ;
8 } else {
9 * max = y ;
10 * min = x ;
11 }
12 return ;
13 }
14
15 int main (){
CAPÍTULO 6. PUNTEROS 66

16 double a , b , maximo =0 , minimo =0;


17 cout << " Ingrese dos numeros " << endl ;
18 cin >> a >> b ;
19 MaxMin (a , b , & maximo , & minimo );
20 cout << " Maximo : " << maximo << endl ;
21 cout << " Minimo : " << minimo << endl ;
22 return 0;
23 }

6.4. Aritmética de punteros y vectores


Dada una expresión p de tipo puntero a un dato de tipo T y una expresión e de tipo entero, la
expresión p+e produce un valor de tipo puntero a un dato de tipo T y de valor p+e*sizeof(T),
donde sizeof(T) es una expresión que devuelve el tamaño en bytes ocupado por un dato de
tipo T. Es decir, si p vale 1000 y apunta a un entero, entonces p + 2 vale 1008, suponiendo
que un entero ocupa cuatro bytes, o sea, que sizeof(int) vale 4. Como los elementos de un
vector ocupan posiciones contiguas de un vector, se puede utilizar un puntero para recorrer sus
elementos.

Tipo sizeof
bool 1
int 4
double 8
string 8

Si p es un puntero que apunta al primer elemento de un vector v, es decir, p = &v[0], entonces la


expresión p + i, donde i es una expresión entera, equivale a &v[i]. Por lo tanto, *(p+i) equivale
a v[i].

A continuación, un programa que muestra la aritmética de punteros:


1 # include < iostream >
2 using namespace std ;
3
4 int main () {
5 int v [5] = {2 ,4 ,6 ,8 ,10};
6 int * p = v ;
7 cout << " La dirección de p es : " << p << endl ;
8 cout << " El valor de p es : " << * p << endl ;
9 bool e =1;
10 cout << " La dirección de p + e es : " << p + e << endl ;
11 cout << " El valor de p + e es : " << *( p + e ) << endl ;
12 return 0;
13 }

En la expresión *(p+i) los paréntesis son necesarios, puesto que el operador desreferencia, *,
tiene mayor precedencia que el operador suma, +, lo que implica que la expresión *p+i equivale
a la expresión (*p)+i, es decir, significa sumarle i al elemento al que apunta p, como en el ejemplo
p vale &v[0], p + i equivale a v[0]+i.

A continuación, un programa que recorre un vector usando punteros:


CAPÍTULO 6. PUNTEROS 67

1 # include < iostream >


2 using namespace std ;
3
4 int main (){
5 int v [8] = {1 ,3 ,5 ,7 ,2 ,4 ,6 ,8};
6 int * p = & v [0];
7 for ( int i = 0; i < 8; i ++)
8 cout << *( p + i ) << endl ;
9 return 0;
10 }

También es posible restar dos expresiones de tipo puntero, pero la resta sólo tiene sentido si
las dos expresiones apuntan a elementos del mismo vector. En dicho caso, la resta devuelve el
número de posiciones que existen entre los dos elementos del vector a los que apuntanlos punteros.

A continuación, se muestra una modificación (obtención de la posición) del programa que mues-
tra el primer elemento par de un vector:
1 # include < iostream >
2 using namespace std ;
3
4 int * Par ( int vec [] , int tam ){
5 for ( int j =0; j < tam ; j ++){
6 if ( vec [ j ] %2 == 0){
7 return & vec [ j ];
8 }
9 }
10 return 0;
11 }
12
13 int main (){
14 const int dim =10;
15 int v [ dim ];
16 cout << " Ingrese 10 valores enteros " << endl ;
17 for ( int i =0; i < dim ; i ++){
18 cin >> v [ i ];
19 }
20 int * npar = Par (v , dim );
21
22 if ( npar !=0){
23 cout << " El primer elemento par del vector es : " << * npar << endl ;
24 cout << " Ocupa la posición : " << npar - & v [0] << endl ;
25 } else {
26 cout << " El vector no tiene valores pares " ;
27 }
28
29 delete [] npar ;
30
31 return 0;
32 }

En el siguiente programa se va a implementar el algoritmo de selección, que sirve para ordenar


CAPÍTULO 6. PUNTEROS 68

de forma creciente una secuencia de elementos almacenados en un vector. Dado un vector de n


elementos, el algoritmo selecciona el mínimo de los elementos del vector y lo intercambia por el
elemento que ocupa la primera posición del vector, a continuación, selecciona el mínimo entre
las posiciones 2 y n y lo intercambia por el elemento de la posición 2.

En general, en la iteración i-ésima se selecciona el mínimo entre las posiciones i y n, luego se


intercambia por el valor en la posición i.

1 # include < iostream >


2 # include < cstdlib >
3 using namespace std ;
4
5 int * minvec ( int v [] , int * pos , int * dim ){
6 int * min ;
7 min = pos ;
8 for ( int i =0; i < dim - pos ; i ++){
9 if (*( pos + i ) < * min ){
10 min = pos + i ;
11 }
12 }
13 return min ;
14 }
15
16 void intercambiar ( int v [] , int *x , int * y ){
17 int aux ;
18 aux = * x ;
19 *x = *y;
20 * y = aux ;
21 }
22
23 void Ordenar ( int v [] , int dim ){
24 int * minimo ;
25 for ( int k =0; k < dim ; k ++){
26 minimo = minvec (v , & v [ k ] , & v [ dim ]);
27 intercambiar (v , & v [ k ] , minimo );
28 }
29 }
30
31 int main (){
32 int n =10;
33 int vec [ n ];
34 srand ( time ( NULL ));
35 cout < < " Vector generado : " << endl ;
36 for ( int j =0; j < n ; j ++){
37 vec [ j ]= rand () %31;
38 cout << vec [ j ] << " \ t " ;
39 }
40
41 Ordenar ( vec , n );
42 cout << " Vector ordenado " << endl ;
43 for ( int j =0; j < n ; j ++){
CAPÍTULO 6. PUNTEROS 69

44 cout << vec [ j ] << " \ t " ;


45 }
46 return 0;
47 }
7 | Librería STL

La STL (Standard Template Library) es una librería estándar de estructuras de datos y algo-
ritmos que forma parte del C++. Esto garantiza que cualquier compilador incluye esta librería,
por lo que cualquier usuario puede usarla para sus programas.

El principal objetivo de la STL es evitar que los usuarios tengan que programar, una y otra
vez, aquellos algoritmos o estructuras de datos fundamentales, como por ejemplo: algoritmos de
ordenacion, o estructuras de datos.

7.1. Vectores
Un vector es la estructura de datos más básica: es un arreglo que almacena n elementos del mismo
tipo en n posiciones consecutivas de memoria. Si el vector se llama VEC, se puede acceder a los
elementos mediante las expresiones: VEC[0], VEC[1], . . . , VEC[n-1]. Para usar vectores de la
STL es necesario añadir la siguiente línea en el preámbulo del programa.
#include <vector>
La sintaxis para crear vectores es la siguiente:
vector <tipo> nombre(longitud, valor por defecto)
A continuación, un primer programa que muestra como crear vectores usando la STL:
1 # include < iostream >
2 # include < vector >
3 using namespace std ;
4
5 int main (){
6 // Vector de 5 elementos enteros
7 vector <int > VEC (5);
8 VEC [0]=1;
9 VEC [4]=3;
10 for ( int k =0; k <5; k ++){
11 cout << VEC [ k ] << " \ t " ;
12 }
13
14 // Vector de 3 elementos string , inicializado con " EPN "
15 vector < string > v (3 , " EPN " );
16 for ( int j =0; j <3; j ++){
17 cout << v [ j ] << " \ t " ;
18 }
19
20 return 0;
21 }

70
CAPÍTULO 7. LIBRERÍA STL 71

A diferencia de los vectores tradicionales del C++, los vectores de la STL pueden cambiar de
tamaño durante la ejecución del programa, además, tienen la muy útil propiedad de actuar co-
mo variables normales y corrientes: podemos asignarlos, compararlos, copiarlos, pasarlos como
parámetro, hacer que una función los devuelva, etc.

A continuación, un programa en el cual una función devuelve un vector:


1 # include < iostream >
2 # include < vector >
3 using namespace std ;
4
5 bool EsPrimo ( int n ){
6 bool m = true ;
7 for ( int k =2; k < n ; k ++){
8 if ( n %k ==0){
9 m = false ;
10 break ;
11 }
12 }
13 return m ;
14 }
15
16 vector <int > Primos ( int n ){
17 vector <int > res ;
18 for ( int j =2; j < n ; j ++){
19 if ( EsPrimo ( j )){
20 res . push_back ( j );
21 }
22 }
23 return res ;
24 };
25
26
27 int main (){
28 int num ;
29 cout << " Ingrese un número entero positivo mayor a 1 : " << endl ;
30 cin >> num ;
31
32 vector <int > vec ;
33 vec = Primos ( num );
34
35 for ( int i =0; i < vec . size (); i ++){
36 cout << vec [ i ] << " \ t " ;
37 }
38
39 return 0;
40 }

Las estructuras de la STL poseen métodos (funciones propias) que facilitan el trabajo, entre los
métodos más empleados podemos mencionar:

size: Retorna el número de elementos que posee el vector.


CAPÍTULO 7. LIBRERÍA STL 72

resize: Modifica el tamaño de un vector, borrando o añadiendo elementos al final, según


convenga.

empty: Contrasta si el vector está vacío.

front: Accede al primer elemento del vector.

back: Accede al primer elemento por fuera del vector (elemento posterior al último).

push_back: Añade un elemento al final del vector.

pop_back: Elimina el último elemento del vector.

erase: Elimina todos los elementos de un vector.

Los vectores y, en general, todos los tipos STL, tienen lo que se llama semántica de valor que,
además de otras cosas, quiere decir que cuando pasamos un vector como parámetro en un función,
lo que se hará es copiar todos sus datos a la función que lo recibe. Lo cual puede generar que
nuestros programas sean demasiado lentos. Por lo tanto, siempre que pasemos un vector u otro
contenedor de la STL por parámetro es conveniente pasarlo como referencia:

FUN(const vector <tipo> &VEC)

si no vamos a modificar el vector. Mientras que emplearemos la siguiente sintaxis:

FUN(vector <tipo> &VEC)

si nuestra intención es modificar el vector VEC.

Esto garantiza que, en ambos casos, la función trabaje con el vector original, y no con una copia
del mismo.

7.1.1. Iteradores
Una de las operaciones más comúnes a realizar con un vector consiste en recorrer todos los
elementos que contiene. Por lo general, esta tarea se la ha venido realizando mediante un indice.

A continuación, un programa que recorre todos los elementos de un vector:


1 # include < iostream >
2 # include < vector >
3 using namespace std ;
4
5 int main (){
6 vector <int > VEC ;
7 int num ;
8 cout << " Ingrese n valores enteros ( digite -999 para finalizar ): " << end
9 cin >> num ;
10 while ( num != -999){
11 VEC . push_back ( num );
12 cin >> num ;
13
14 }
15
16 cout << " El vector ingresado es : " << endl ;
17 for ( int i =0; i < int ( VEC . size ()); i ++){
CAPÍTULO 7. LIBRERÍA STL 73

18 cout << VEC [ i ] << " \ t " ;


19 }
20
21 return 0;
22 }

La STL ofrece otro modo de recorrer los vectores distinto al método tradicional de usar un
índice, a partir de los iteradores.

1 # include < iostream >


2 # include < vector >
3 using namespace std ;
4
5 int main (){
6 vector <int > VEC ;
7 int num ;
8 cout << " Ingrese n valores enteros ( digite -999 para finalizar ): " << end
9 cin >> num ;
10 while ( num != -999){
11 VEC . push_back ( num );
12 cin >> num ;
13
14 }
15
16 cout << " El vector ingresado es : " << endl ;
17 for ( vector <int >:: iterator it = VEC . begin (); it != VEC . end (); ++ it ){
18 cout << * it << " \ t " ;
19 }
20
21 return 0;
22 }

Esencialmente, un iterador es una variable de tipo vector<tipo>::iterator que actúa como un


apuntador a un elemento del vector. Si it es un iterador, las operaciones ++it y –it sirven para
desplazar el iterador hacia la derecha o izquierda del vector, y la operación *it devuelve el ele-
mento apuntado por el iterador. Los iteradores v.begin() y v.end() son dos iteradores especiales
que apuntan al primer elemento del vector, y al primer elemento fuera del vector.

Los iteradores no son punteros, pero actúan como si lo fuesen. En cierto modo, un iterador es
un puntero seguro, que no sólo sirve para apuntar a un valor de un tipo, sino que además sabe
desplazarse por el contenedor donde habita.

La STL permite ordenar muy fácilmente los elementos de un vector<T>: basta con hacer una
llamada a la función sort, misma que se encuentra disponible dentro de la librería algorithm.
1 # include < iostream >
2 # include < vector >
3 # include < algorithm >
4 using namespace std ;
5
6 int main (){
CAPÍTULO 7. LIBRERÍA STL 74

7 vector <int > VEC ;


8 int num ;
9 cout << " Ingrese n valores enteros ( digite -999 para finalizar ): " << end
10 cin >> num ;
11 while ( num != -999){
12 VEC . push_back ( num );
13 cin >> num ;
14
15 }
16
17 cout << " El vector ingresado es : " << endl ;
18 for ( vector <int >:: iterator it = VEC . begin (); it != VEC . end (); ++ it ){
19 cout << * it << " \ t " ;
20 }
21
22 cout << endl ;
23
24 cout << " El vector ordenado es : " << endl ;
25 sort ( VEC . begin () , VEC . end ()); // orden ascendente
26
27 for ( vector <int >:: iterator it = VEC . begin (); it != VEC . end (); ++ it ){
28 cout << * it << " \ t " ;
29 }
30
31 return 0;
32 }

7.2. Matrices
Una matriz no es más, pues, que un vector de filas, donde cada fila es un vector de elementos.
La sintaxis a utilizarse en la creación de matrices con la librería STL es la siguiente:

vector<vector<tipo> > nombre(número de filas)

A continuación, un programa que define matrices de la librería STL:


1 # include < iostream >
2 # include < vector >
3 using namespace std ;
4
5 int main (){
6 vector < vector <int > > M (5); // Matriz de 5 filas vacías
7
8 for ( int i =0; i < M . size (); i ++) {
9 M [ i ]. resize (3); // ahora obtenemos matriz (5 x3 )
10 }
11
12 cout << " Llenado de la matriz " << endl ;
13 for ( int i =0; i < M . size (); i ++) {
14 for ( int j =0; j < M [ i ]. size (); j ++) {
15 M [ i ][ j ] = i + j ;
16 }
CAPÍTULO 7. LIBRERÍA STL 75

17 }
18 return 0;
19 }

A continuación, un programa que recibe un número decimal y calcula el número binario asociado,
y viceversa:
1 # include < iostream >
2 # include < vector >
3 # include < cmath >
4 # include < algorithm >
5 using namespace std ;
6
7 int Mitad ( int * n ){
8 * n /=2;
9 }
10
11 vector <int > numero_binario ( int n ){
12 vector <int > S ;
13 int * m ;
14 m =& n ;
15 do {
16 S . push_back (* m %2);
17 Mitad ( m );
18 } while (* m >=1);
19 return S ;
20 }
21
22 int numero_decimal ( const vector <int > & M ){
23 int longitud = int ( M . size () -1);
24 int numero =0;
25 for ( int i =0; i < M . size (); i ++){
26 numero += M [ i ]* pow (2 , longitud - i );
27 }
28 return numero ;
29 }
30
31
32 int main (){
33 vector <int > R ;
34 int num ;
35 cout << " Ingrese el valor de n : " << endl ;
36 cin >> num ;
37
38 R = numero_binario ( num );
39
40 for ( int i =1; i <= R . size (); i ++){
41 cout << R [ int ( R . size ()) - i ] << " \ t " ;
42 }
43
44 int b ;
45 vector <int > BIN ;
CAPÍTULO 7. LIBRERÍA STL 76

46 cout << " Ingrese un numero binario ( para finalizar teclea 2) " << endl ;
47 cin >> b ;
48 while ( b !=2){
49 BIN . push_back ( b );
50 cin >> b ;
51 }
52
53 cout << " El numero decimal es : " << numero_decimal ( BIN ) << endl ;
54
55 return 0;
56 }
8 | Clases y Objetos

Las variables de los tipos fundamentales de datos no son suficientes para modelar adecuadamente
objetos del mundo real. Por ejemplo, no se puede trabajar directamente en C++ con los números
complejos, pero si se definen las variables parte real y parte imaginaria, se las puede juntar en
una estructura llamada Complejo.
x = a + bi
donde, a es la parte real y b la parte compleja.

La solución que hemos adoptado hasta el momento es definir un nuevo tipo, normalmente una
estructura (struct), y definir funciones y procedimientos que procesarán tales estructuras. Esta
solución presenta varios inconvenientes que ilustraremos a continuación.

Supongamos que vamos a escribir un programa en C++ en el que vamos a procesar números
complejos. C++ carece de un tipo predefinido adecuado para representar dichos números, de
manera que decidimos definir un tipo Complejo de la siguiente manera:

struct Complejo {
double real;
double imaginaria;
}

Además de definir la estructura Complejo, debemos definir funciones y procedimientos que so-
porten operaciones básicas sobre este tipo. Por ejemplo, se pueden incluir las siguientes decla-
raciones:

bool EsComplejo(Complejo u);


Complejo Suma(Complejo u, Complejo v);
Complejo Producto(Complejo u, Complejo v);
double ParteReal(Complejo u);
double ParteImaginaria(Complejo v);

Sería deseable que una vez que hemos definido el tipo Complejo y sus operaciones básicas, este
nuevo tipo se pudiera emplear como si fuera un tipo predefinido de C++.

El propósito de las clases en C++ es facilitar al programador una herramienta que le permita
definir un nuevo tipo que se pueda usar como un tipo predefinido de C++.

En este capítulo intentamos mostrar que es posible definir variables de este nuevo tipo de la
misma manera que con variables de tipos básicos. Se podrá crear, manipular y destruir tantos
objetos de tipo Complejo como se quiera. De esta manera veremos cómo C++ incorpora la
noción de clase del paradigma de Orientación a Objetos.

La palabra class es la palabra reservada para implementar este concepto.

77
CAPÍTULO 8. CLASES Y OBJETOS 78

8.1. Definición de clases


Las clases se definen en C++ mediante una construcción dividida en dos partes: una parte
privada (private) que contiene algunos detalles de la implementación y funcionamiento, y una
parte pública (public) que contiene todas las instrucciones de uso de la clase (métodos). La
sintaxis de uso es la siguiente:

class Complejo {
private:
// Implementacion clase (atributos)
double real;
double imaginario;
public:
// Métodos
void asigna_real(double a);
void asigna_imag(double b);
double parte_real();
double parte_imag();
void suma(const Complejo &a, const Complejo &b);
}

Los campos real e imag son los atributos de la clase y codifican el estado de un objeto de la
clase Complejo. Puesto que los atributos están declarados en la parte privada de la clase, forman
parte de la implementación y no es posible acceder a ellos desde fuera de la clase. Su acceso está
restringido: sólo se puede acceder a ellos en la implementación de los métodos de la clase.

Los métodos que aparecen en la parte pública describen el comportamiento de la clase; es decir,
las operaciones que podemos aplicar a un objeto del tipo Complejo. En particular, con estos
métodos podemos asignar valores a las partes real e imaginaria, leer las partes real e imaginaria,
y sumar dos numeros complejos.

La implementación de los métodos de una clase se realiza fuera de la construcción de la clase.


La sintaxis de la definición de un método es similar al de la definición de una función, excepto
que el nombre del método debe estar precedido por el nombre de la clase de la que forma parte.
Así, el método asigna_real se implementa como sigue:

void Complejo:: asigna_real(double a){


// Instrucciones del método
real = a;
}

donde el atributo real que aparece a la izquierda de la asignación es el atributo del argumento
implícito. Incluso un método como parte_imaginaria, que aparentemente no tiene argumentos,
recibe este argumento implícito que representa el objeto al que se aplica el método:

double Complejo::parte_imaginaria(){
return imag;
}

Por otro lado, un método puede recibir argumentos explícitos de la clase a la que pertenece. Por
ejemplo, el método suma recibe dos argumentos explícitos de tipo Complejo. Al definir el método
suma, podemos acceder a los atributos de los argumentos explícitos utilizando la notación punto
usual, como si se tratara de una estructura:
CAPÍTULO 8. CLASES Y OBJETOS 79

void Complejo::suma(const Complejo& a, const Complejo& b){


real= a.real + b.real;
imag= a.imag + b.imag;
}

8.2. Uso de clases


Una vez que se ha definido e implementado una clase, es posible declarar objetos de esta clase y
usarlos desde fuera de la clase como si se tratara de tipos predefinidos. En concreto, una variable
de un tipo clase se declara como cualquier otra variable:

Complejo x; // Variable de tipo Complejo


Complejo y[5]; // Vector de Complejos

Sobre un objeto de una clase sólo pueden aplicarse las siguientes operaciones:

1. Aquellas definidas dentro de la clase.

2. La asignación.

3. El paso de parámetros por valor o referencia.

Aparte de la asignación y del paso de parámetros por referencia, toda la manipulación de los
objetos por parte del usuario debe hacerse a través de los métodos:

x.asigna_real(4);
x.asigna_imag(5);

8.3. Constructores y destructores


Como todas las variables de C++, los objetos no están inicializados por defecto. Si el usuario
declara objetos Complejo y opera con ellos sin asignarles un valor inicial, el resultado de la
operación no estará definido:

Complejo m,n,o;
o.suma(m,n);

cout << o.parte_imag() << endl; // Imprime cualquier cosa

Para paliar este problema, C++ permite definir un método especial llamado el constructor de
la clase, cuyo cometido es precisamente inicializar por defecto los objetos de la clase. Para que
nuestros números complejos estén inicializados, basta añadir un constructor a la clase Complejo
de la siguiente manera:

class Complejo {
private:
// Implementacion clase (atributos)
double real;
double imaginario;
public:
Complejo(); // Añadimos el constructor
// Métodos
void asigna_real(double a);
void asigna_imag(double b);
double parte_real();
CAPÍTULO 8. CLASES Y OBJETOS 80

double parte_imag();
void suma(const Complejo &a, const Complejo &b);
}

La implementación del constructor se hace fuera de la construcción de la clase. En nuestro caso,


podemos optar por inicializar los complejos en 0 + 0i:

Complejo::Complejo(){
real= 0;
imaginario= 0;
}

Un constructor es un método muy diferente de todos los demás. En primer lugar, su nombre
coincide siempre con el de la clase a la que pertenece. Además, un constructor no es ni un pro-
cedimiento ni una función, y por lo tanto no tiene asociado ningún tipo de retorno.

Por último, el usuario nunca invoca un constructor de manera explícita. Esto no tendría sentido,
pues de lo que se trata es de que los objetos sean inicializados de manera implícita por C++,
sin intervención alguna por parte del usuario. Por ello, el constructor de una clase es invocado
automáticamente justo después de cada declaración un objeto de esa clase.

Complejo m,n,o;
m.asigna_real(3);
o.suma(m,n);

cout << o.parte_real() << endl;

Además del constructor por defecto, es posible asociar a una clase un constructor extendido en
que se indiquen mediante argumentos los valores a los que se debe inicializar un objeto de la
clase.

class Complejo {
private:
// Implementacion clase (atributos)
double real;
double imaginario;
public:
Complejo();
Complejo(double a, double b); // Constructor extendido
// Métodos
void asigna_real(double a);
void asigna_imag(double b);
double parte_real();
double parte_imag();
void suma(const Complejo &a, const Complejo &b);
}

La implementación del constructor extendido es inmediata, basta emplear los argumentos para
inicializar los atributos:

Complejo::Complejo(double a, double b){


real = a;
imaginario = b;
}
CAPÍTULO 8. CLASES Y OBJETOS 81

Para que C++ ejecute de forma automática el constructor extendido, basta añadir a la decla-
ración de un objeto los valores a los que desea que se inicialice:
Complejo x(3,2); // Inicializa un complejo en 3 + 2i
De la misma manera que C++ permite definir constructores para los objetos de una clase,
también es posible definir un destructor que se encargue de destruir los objetos automáticamente,
liberando los recursos que pudieran tener asignados:
class Complejo {
private:
// Implementacion clase (atributos)
double real;
double imaginario;
public:
Complejo();
Complejo(double a, double b);
~Complejo(); // Destructor de la clase
// Métodos
void asigna_real(double a);
void asigna_imag(double b);
double parte_real();
double parte_imag();
void suma(const Complejo &a, const Complejo &b);
}
El nombre del destructor es siempre el de la clase a la que pertenece antecedido por el símbolo
∼. Al igual que los constructores, los destructores se definen fuera de la construcción de la clase
y no tienen tipo de retorno alguno:
Complejo::~Complejo(){
// destruir el numero complejo
}
Es importante recordar que sólo puede definirse un destructor para cada clase, y que éste nun-
ca toma argumentos. Además, el usuario nunca debe ejecutar un destructor de forma explícita.
Los destructores son invocados automáticamente por C++ cada vez que un objeto deja de existir.

A continuación, se muestra un programa en el que se crea la clase Caja:


1 # include < iostream >
2 # include < cmath >
3 using namespace std ;
4
5 class Caja {
6 private :
7 double ancho ;
8 double largo ;
9 double alto ;
10
11 public :
12 Caja ();
13 Caja ( double a , double b , double c );
14 double Volumen ();
15 double Area ();
CAPÍTULO 8. CLASES Y OBJETOS 82

16 };
17
18 Caja :: Caja (){
19 ancho =0;
20 largo =0;
21 alto =0;
22 };
23
24 Caja :: Caja ( double a , double b , double c ){
25 ancho = a ;
26 largo = b ;
27 alto = c ;
28 };
29
30 Caja :: Volumen (){
31 return ancho * largo * alto ;
32 };
33
34 Caja :: Area (){
35 return 2*( ancho * largo + ancho * alto + largo * alto );
36 }
37
38 int main (){
39 Caja M (0.98 , 0.85 , 0.75);
40 cout << " Volúmen de la caja : " << M . Volumen () << endl ;
41 cout << " Area de la caja : " << M . Area () << endl ;
42 return 0;
43 }

8.4. Sobrecarga de operadores


La sobrecarga de operadores permite que los operadores estándares de C++ puedan progra-
marse para operar con los tipos de datos o clases definidas por el usuario. No está permitido
inventar nuevos operadores ni cambiar la precedencia de los existentes. Obviamente, se espera
que el comportamiento de un operador sobrecargado sea consistente con el uso normal que se le
da al mismo y razonablemente intuitivo.

La sintaxis para sobrecargar un operador es la siguiente:

<tipo> <Nombre de la clase> operator <#> (lista de parámetros){


// instrucciones del operador
}

donde:

tipo de dato: indica el tipo de dato que produce el operador sobrecargado, por lo general
es la referencia del objeto.

Nombre de la clase: es el nombre de clase en la que estamos aplicando sobrecarga.

operator: es una palabra reservada utilizada para realizar la sobrecarga

#: operador que estamos sobrecargando.


CAPÍTULO 8. CLASES Y OBJETOS 83

Lista de parámetros: es la lista de objetos (en referencia) o variables que se procesarán


en la sobrecarga, si la operación es unaria no tendrá parámetros, si la operación es binaria
tendrá un parámetro.
El lenguaje C++ permite redefinir la funcionalidad de los siguientes operadores:
+ - * / % ^ & ~ ! = < > += -= *= /= %= ^= &= |=
<< >> >>= <<= == != <= >= && || ++ -- ->* -> [] ()
Los operadores +, −, ∗ y & son sobrecargables en sus dos versiones, unaria y binaria.
A continuación, un ejemplo de operadores sobrecargados para la clase Complejo:
Complejo Complejo::operator+ (const Complejo &a){
Complejo suma;
suma.real = real + a.real;
suma.imaginario = imaginario + a.imaginario;
return suma;
}

Complejo & Complejo::operator= (const Complejo &a) {


real = a.real;
imaginario = a.imaginario;
return (*this);
}

8.5. Sobrecarga de los operadores de entrada y salida


Un caso especial es la sobrecarga de los operadores << y >> utilizados en la salida y entrada
de datos respectivamente. Estos operadores se encuentran definidos en la biblioteca iostream de
C++.

La sintaxis empleada para la sobrecarga del operador de entrada es la siguiente:


friend istream & operator >> (istream &, <Nombre de la clase> &)
La sintaxis empleada para la sobrecarga del operador de salida es la siguiente:
friend ostream & operator << (ostream &, <Nombre de la clase> &)
Observe que las funciones que se obtienen al sobrecargar los operadores se declaran como amigas
(friend) de la clase en la cual se insertaron. Esto es para que dichas funciones, externas a la clase,
puedan tener acceso a los miembros privados de la misma.

A continuación, un ejemplo de la sobrecarga del operador de salida para la clase Complejo:


ostream & operator << (ostream & co, const Complejo & a){
co << a.real;
co << " + ";
co << a.imaginario << "i";
return co;
}

8.6. Ejercicios
1.

También podría gustarte