Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Programaci N 1
Programaci N 1
Programación en C++
Primera Edición
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
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.
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.
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.
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.
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.
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.
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
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.
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.
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
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.
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.
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.
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.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.
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.
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. 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.
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 .
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/
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.
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).
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>
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
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)
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.
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
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.
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.
(tipo) operando
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:
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
if(expresión lógica){
bloque de instrucciones 1
} else {
bloque de instrucciones 2
}
CAPÍTULO 2. SENTENCIAS CONDICIONALES Y REPETITIVAS 17
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
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.
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.
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.
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
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.
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
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
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.
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.
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
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.
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
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.
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.
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
3.3. Arrays
C++ permite trabajar con arrays multidimensionales, es decir, de varias dimensiones.
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.
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
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.
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.
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).
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.
36
CAPÍTULO 4. NÚMEROS ALEATORIOS 37
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
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 }
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
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.
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.
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.
Las funciones devuelven únicamente tipos básicos: int, double, float, string, etc.
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 }
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
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
A continuación, un programa que calcula el logaritmo en diferentes bases (por defecto se mantiene
la base 2):
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.
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
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 }
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.
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.
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.
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.
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.
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.
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 }
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 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.
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.
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 }
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.
30 impcomplejo ( z );
31
32 return 0;
33 }
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.
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 }
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.
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.
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.
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 }
int *pun = 0;
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 }
Tipo sizeof
bool 1
int 4
double 8
string 8
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.
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 }
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.
Las estructuras de la STL poseen métodos (funciones propias) que facilitan el trabajo, entre los
métodos más empleados podemos mencionar:
back: Accede al primer elemento por fuera del vector (elemento posterior al último).
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:
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.
La STL ofrece otro modo de recorrer los vectores distinto al método tradicional de usar un
índice, a partir de los iteradores.
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.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:
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:
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.
77
CAPÍTULO 8. CLASES Y OBJETOS 78
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.
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
Sobre un objeto de una clase sólo pueden aplicarse las siguientes operaciones:
2. La asignación.
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);
Complejo m,n,o;
o.suma(m,n);
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);
}
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);
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:
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.
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 }
donde:
tipo de dato: indica el tipo de dato que produce el operador sobrecargado, por lo general
es la referencia del objeto.
8.6. Ejercicios
1.