Sie sind auf Seite 1von 99

Qué es un Lenguaje de Programación[1]

Antes de hablar de C++, es necesario explicar que un lenguaje de programación es una


herramienta que nos permite comunicarnos e instruir a la computadora para que realice una
tarea específica. Cada lenguaje de programación posee una sintaxis y un léxico particular,
es decir, forma de escribirse que es diferente en cada uno por la forma que fue creado y por
la forma que trabaja su compilador para revisar, acomodar y reservar el mismo programa en
memoria.

Existen muchos lenguajes de programación de entre los que se destacan los siguientes:

1. C ▶
2. C++ ▶
3. Basic
4. Ada ▶
5. Java ▶
6. Pascal ▶
7. Python □
8. Fortran
9. Smalltalk

Historia de C++
C++ es un lenguaje de programación creado por Bjarne Stroustrup en los laboratorios de
At&T en 1983. Stroustrup tomó como base un lenguaje de programación popular en aquella
época el cual era C.

El C++ es un derivado del mítico lenguaje C, el cual fue creado en la década de los 70 por
la mano del finado Dennis Ritchie para la programación del sistema operativo [1] (un
sistema parecido a Unix es GNU/Linux), el cual surgió como un lenguaje orientado a la
programación de sistemas (System Programming) y de herramientas (Utilities)
recomendado sobre todo para programadores expertos, y que no llevaba implementadas
muchas funciones [¿cómo cuáles?] que hacen a un lenguaje más comprensible.

Sin embargo, aunque esto en un inicio se puede convertir en un problema, en la práctica es


su mayor virtud, ya que permite al programador un mayor control sobre lo que está
haciendo. Años más tarde, un programador llamado Bjarne Stroustrup, creo lo que se
conoce como C++.

Necesitaba ciertas facilidades de programación, incluidas en otros lenguajes pero que C no


soportaba, al menos directamente, como son las llamadas clases y objetos, principios
usados en la programación actual. Para ello rediseñó C, ampliando sus posibilidades pero
manteniendo su mayor cualidad, la de permitir al programador en todo momento tener
controlado lo que está haciendo, consiguiendo así una mayor rapidez que no se conseguiría
en otros lenguajes.
C++ pretende llevar a C a un nuevo paradigma de clases y objetos con los que se realiza
una comprensión más humana basándose en la construcción de objetos, con características
propias solo de ellos, agrupados en clases. Es decir, si yo quisiera hacer un programa sobre
animales, crearía una clase llamada animales, en la cual cada animal, por ejemplo un pato,
sería un objeto, de tal manera que se ve el intento de esta forma de programar por ser un fiel
reflejo de cómo los humanos (en teoría) manejamos la realidad[referencia].

Se dice que nuestro cerebro trabaja de forma relacional[referencia] (relacionando hechos),


es por ello que cada vez que recuerdas algo, (cuentas un hecho), termina siendo diferente
(se agregan u omiten partes).

Qué es C++

C++ es un lenguaje de programación orientado a objetos que toma la base del lenguaje C y
le agrega la capacidad de abstraer tipos como en Smalltalk.

La intención de su creación fue el extender al exitoso lenguaje de programación C con


mecanismos que permitieran la manipulación de objetos. En ese sentido, desde el punto de
vista de los lenguajes orientados a objetos, el C++ es un lenguaje híbrido [¿por? fusionar
con el siguiente].

Posteriormente se añadieron facilidades de programación genérica, que se sumó a los otros


dos paradigmas que ya estaban admitidos (programación estructurada y la programación
orientada a objetos). Por esto se suele decir que el C++ es un lenguaje de programación
multiparadigma.

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

1. Un equipo ejecutando un sistema operativo.


2. Un compilador de C++
1. Windows MingW (GCC para Windows) o MSVC (compilador de microsoft
con versión gratuita)
2. Linux (u otros UNIX): g++
3. Mac (con el compilador Xcode)
3. Un editor cualquiera de texto, o mejor un entorno de desarrollo (IDE)
1. Windows:
1. Microsoft Visual C++ (conocido por sus siglas MSVC). Incluye
compilador y posee una versión gratuita (versión express)
2. Bloc de notas (no recomendado)
3. Editor Notepad++
4. DevCpp (incluye MingW - en desuso, no recomendado, incluye
también un compilador)
5. Code::Blocks
2. Linux (o re-compilación en UNIX):
1. Gedit
2. Kate
3. KDevelop
4. Code::Blocks
5. SciTE
6. GVim
3. Mac:
1. Xcode (con el compilador trae una IDE para poder programar)
4. Tiempo para practicar
5. Paciencia

Adicional

1. Inglés (Recomendado)
2. Estar familiarizado con C u otro lenguaje derivado (PHP, Python, etc).

Es recomendable tener conocimientos de C, debido a que C++ es una mejora de C, tener los
conocimientos sobre este te permitira avanzar mas rapido y comprender aun mas. Tambien,
hay que recordar que C++, admite C, por lo que se puede programar (reutilizar), funciones
de C que se puedan usar en C++.

Aunque No es obligacion aprender C, es recomendable tener nociones sobre la


programación orientada a objetos en el caso de no tener conocimientos previos de
programación estructurada. Asimismo, muchos programadores recomiendan no saber C
para saber C++, por ser el primero de ellos un lenguaje imperativo o procedimental y el
segundo un lenguaje de programación orientado a objetos.

Consejos iniciales antes de programar


Con la práctica, se puede observar que se puede confundir a otros programadores con el
código que se haga. Antes de siquiera hacer una línea de código, si se trabaja con otros
programadores, ha de tenerse en cuenta que todos deben escribir de una forma similar el
código, para que de forma global puedan corregir el código en el caso de que hubieran
errores o rastrearlos en el caso de haberlos.

También es muy recomendable hacer uso de comentarios (comenta todo lo que puedas, hay
veces que lo que parece obvio para ti, no lo es para los demás) y tratar de hacer un código
limpio y comprensible, especificando detalles y haciendo tabulaciones, aunque te tome un
poco mas de tiempo, es posible que mas adelante lo agradezcas tu mismo.
Estructura de un programa
En general, los programas escritos en C++ tienen una estructura (básica) compuesta por tres
secciones:

1. Directivas de preprocesador
2. declaraciones globales
3. declaración de funciones

Directivas de preprocesador

Los compiladores de C++ proporcionan bibliotecas de funciones, y cada biblioteca de


funciones tiene asociada un archivo de definición que se denomina cabecera. Para utilizar
algo de una biblioteca en un programa (por ejemplo, una función), hay que colocar al
principio del programa una directiva de preprocesamiento seguida de la cabecera de la
biblioteca entre signos de "menor que" y "mayor que" (<>).

A continuación se muestra un típico ejemplo de una directiva de preprocesador:

#include <iostream>

En el ejemplo anterior, la directiva invocada es include y la cabecera iostream, e indican al


preprocesador que debe incluir la librería iostream a nuestro programa. Cabe señalar que
todas las directivas comienzan con el símbolo numeral #. Dentro de las directivas de
preprocesador se encuentran:

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


2. Inclusiones condicionales (#ifdef, #ifndef, #if, #endif, #else and #elif)
3. Control (#line)
4. Error (#error)
5. Inclusión de fichero (#include)
6. Pragma (#pragma)

Macros de preprocesador

Para definir macros de preprocesador, usamos la directiva #define. La sintaxis es:

#define identificador reemplazo

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

#undef nombre_macro

Inclusiones condicionales

Estas directivas permiten incluir o descartar partes de código, si se cumplen algunas


condiciones.

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

#ifdef TABLE_SIZE
int table[TABLE_SIZE];
#endif

El ejemplo anterior indica que, si la macro TABLE_SIZE se encuentra definida, entonces


procede a la creación de una matriz de enteros de tamaño TABLE_SIZE. Cabe señalar que
el fin para esta directiva es la directiva #endif

 #ifndef: Permite realizar exactamente todo lo contrario a #ifdef. La lineas de código que se
encuentren entre #ifndef y #endif, serán ejecutadas siempre y cuando la macro señalada
como parámetro de #ifndef no se encuentre definida aun. Por ejemplo:

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

En el ejemplo anterior, se indica que si la macro TABLE_SIZE no está definida aún,


entonces se define. Cabe señalar que el fin para la directiva #ifndef es, al igual que #ifdef,
#endif.

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

#if TABLE_SIZE>200
#undef TABLE_SIZE
#define TABLE_SIZE 200

#elif TABLE_SIZE<50
#undef TABLE_SIZE
#define TABLE_SIZE 50

#else
#undef TABLE_SIZE
#define TABLE_SIZE 100
#endif

int table[TABLE_SIZE];
En el ejemplo anterior, se muestra el uso de todas las directivas condicionales del
preprocesador.

Control

Cuando compilamos un programa y un error ocurre durante el proceso de compilación, el


compilador muestra un mensaje de error con las referencias al nombre del archivo donde
ocurrió el error y un número de línea, por lo que es más fácil encontrar el código que
genera el error. La directiva #line nos permite controlar ambas cosas, los números de línea
en los archivos de código, así como el nombre del archivo que queremos que aparece
cuando un error ocurre. Su formato es:

#line numero "nombre_de_archivo"

Donde número es el nuevo número de línea que se asignará a la siguiente línea de código.
Los números de línea de las líneas sucesivas se incrementarán uno por uno desde este punto
en adelante."nombre_de_archivo" es un parámetro opcional que permite redefinir el
nombre del archivo que se mostrará. Por ejemplo:

#line 20 "asignacion de variable"


int a?;

Este código generará un error que se mostrará como un error en el archivo de "asignación
de variable", línea 20.

Error

Esta directiva cancela la compilación cuando es encontrada, generando un error de


compilación que puede ser especificado según un parámetro de un ejercicio. Por ejemplo:

#ifndefcplusplus
#error A Se requiere compilador de C++
#endif

En el ejemplo anterior, la compilación es cancelada si la macro __cplusplus no está


definida (Esta macro es definida por defecto en todos los los compiladores de C++).

Inclusión de fichero

Cuando el preprocesador encuentra la directiva #include, este reemplaza todas las


ocurrencias de ella por el archivo o cabecera especificada. Existen dos formas de utilizar
#include:

 #include <cabecera>: Es usado para incluir las cabeceras proporcionadas por defecto, por
ejemplo, la librería estándar (string, iostream, etc.).
 #include "archivo" : Es usado para incluir archivos.
Pragma

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

Declaraciones globales

En esta seccion se declaran todas variables y cabeceras de funciones que seran vistas de
manera global, es decir, que su alcance es total en el programa. Por ejemplo:

#include <iostream>
#define PI 3.1415

std::string autor: "Wikibooks";


int adicion(int, int);

En el ejemplo anterior, se definen (ademas de las directivas de preprocesador, vistas en la


sección anterior) una variable y una función. La variable autor podrá ser utilizada por todas
las funciones del programa. Mientras que la función "adicion" solo fue declarada de la
forma "inline". Una función se dice declarada "inline" cuando solo se señala su nombre y
sus tipos de entrada y salida. Su definición completa se reserva para mas adelante.

Declaración de funciones

La ultima sección del programa es la declaración de funciones. La primera función que se


debe declarar es la función principal o "main". La función main es la mas importante, pues
es la que es invocada cuando el programa se ejecuta. Toda instrucción que no sea declarada
dentro de esta, simplemente no sera considerada. A continuación de la función "main", se
permite la definición completa de las funciones declaradas en la sección de declaraciones
globales. En el siguiente ejemplo, mostraremos la estructura definitiva de un programa y la
declaración completa de sus funciones:

#include <iostream>
#define PI 3.1415

std::string autor= "Wikibooks";


int adicion(int, int);

int main(int argc, char **argv)


{
std::cout<<"El resultado de la suma de 1 y 2 es
"<<adicion(1,2)<<std::endl;
return 0;
}

int adicion(int a, int b)


{
return a+b;
}

Los parámetros de entrada de la función main es algo que se abordará mas adelante.

Proceso de desarrollo de un programa


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

1. Escribir con un editor de texto plano un programa sintácticamente válido o usar un


entorno de desarrollo (IDE) apropiado para tal fin
2. Compilar el programa y asegurarse de que no han habido errores de compilación
3. Ejecutar el programa y comprobar que no hay errores de ejecución

Este último paso es el más costoso, por que en programas grandes, averiguar si hay o no un
fallo prácticamente puede ser una tarea totémica.

Como ejemplo, si se desea escribir un archivo con el nombre hola.cpp y en él escribir un


programa con emacs, por ejemplo, que es un programa de edición de textos, se puede, en
GNU, ejecutar el siguiente comando:

$emacs hola.cpp &

Para otros sistemas operativos u otros entornos de desarrollo, no necesariamente se sigue


este paso.

A continuación se escribe el siguiente código en C++:


Ejemplo

// Aquí generalmente se suele indicar qué se quiere con el programa a


hacer
// Programa que muestra 'Hola mundo' por pantalla y finaliza

// Aquí se sitúan todas las librerias que se vayan a usar con #include,
// que se verá posteriormente
// Las librerias del sistema son las siguientes
#include <iostream>

// Función main
// Recibe: void
// Devuelve: int
// Función principal, encargada de mostrar "Hola Mundo",por pantalla

int main(void)
{
// Este tipo de líneas de código que comienzan por '//' son
comentarios
// El compilador los omite, y sirven para ayudar a otros programadores
o
// a uno mismo en caso de volver a revisar el código
// Es una práctica sana poner comentarios donde se necesiten,

std::cout << "Hola Mundo" << std::endl;

// Mostrar por std::cout el mensaje Hola Mundo y comienza una nueva


línea

return 0;

// se devuelve un 0.
//que en este caso quiere decir que la salida se ha efectuado con
éxito.
}

Mediante simple inspección, el código parece enorme, pero el compilador lo único que
leerá para la creación del programa es lo siguiente:
Ejemplo

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

Como se puede observar, este código y el original no difieren en mucho salvo en los saltos
de línea y que los comentarios, de los que se detallan posteriormente, están omitidos y tan
sólo ha quedado "el esqueleto" del código legible para el compilador. Para el compilador,
todo lo demás, sobra.

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

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

Nota: si se usa Windows, el código es el mismo, pero debemos agregar un metodo mas para
que el programa se mantenga abierto y no se cierre la consola, cosa que en GNU, no es
necesaria por que la consola ya esta abierta (al mandar a ejecutar).

Para esto podemos usar cin.get() que nos permitira leer del teclado, por lo que el programa
no finalizara, hasta que el usuario pulse enter.

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

Los pasos siguientes son para una compilación en GNU o sistema operativo Unix. En
Windows tampoco es aplicable.

Con ctrl-x ctrl-s se guarda el archivo. Ahora para generar el ejecutable del programa se
compila con g++ de la siguiente forma:

$ g++ hola.cpp -o hola

Para poder ver los resultados del programa en acción, se ejecuta el programa de la siguiente
forma:

$./hola

Y a continuación se debe mostrar algo como lo siguiente:

Hola Mundo

Comentarios

Cuando se escriben programas es muy útil agregar comentarios que ayuden a explicar lo
que realiza un programa. En C++ se pueden utilizar tres tipos de comentarios: al estilo C, al
estilo C++ y usando preprocesador.

Los comentarios al estilo C se caracterizan por lo siguiente: comenzar el "bloque" de


comentarios con /* y terminar dicho "bloque" de comentarios con */

Ej:

/*

Este es un comentario al estilo C.


Todo lo escrito dentro de las etiquetas de apertura y cierre es un
comentario.
A estos comentarios se le llaman multilinea, logicamente
por el hecho de permitir varias lineas de comentarios.

*/
Si se usan este tipo de etiquetas de comentarios, hay que tener cuidado con el cierre (*/),
por que el compilador puede tomar todo el texto como comentario, o cerrar antes de lo
deseado.

Usando el estilo de código de C++ sólo pueden ocupar una línea como en el siguiente
código:

// Este es un comentario al estilo C++

Una buena práctica de programación es pensar que se programa a sabiendas de que otro
programador, tal vez el lector mismo en un futuro, tenga que "desencriptar" qué quiso hacer
ahí y por qué.

Otra posible forma de comentar código es usando el preprocesador. Esto se detallará más
adelante en profundidad, por ahora la parte útil del preprocesador que interesa es la
siguiente:

#if 0
Comentarios sobre el programa /parte del programa.
Pueden ocupar múltiples líneas.

Más complicado de visualizar que los comentarios C/C++


#endif

Este tipo de comentarios se usan rara vez. Generalmente son difíciles de localizar, incluso
para programadores experimentados que trabajan en papel, y son fáciles de distinguir en
casi cualquier IDE. Es recomendable indicar que se tratan de comentarios, o directamente
no usarlos, salvo si son grandes cantidades de comentarios. Se verá más adelante que
también puede tener otros usos.

Utilización de la consola o terminal

En los ejemplos anteriores se utilizaron 'std::cout'. 'std::cout' es un "objeto" que permite


escribir en la consola (la terminal en GNU/Unix/MacOSX), solo se puede utilizar gracias a
que se ha incluido definiciones de su uso con la línea de código '#include <iostream>'.

std::cout << ALGO;

Donde ALGO puede ser lo que sea que 'std::cout' sea capaz de mostrar por consola. Más
adelante se trata más sobre ello en detalle y aclarando posibles dudas que ahora puedan
surgir. También se utilizó 'std::endl', esto permite que el texto se escriba en una nueva línea.

Un ejemplo más completo sobre la escritura en consola es el siguiente. Ha de tenerse en


cuenta que se han eliminado algunos comentarios superfluos del primer programa debido a
que ahora se está tratando con más detalle acerca del uso de imprimir texto en la consola:
Ejemplo
// Programa que muestra diversos textos por consola

// Las librerías del sistema usadas son las siguientes


#include <iostream>

// Función: main
// Recibe: void
// Devuelve: int
// Es la función principal encargada de mostrar por consola diferentes
textos
int main(void)
{
// Ejemplo con una única línea, se muestra el uso de std::cout y
std::endl
std::cout << "Bienvenido. Soy un programa. Estoy en una linea de
codigo." << std::endl;

// Ejemplo con una única línea de código que se puede fraccionar


mediante el uso de '<<'
std::cout << "Ahora "
<< "estoy fraccionado en el codigo, pero en la consola me muestro como
una unica frase."
<< std::endl;

// Uso de un código largo, que cuesta leer para un programador, y que


se ejecutará sin problemas.
// *** No se recomienda hacer líneas de esta manera, esta forma de
programar no es apropiada ***
std::cout << "Un gran texto puede ocupar muchas lineas."
<< std::endl
<< "Pero eso no frena al programador a que todo se pueda poner en una
unica linea de codigo y que"
<< std::endl
<< "el programa, al ejecutarse, lo situe como el programador quiso"
<< std::endl;

return 0; // Y se termina con éxito.


}

Se reta a compilar este código y a observar sus resultados. En este momento el lector está
capacitado para escribir programas que impriman por pantalla el mensaje que se quiera.

Atención: Se hace hincapié en la posibilidad de que las palabras acentuadas no se puedan


mostrar en la consola. Depende completamente del compilador que se pueda ver lo
siguiente:

std::cout << "programación";

Con algunos compiladores, verá 'programación', pero con otros puede ver incluso
'programaci n'.
Advertencia: cout puede ser utilizado sin tener std:: de forma previa porque se puede introducir
una directiva, denominada 'using', que acomoda todos los cout. De otro modo habría un error de
compilador. Este tema se trata en detalle más adelante.

using namespace std;

Sintaxis
Sintaxis es la forma correcta en que se deben escribir las instrucciones para el computador
en un lenguaje de programación específico. C++ hereda la sintaxis de C estándar, es decir,
la mayoría de programas escritos para el C estándar pueden ser compilados en C++.

El punto y coma

El punto y coma es uno de los simbólos más usados en C, C++; y se usa con el fin de
indicar el final de una línea de instrucción. El punto y coma es de uso obligatorio.

ejemplo
´´´´´´

clrscr(); //Limpiar pantalla, funciona solo con la librería conio de


Borland C++
x = a + b;

string IP = "127.0.0.1"; // Variable IP tipo string


cout << IP << endl; // Devuelve 127.0.0.1

char Saludo[5] = "Hola"; // Variable Saludo tipo char


cout << Saludo[0] << endl; // Igual a H
cout << Saludo[1] << endl; // Igual a o
cout << Saludo[2] << endl; // Igual a l
cout << Saludo[3] << endl; // Igual a a

El punto y coma se usa también para separar contadores, condicionales e incrementadores


dentro de una sentencia for

ejemplo

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

Espacios y tabuladores

Usar caracteres extras de espaciado o tabuladores ( caracteres tab ) es un mecanismo que


nos permite ordenar de manera más clara el código del programa que estemos escribiendo,
sin embargo, el uso de estos es opcional ya que el compilador ignora la presencia de los
mismos. Por ejemplo, el segundo de los ejemplos anteriores se podría escribir como:
for (int i=0; i < 10; i++) { cout << i * x; x++; }

y el compilador no pondría ningún reparo.

Tipos primitivos
En un lenguaje de programación es indispensable poder almacenar información, para esto
en C++ están disponibles los siguientes tipos que permiten almacenar información
numérica de tipo entero o real:

Nombre Descripción Tamaño* Rango de valores*

con signo: -128 to 127


char Carácter o entero pequeño 1byte
sin signo: 0 a 255
short int con signo: -32768 a 32767
Entero corto 2bytes
(short)
sin signo: 0 a 65535
con signo: -2147483648 a
int Entero 4bytes 2147483647

sin signo: 0 a 4294967295


con signo: -2147483648 a
long int
Entero largo 8bytes 2147483647
(long)
sin signo: 0 a 4294967295
Valor booleano. Puede tomar dos valores:
bool 1byte true o false
verdadero o falso

float Número de punto flotante 4bytes 3.4e +/- 38 (7 digitos)

double De punto flotante de doble precisión 8bytes 1.7e +/- 308 (15 digitos)

long double Long de punto flotante de doble precisión 8bytes 1.7e +/- 308 (15 digitos)

 Los valores dependen de la arquitectura utilizada. Los mostrados son los que
generalmente se encuentran en una máquina típica de arquitectura 32 bits.
El modificador long

El modificador long le indica al compilador que el tipo debe utilizar más bits que los
normalmente utilizados por ejemplo si tenemos en una maquina de 32 bits como un
Pentium de Intel, normalmente de un int ocupara 32 bits, pero si al declarar un entero le
antecedemos long, este entero ocupa 64 bits, el siguiente código muestra como utilizar este
modificador:

int corto; // Entero de 32 bits


long int largo; // Entero de 64 bits

El Modificador short

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

El Modificador unsigned

El modificador unsigned es utilizado únicamente con los enteros, su utilización permite


utilizar en los enteros únicamente la parte positiva,

int a; // Almacena valores entre -32768 y 32767


unsigned int a; // Almacena valores entre 0 y 65535

El Modificador register

Este modificador sobre una variable le indica al compilador que la variable debe
almacenarse en un registro en el compilador, que para el caso de los IA32, es un registro
real de la propia CPU, y por tanto el tiempo de acceso es más rápido respecto a la memoria
RAM. Hoy en día apenas se utiliza este modificador, ya que los compiladores son capaces
de determinar de manera óptima la asignación de registros a variables del programa.

El Modificador volatile

Al contrario que el modificador registrer, volatile obliga al compilador a forzar el código


resultante de manera que la variable modificada con este modificador, sea almacenada
siempre en la memoria. El efecto que tiene es que cuando la variable se modifica con otro
valor, dicha variable se almacena directamente en memoria y no queda localizado el valor
sólo en el registro de la CPU como pasaba en el caso de register o en condiciones normales.
Un uso muy común en el que se suele emplear este modificador, es para acceder a variables
que están siendo utilizadas por drivers o por periféricos, ya que si no declarásemos esta
propiedad, podría darse el caso que la CPU usase el valor de la variable, por lo que la caché
guarda el valor, y poco después la CPU usase de nuevo dicha variable, pero como ésta está
en cache, la CPU coge el valor que existe en la caché, que puede ser bien distinta al real
puesto que un posible periférico puede haber modificado su valor.
El Modificador static

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

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

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

1. Si declaramos una variable static fuera del cuerpo de una función, lo que le estamos
indicando al compilador, es que dicha variable es privada para el modulo donde se
implementa el código del contexto de la variable, es decir, que otro fichero objeto binario,
no podrá tener acceso a dicha variable utilizando el modificador extern. Ejemplo:

#import "prueba.h"

//variable privada para prueba.cpp


static int i=0;

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

Tenga en cuenta que para este último caso, usted podrá acceder a la variable y desde
cualquier función que defina dentro de prueba.cpp, pero no tendrá acceso desde cualquier
fichero objeto o fuente que no sea prueba.cpp

Espacio que ocupan la variables (en máquinas x86)

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

Tipo Número de Bits

char 8
short 16

int 32

long int 64

float 32

double 64

Rango de los Tipos Primitivos

El rango que puede almacenar los tipos primitivos en C++ es muy importante, para poder
saber cual es el rango de valores que puede almacenar un tipo es necesario conocer el
número de bits del tipo. El caso para enteros y para flotantes es distinto. Para enteros se
debe saber si el tipo es con signo o sin signo. si es sin signo el rango de valores que puede
almacenar es el siguiente:

Si el tipo es con signo el rango es el siguiente

Para ilustrar lo anterior supongamos que tenemos un entero de 16 bits sin signo, entonces el
rango de valores que puede almacenar es el siguiente:

Para obtener el rango de un entero de 32 bits con signo se puede realizar el siguiente
calculo:

El caso de los números flotantes es distinto y depende en gran manera del compilador y el
procesador que este utilizando. Sin embargo hoy en día la mayoría de los compiladores y
los procesadores utilizan en estándar de la IEEE para representación en coma flotante. Para
saber mas al respecto ver IEEE floating-point standard.

Tipos enumerados (enum)


Los tipos enumerados son un mecanismo usado en C y C++ con el objetivo de agrupar de
alguna manera constantes simbólicas. Para definir tipos enumerados se usa la palabra
reservada enum.

Ejemplo 1

enum dias { domingo, lunes, martes, miercoles, jueves, viernes, sabado };

En el ejemplo anterior se define por medio de enum el tipo enumerado dias, en el mismo
ejemplo se debe observar que dentro de la construcción se definen las constantes
simbólicas: domingo, lunes, ... sabado; y que a las mismas el compilador les asignará
respectivamente y por defecto los valores: 0, 1, 2, 3, 4, 5, 6. Es decir, las constantes
mencionadas pueden usarse dentro del programa y este sabrá a que valor hace referencia
cada una de las mismas. Por ejemplo, la instrucción: cout << domingo; desplegará 0 en la
pantalla. El siguiente ejemplo muestra como usar las constantes enumeradas en un ciclo for.

for (int d = domingo; d <= sabado; d++) cout << d;

En el siguiente ejemplo se define por medio de enum el tipo enumerado colores, en el


mismo se debe de observar la elinación del comportamiento por defecto ya que a la primera
constante (gris) se le asigna en forma específica el valor de 7, de tal manera que la siguiente
constante (grisoscuro) tendrá un valor igual a 8 y la constante amarillo será igual a 14.

Ejemplo 2

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

Tipos definidos por el usuario


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

typedef int entero;

Luego, podremos declarar variables, constantes y funciones del tipo entero. Por ejemplo,

entero edad = 33;

Un uso más útil y común es el empleo de typedef para definir datos estructurados. Por
ejemplo, supongamos que deseamos definir un tipo estructurado llamado persona y que
contenga nombre, edad y sexo. Entonces podemos definir persona como:

typedef struct persona {


char nombre[32];
int edad;
char sexo;
};

Una vez que un tipo ya se ha definido, el mismo puede emplearse para declarar variables y
constantes de este. Por ejemplo, con la instrucción:

persona hombre;

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

hombre.edad

Variables y constantes
Una variable, como su nombre lo indica, es un determinado objeto cuyo valor puede
cambiar durante el proceso de una tarea específica. Contrario a una variable, una constante
es un determinado objeto cuyo valor no puede ser alterado durante el proceso de una tarea
específica. En C, C++ para declarar variables no existe una palabra especial, es decir, las
variables se declarán escribiendo el tipo seguido de uno o más identificadores o nombres de
variables. Por otro lado, para declarar constantes existe la palabra reservada const, así como
la directiva #define. A continuación se muestran ejemplos de declaración de variables y
constantes.

Variables Constantes Constantes

#define a
int a; const int a = 100;
100

const float b = #define b


float b;
100; 100

Notas:

A diferencia de las constantes declaradas con la palabra const los símbolos definidos con
#define no ocupan espacio en la memoria del código ejecutable resultante.

El tipo de la variable o constante puede ser cualquiera de los listados en Tipos primitivos, o
bien de un tipo definido por el usuario.
Las constantes son usadas a menudo con un doble propósito, el primero es con el fin de
hacer más legible el código del programa, es decir, si se tiene (por ejemplo) la constante
numerica 3.1416 y esta representa al número pi, entonces podemos hacer declaraciones
tales como:

#define pi 3.1416

En este caso podremos usar la palabra pi en cualquier parte del


programa y el compilador se encargará de cambiar dicho simbolo por
3.1416.

o bien,

const pi = 3.1416;

En este otro caso podremos usar la palabra pi en cualquier parte del


programa y el compilador se encargará
de cambiar dicho símbolo por una referencia a la constante pi guardada en
la memoria.

Sentencias de decisión

Estructura de control IF

DEFINICIÓN

Las sentencias de decisión o también llamadas de CONTROL DE FLUJO son estructuras


de control que realizan una pregunta la cual retorna verdadero o falso (evalúa una
condicion) y selecciona la siguiente instrucción a ejecutar dependiendo la respuesta o
resultado.
En algún momento dentro de nuestros algoritmos, es preciso cambiar el flujo de ejecución
de las instrucciones, es decir, el orden en que las instrucciones son ejecutadas. Muchas de
las veces tenemos que tomar una decisión en cuanto a que se debe ejecutar basándonos en
una respuesta de verdadero o falso (condicion).
La ejecución de las instrucciones incluyendo una estructura de control como el condicional
funcionan de esta manera:

 Las instrucciones comienzan a ejecutarse de forma secuencial (en orden) y cuando


se llega a una estructura condicional, la cual esta asociada a una condicion, se
decide que camino tomar dependiendo siempre del resultado de la condicion siendo
esta falsa o verdadera.

 Cuando se termina de ejecutar este bloque de instrucciones se reanuda la ejecución


en la instrucción siguiente a la de la condicional.

Sentencia if

La instrucción if es, por excelencia, la más utilizada para construir estructuras de control de
flujo.

SINTAXIS

Primera Forma

Ahora bién, la sintaxis utilizada en la programación de C++ es la siguiente:

if (condicion)
{
Set de instrucciones
}

siendo "condicion" el lugar donde se pondrá la condición que se tiene que cumplir para que
sea verdadera la sentencia y así proceder a realizar el "set de instrucciones" o código
contenido dentro de la sentencia.

Segunda Forma
Ahora veremos la misma sintaxis pero ahora le añadiremos la parte "Falsa" de la sentencia:

if (condicion)
{
Set de instrucciones //PARTE VERDADERA
}
else
{
Set de instrucciones 2 //Parte FALSA
}

La forma mostrada anteriormente muestra la union de la parte "VERDADERA" con la


nueva secuencia la cual es la parte "FALSA" de la sentencia de decision "IF" en la cual esta
compuesta por el:

else
{
Set de instrucciones 2 //Parte FALSA
}

la palabra "else" o "De lo contrario" indica al lenguaje que de lo contrario al no ser


verdadera o no se cumpla la parte verdadera entonces realizara el "set de instrucciones 2".

EJEMPLOS DE SENTENCIAS IF...

Ejemplo 1:

if(numero == 0) //La condicion indica que tiene que ser igual a Cero
{
cout<<"El Numero Ingresado es Igual a Cero";
}

Ejemplo 2:

if(numero > 0) // la condicion indica que tiene que ser mayor a Cero
{
cout<<"El Numero Ingresado es Mayor a Cero";
}

Ejemplo 3:

if(numero < 0) // la condicion indica que tiene que ser menor a Cero
{
cout<<"El Numero Ingresado es Menor a Cero";
}
Ahora uniremos todos estos ejemplos para formar un solo programa mediante la utilización
de la sentencia "Else" e introduciremos el hecho de que se puede escribir en este espacio
una sentencia if ya que podemos ingresar cualquier tipo de código dentro de la sentencia
escrita después de un Else.

Ejemplo 4:

if(numero == 0) //La condicion indica que tiene que ser igual a Cero
{
cout<<"El Numero Ingresado es Igual a Cero";
}
else
{
if(numero > 0) // la condicion indica que tiene que ser mayor a
Cero
{
cout<<"El Numero Ingresado es Mayor a Cero";
}
else
{
if(numero < 0) // la condicion indica que tiene que ser menor a
Cero
{
cout<<"El Numero Ingresado es Menor a Cero";
}
}
}

Sentencia switch

switch es otra de las instrucciones que permiten la construcción de estructuras de control. A


diferencia de if, para controlar el flujo por medio de una sentencia switch se debe de
combinar con el uso de las sentencias case y break.

Notas: cualquier número de casos a evaluar por switch así como la sentencia default son
opcionales. La sentencia switch es muy útil en los casos de presentación de menus.

Sintaxis:

switch (condición)
{
case primer_caso:
bloque de instrucciones 1
break;

case segundo_caso:
bloque de instrucciones 2
break;

case caso_n:
bloque de instrucciones n
break;

default: bloque de instrucciones por defecto


}

Ejemplo 1

switch (numero)
{
case 0: cout << "numero es cero";
}

Ejemplo 2

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

Ejemplo 3

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

Operador condicional ternario ?:

En C/C++, existe el operador condicional ( ?: ) el cual es conocido por su estructura como


ternario. El comportamiento de dicho operador es el mismo que una estructura if - then -
else del lenguaje BASIC (y de la función IIf de Visual Basic). El operador condicional ?:
es útil para evaluar situaciones tales como:

Si se cumple tal condición entonces haz esto, de lo contrario haz esto otro.

Sintaxis:

( (condicion) ? proceso1 : proceso2 )

En donde, condicion es la expresión que se evalua, proceso1 es la tarea a realizar en el


caso de que la evaluación resulte verdadera, y proceso2 es la tarea a realizar en el caso de
que la evaluación resulte falsa.

Ejemplo 1
int edad;
cout << "Cual es tu edad: ";
cin >> edad;
cout << ( (edad < 18) ? "Eres joven aun" : "Ya tienes la mayoría de edad"
);

El ejemplo anterior podría escribirse de la siguiente manera:

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

Ejemplo 2

Vamos a suponer que deseamos escribir una función que opere sobre dos valores numéricos
y que la misma ha de regresar 1 (true) en caso de que el primer valor pasado sea igual al
segundo valor; en caso contrario la función debe retornar 0 (false).

int es_igual( int a, int b)


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

Sentencias de iteración
DEFINICIÓN

Las Sentencias de Iteración o Ciclos son estructuras de control que repiten la ejecución de
un grupo de instrucciones. Básicamente, una sentencia de iteración es una estructura de
control condicional, ya que dentro de la misma se repite la ejecución de una o más
instrucciones mientras que una a condición especifica se cumpla. Muchas veces tenemos
que repetir un número definido o indefinido de veces un grupo de instrucciones por lo que
en estos casos utilizamos este tipo de sentencias. en C++ los ciclos o bucles se construyen
por medio de las sentencias for, while y do - while. La sentencia for es útil para los casos
en donde se conoce de antemano el número de veces que una o más sentencias han de
repetirse. Por otro lado, la sentencia while es útil en aquellos casos en donde no se conoce
de antemano el número de veces que una o más sentencias se tienen que repetir.

Sentencias For
for(contador; final; incremento)
{
Codigo a Repetir;
}

donde:
1. contador es una variable numérica
2. final es la condición que se evalua para finalizar el ciclo (puede ser independiente
del contador)
3. incremento es el valor que se suma o resta al contador

Hay que tener en cuenta que el "for" evalua la condición de finalización igual que el while,
es decir, mientras esta se cumpla continuaran las repeticiones.

Ejemplo 1:

for(int i=1; i<=10; i++)


{
cout<<"Hola Mundo";
}

Esto indica que el contador "i" inicia desde 1 y continuará iterando mientras i sea menor o
igual a 10 ( en este caso llegará hasta 10) e "i++" realiza la sumatoria por unidad lo que
hace que el for y el contador se sumen. repitiendo 10 veces "HOLA MUNDO" en pantalla.

Ejemplo 2:

for(int i=10; i>=0; i--)


{
cout<<"Hola Mundo";
}

Este ejemplo hace lo mismo que el primero, salvo que el contador se inicializa a 10 en lugar
de 1; y por ello cambia la condición que se evalua así como que el contador se decrementa
en lugar de ser incrementado.

La condición también puede ser independiente del contador:

Ejemplo 3:

int j = 20;
for(int i=0; j>0; i++){
cout<<"Hola"<<i<<" - "<<j<<endl;
j--;
}

En este ejemplo las iteraciones continuaran mientras j sea mayor que 0, sin tener en cuenta
el valor que pueda tener i.

Sentencia while
while(condicion)
{
código a Repetir
}
donde:

1. condicion es la expresión a evaluar

Ejemplo 1:

int contador = 0;

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

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

Sentencia do - while

La sentencia do es usada generalmente en cooperación con while para garantizar que una o
más instrucciones se ejecuten al menos una vez. Por ejemplo, en la siguiente construcción
no se ejecuta nada dentro del ciclo while, el hecho es que el contador inicialmente vale
cero y la condición para que se ejecute lo que está dentro del while es "mientras el contador
sea mayor que diez". Es evidente que a la primera evaluación hecha por while la condición
deja de cumplirse.

int contador = 0;

while(contador > 10)


{
contador ++;
cout<<"Hola Mundo";
}

Al modificar el segmento de código anterior usando do tenemos:

int contador = 0;

do
{
contador ++;
cout<<"Hola Mundo";
}
while(contador > 10);
Observe cómo en el caso de do la condición es evaluada al final en lugar de al principio del
bloque de instrucciones y, por lo tanto, el código que le sigue al do se ejecuta al menos la
primera vez.

Sentencias break y continue


En la sección (Sentencia switch) vimos que la sentencia break es utilizada con el
propósito de forzar un salto dentro del bloque switch hacia el final del mismo. En esta
sección volveremos a ver el uso de break, salvo que esta ocasión la usaremos junto con las
sentecias for y la sentencia while. Además, veremos el uso de la sentencia continue.

break

La sentencia break se usa para forzar un salto hacia el final de un ciclo controlado por for o
por while.

Ejemplo:

En el siguiente fragmento de código la sentencia break cierra el ciclo for cuando la variable
( i ) es igual a 5. La salida para el mismo será:

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

continue

La sentencia continue se usa para ignorar una iteración dentro de un ciclo controlado por
for o por while.

Ejemplo:

En el siguiente fragmento de código la sentencia continue ignora la iteración cuando la


variable ( i ) es igual a 5. La salida para el mismo será:

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

Uso de break y continue junto con while

Los dos ejemplos anteriores se presentan en seguida, salvo que en lugar de for se hace uso
de while.
Nota: no deje de observar que la construcción del ciclo while para el caso de la sentencia
continue es diferente, esto para garantizar que el ciclo no vaya a caer en una iteración
infinita.

break

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

continue

int i = -1;
while (i<10) {
i++;
if (i == 5) continue;
cout << i << " ";
}
Estructuras de datos

Las estructuras de datos se emplean con el objetivo principal de organizar los datos
contenidos dentro de la memoria del ordenador. Así, nuestra primera experiencia con
estructuras comienza desde el momento mismo en que usamos en nuestros programas
variables de tipos primitivos (char, short, int, float, etc). A la memoria del ordenador se le
puede considerar como un gran bloque compuesto por una serie de BYTES dispuestos
secuencialmente uno detrás de otro. por ejemplo, si un ordenador posee una memoria de
128MB (128 megas) entonces se le puede leer o escribir desde el BYTE 0 hasta el BYTE
128MB - 1 ( 0000000H .. 7FFFFFFH ).

La idea de ver la memoria como un serie de bytes es buena, sin embargo no es suficiente ya
que en la misma podemos guardar números, cadenas de caracteres, funciones, objetos, etc.
de tal manera que surge la necesidad de establecer los mecanismos adecuados para dar
cuenta de la forma, tamaño y objetivo de los datos almacenados. Según el tipo de
microprocesador, estos tienen la capacidad para manipular o direccionar estructuras
compuestas por uno, dos, cuatro, etc, bytes; de donde se derivan los tipos que comunmente
se conocen como: BYTE, WORD, DWORD, QWORD y TWORD.

La estructura mínima de información manipulable en un sistema de computación es el BIT


el cual se agrupa normalmente en bloques de 8 para formar un BYTE. Cabe mencionar que
los BITS no son direccionables directamente, sino a través de compuertas AND, OR, NOT,
XOR, las cuales en C y C++ se escriben como &, |, ~ y ^, conocidos como "Bitwise
operators" u "Operadores de manipulación de bits".

En C,C++ existe una serie de estructuras básicas o tipos primitivos, los cuales pueden ser
usados por el programador para declarar variables, y también son el fundamento sobre el
cual se crean estructuras complejas. El tamaño de los tipos primitivos no es estándar ya que
los mismos dependen de factores tales como:

 Tipo del microprocesador


 El compilador

Sin embargo, en la actualidad, la mayoria de compiladores de C y C++ soportan los


siguientes tipos con la longitud indicada:

Estructuras básicas en C, C++


Tipos primitivos

Nombre común Nombre C Longitud Procesador 64 bits


BYTE char 8 bits 8 bits

WORD short 16 bits 16 bits

DWORD int 32 bits 32 bits

DWORD long 32 bits 64 bits

DWORD float 32 bits 32 bits

QWORD double 64 bits 64 bits

TWORD long double 80 bits 128 bits

Nota: en el lenguaje C,C++ existe el operador sizeof(), con el cual se puede obtener el
tamaño (número de bytes) ocupados por un tipo específico. Por ejemplo, sizeof(int) regresa
el número de bytes ocupados por los datos de tipo int.

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

tipo id1 [, id2 ...] ;

donde, tipo se refiere a uno de los tipos mostrados en la tabla anterior; id1 es el nombre con
el cual se identificará a la variable. Observe que si se quiere declarar más de una variable en
una línea de instrucción, las mismas deben separarse por medio de una coma.

Ejemplos:

char c; // La variable c es una variable char


int i; // La variable i es una variable int
float f; // La variable f es una variable float
int x,y,z; // Las variables x,y,z son todas variables int, y declaradas a
la vez

De acuerdo con la tabla anterior y según las instrucciones anteriores, con la primera, o sea (
char c;), se le está indicando al ordenador que reserve en la memoria un espacio de tipo
char (8 bits) y que el mismo será identificado bajo el nombre de c. La segunda instrucción (
int i;) le indica al ordenador que reserve en la memoria un espacio de tipo int (16 bits) y que
el mismo será identificado bajo el nombre de i. Luego, la instrucción ( float f;) le indica al
ordenador que reserve en la memoria un espacio de tipo float (32 bits) y que el mismo será
identificado bajo el nombre de f. Por último, se le indica al compilador que reserve espacio
para otras tres variables enteras identificadas como: x, y, z. Así, se puede dar cuenta cómo
los tipos primitivos sirven con el propósito de estructurar los datos dentro de la memoria y
con la idea de referirnos a los mismos mediante nombres usamos identificadores de
variables.

Matrices o Arreglos
Una Matriz (en inglés, array, también denominado arreglo) es una
estructura usada para agrupar bajo un mismo nombre
listas de datos de un mismo tipo.

El tipo de matriz puede ser cualquiera, sin embargo cada componente tiene que ser del
mismo tipo. En C estándar solamente da soporte para matrices estáticas, mientras que con
C++ se pueden crear matrices dinámicas pudiendo usar la librería estándar de plantillas
(STL).

Matrices estáticas

Una matriz estática es una estructura cuyo tamaño es determinado en tiempo de


compilación, es decir, una vez establecido el tamaño de la matriz ésta no podrá cambiarse
durante el tiempo de ejecución. En C, C++ para declarar un arreglo estático de datos se
emplea la sintaxis:

tipo identificador[ [tamaño] ] [ = { lista de inicialización } ] ;

donde,

 tipo se refiere al tipo de datos que contendrá la matriz. El tipo puede ser cualquiera de los
tipos estándar (char, int, float, etc.) o un tipo definido por el usuario. Es más, el tipo de la
matriz puede ser de una estructura creada con: struct, union y class.

 identificador se refiere al nombre que se le dará a la matriz.

 tamaño es opcional e indica el número de elementos que contendrá la matriz. Si una


matriz se declara sin tamaño, la misma no podrá contener elemento alguno a menos que
en la declaración se emplee una lista de inicialización.

 lista de inicialización es opcional y se usa para establecer valores para cada uno de los
componentes de la matriz. Si la matriz es declarada con un tamaño especifíco, el número
de valores inicializados no podrá ser mayor a dicho tamaño.

Ejemplos:

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

Acceso a los miembros de una matriz de datos:

En orden de acceder a los miembros de una matriz se debe indicar el nombre de la matriz
seguido de dos corchetes, dentro de los cuales se debe especificar el índice del elemento
deseado. Se debe aclarar que los índices son números o expresiones enteras y que en C,
C++ estos tienen un rango permitido de 0 a T-1 ( T = tamaño de la matriz ).

Ejemplos: dadas las matrices intA, charA, longA ( ejemplo anterior )

intA[0] = 100; // establece el valor del elemento 0 de intA a 100.

charA[3] = 'O'; // establece el valor del elemento 3 de charA a 'O'.

cout << longA[0]; // muestra por pantalla el elemento 0 de longA, que es


longA[0].

Matrices dinámicas

Una matriz dinámica es una estructura compleja y, ya que C estándar no da el soporte para
operar con estos tipos de estructuras, le corresponde al programador crear los algoritmos
necesarios para su implementación. Crear lista dinámicas de datos en C estándar no es una
tarea para programadores inexpertos, ya que para lograr tal objetivo se necesita tener
conocimentos solidos acerca de los punteros y el comportamiento de los mismos. Los
usuarios de C++ pueden auxiliarse de la librería estándar de plantillas, conocidas por sus
siglas en ingles como STL.

Estructuras compuestas (struct, union, class)


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

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

 union: una union es similar a una struct, salvo que en una estructura creada con union los
campos o variables comparten una dirección de almacenamiento común.

 class: una clase es una estructura en donde se agrupan variables y funciones, la misma es
usada en Programación Orientada al Objeto. Las clases no son soportadas por el C
estándar.

Nota: tanto las estructuras como las uniones y las clases pueden ser
anónimas, pero lo más
recomendable es darle a las mismas un nombre. Si una estructura, union o
clase posee
nombre, esta pueden ser empleadas para declarar variables de la misma y,
lo más importante,
puede ser usada para el paso de parámetros a funciones.

Sintaxis general: struct


struct [ <nombre tipo de estructura > ] {
[ <tipo> <nombre-variable[, nombre-variable, ...]> ] ;
[ <tipo> <nombre-variable[, nombre-variable, ...]> ] ;
...
} [ <variables de estructura> ] ;

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

struct: Sintaxis ( variación uno, estructura anónima )

De acuerdo con la sintaxis general de la orden struct es posible crear estructuras de datos
anónimas. Solamente hay que tener en cuenta que en una declaración anónima se debe
definir al menos una variable al final de la declaración. Por ejemplo, con el siguiente
fragmento de código:

struct { int a, b; } p1;

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

struct { int a, b; } p2;

Por supuesto, en una misma línea de instrucción podemos definir más de una variable.
Ejemplo:

struct { int a, b; } p1, p2;

Entonces, para crear nuevos tipos con struct deberemos de modificar la sintaxis mostrada
en los ejemplos anteriores.

Sintaxis: struct ( variación dos, estructura con nombre )


Observe que, la sintaxis para declarar estructuras con nombre es bastante parecida a la
sintaxis para declarar estructuras anónimas; salvo que en una declaración de estructura con
nombre se debe especificar el nombre deseado para la misma. Además, en una declaración
de estructura con nombre la o las variables definidas al final de la misma son opcionales.

Ejemplos:

struct pareja { int a, b; } p1;

En el fragmento de código anterior se declara la estructura identificada como pareja, misma


que se compone de los miembros a y b, ambos de tipo int. En el mismo ejemplo, se define
la variable p1; la cual es una variable estructurada de tipo pareja.

Una vez que una estructura con nombre ha sido creada, la misma puede ser usada para
declarar cualquier número de variables. Por ejemplo, en el siguiente fragmento de código se
crea la estructura tiempo compuesta por los miembros hora, minuto y segundo; todos del
tipo int. En el mismo ejemplo, se declaran las variables t1 y t2.

/* declaración de estructura tiempo */


struct tiempo { int hora, minuto, segundo; };

/* declaración de variables de tipo tiempo */


struct tiempo t1, t2;

Nota: en C++ puede obviarse la palabra struct a la hora de declarar variables. Así, en C++
la línea de instrución struct tiempo t1, t2; ( del ejemplo anterior) puede escibirse como:
tiempo t1, t2;

Acceso a los miembros de una estructura

En orden de poder leer o escribir uno de los miembros de una variable estructurada, se debe
usar el operador de acceso ( . ); o sea, el nombre de la variable seguida por un punto
seguido por el nombre del miembro o componente deseado de la estructura. Por ejemplo,
para acceder a los miembros de la variable t1 (mostrada arriba) podemos hacerlo de la
siguiente manera:

t1.hora = 12;
t1.minuto = 0;
t1.segundo = 0;

printf ("%i\n", t1.hora);


cout << t1.minuto << endl;

Estructuras anidadas
Los miembros de una estructura pueden ser ellos mismos, otra estructura previamente
identificada, o bien una estructura anónima. Por ejemplo, en el siguiente fragmento de
código, se crean las estructuras pareja y pareja2. Obsérvese cómo dentro de los miembros
de pareja2, se declara el miembro X, que a su vez es una estructura del tipo pareja. Luego,
las variables declaradas a raíz de la estructura pareja2 poseerán los miembros variables a y
b heredados de pareja, y c.

struct pareja { int a, b ; };


struct pareja2 { struct pareja X; int c; } P3;

Ahora bien, para acceder a los miembros de una estructura dentro de otra estructura se
emplea el mismo mecanismo de acceso (el punto). Por ejemplo, para desplegar el miembro
a de la variable P3 declarada en el ejemplo anterior, lo haremos más o menos así:

printf( "%i\n", P3.X.a );

Herencia

El término herencia se usa con gran frecuencia en Programación Orientada a Objetos, y se


le relaciona principalmente con las clases. Sin embargo, la herencia está presente siempre y
cuando una estructura "struct", "union" o "class" posea a otra estructura. En ese sentido, en
C++ se presentan dos tipos de herencia:

 herencia por agregación o composición.


 herencia por extensión.

Por ejemplo, en la definición de las estructuras pareja y pareja2 del ejemplo anterior, se
dice que pareja2 hereda por composición todos los miembros de pareja. Ahora, en el
siguiente ejemplo se usa la sintaxis para que la estructura pareja2 herede por extensión los
miembros de pareja:

// solo C++
struct pareja { int a, b ; };
struct pareja2 : pareja { int c; } P3;

Con esta forma de herencia, la estructura pareja2 hereda de pareja los miembros a y b, y
además agrega un miembro c. Y a diferencia del ejemplo anterior, para acceder a alguno de
sus miembros heredados, basta con utilizar el mecanismo de acceso (el punto).

// solo C++
cout << P3.a << P3.b ;

Estructura de campos de bits


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

Antes de ver un ejemplo del uso de struct para crear estructuras de campos de bits,
consideremos el caso en donde se tiene una variable del tipo short (16 bits) y que para la
misma se desea que los bits tengan significados específicos. Digamos que el primer bit
servirá para controlar alguna condición; los siguientes cuatro bits, o sea del segundo al
quinto bit, controlarán otra condición; el bit 6 tendrá otra función; y el resto, o sea del
séptimo al decimosexto bit se emplearán para controlar otra condición. De tal manera que si
queremos, por ejemplo, saber si el primer bit de la variable tiene almacenado un 1 o un 0,
podemos emplear la siguiente sintaxis:

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

la cosa parece sencilla, pero ahora consideremos el caso en el cual deseamos saber cual es
el valor contenido por el grupo de bits ( segundo al quinto ), entonces nos daremos cuenta
que no basta con una prueba mediante AND ( X & 1 ), sino que hay que realizar otros
pasos.

Precisamente, para problemas como el planteado arriba es por los que los lenguajes C y
C++ soportan las estructuras de campos de bits. Por ejemplo, la estructura

struct campo_de_bit {
int bit_1 : 1;
int bits_2_a_5 : 4;
int bit_6 : 1;
int bits_7_a_16 : 10;
} bit_var;

corresponde a la siguiente colección de campos bits:

El mecanismo de estructuras de campos de bits soportado por C,C++ es una herramienta


útil y poderosa, sobre todo en programación de bajo nivel; ya que mediante el mismo es
posible aislar y dar nombres a todos y cada uno de los bits de un dato, y también crear en
un mismo campo grupos de bits (como se mostró arriba). Ahora bien, no hay que olvidar
que la estructura mínima de información en un sistema de memoria de un PC es el bit, y
que para aislar a cada uno de estos se puede emplear el operador AND ( &, en C ), pero
mediante el mecanismo mencionado el compilador genera los algoritmos necesarios para
llevar a cabo el aislamiento de los bits. Por ejemplo, para escribir y leer bit identificado
como bit_1 de la variable bit_var del ejemplo anterior, podemos emplear las siguientes
instrucciones:

bit_var.bit_1 = 1;
printf("%i\n", bit_var.bit_1 );

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

union: Sintaxis general


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

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

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


los miembros de una struct.

 Los miembros de una union comparten un espacio de almacenamiento común.

 En una union, el compilador reserva el espacio de almacenamiento para la misma de


acuerdo con el tipo de la variable de mayor tamaño.

Ejemplo: union anónima


union { short a; long b; } u1;

En el ejemplo anterior se declara la variable u1, la cual es una estructura tipo union. El
espacio de almacenamiento para la variable a es compartido por la variable b, en
consecuencia, al escribir sobre cualquiera de estas dos variables se altera el contenido de
ambas.

Ejemplo: union con nombre

union ux { short a; long b; } u1;

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

union ux u2, u3;

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

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


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

Nota: Dentro de una clase,

 los datos son llamados "datos miembros"

 las funciones son llamadas "funciones miembros".

 El mecanismo para acceder a los miembros de una class es igual que aquel utilizado para
acceder a los miembros de una struct
Las clases son algo así como "super" estructuras capaces de agrupar no solo datos
miembros sino también funciones miembros. En el lenguaje común a los datos miembros
de una clase se les conoce como atributos; mientras que a las funciones miembros de una
clase se les llama métodos. Normalmente, los métodos se emplean para leer o escribir los
atributos. Es decir, la norma general es no permitir el acceso directo a los atributos de una
clase, con la idea de aumentar la seguridad de los datos.

En seguida se mostrará el código para crear la clase Pareja, misma que poseerá los atributos
a y b, y los métodos setA(), setB(); getA(), getB(), y mostrar();

class Pareja {
int a, b;
public:
void setA(int n) { a = n; }
void setB(int n) { b = n; }
int getA() { return a; }
int getB() { return b; }
void mostrar() {
cout << "a = " << a << "; b = " << b << endl;
}
} p1;

Nota: por omisión, los miembros de una clase son privados, lo cual significa que los
objetos instanciados de dicha clase no tendrán acceso a los mismos. Así, en el ejemplo
anterior se está creando la clase Pareja, y al mismo tiempo el objeto p1. Luego, para leer o
escribir los atributos de p1 se debe hacer a traves de los métodos definidos con tal objetivo.
Por ejemplo, con el siguiente fragmento de código se establecen respectivamente a 100 y a
200 los atributos a y b; y posteriormente se despliegan por medio del método mostrar().

p1.setA(100);
p1.setB(200);
p1.mostrar();

struct vs. class


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

 Los miembros de una struct son públicos por defecto, mientras que los miembros de una
class son privados por defecto.
 Los parámetros-argumentos struct se pasan normalmente por copia, los parámetros-
argumentos class se pasan normalmente por referencia.
// Este programa ha sido probado en Dev-C++, Borland C++ y Code::Blocks

#include <iostream>

using namespace std;

// estructura tipo clase base


struct Par {

int a, b;

// constructor base
Par() { a = b = 0; }

// destructor base
~Par() { cout << "hecho..." << endl; }

void setA(int n) { a = n; }
void setB(int n) { b = n; }

void mostrar() {
cout << "a = " << a << ", b = " << b << "; suma = " << a+b <<
endl;
}
};

// estructura tipo clase hija


// ParHijo es una extensión de Par, y por lo tanto hereda los miembros de
Par
struct ParHijo : Par {

// constructor del hijo


ParHijo(int a, int b) {
this->a = a;
this->b = b;
}
};

// prueba
void test00() {
ParHijo p1(100, 200); // p1 es instancia de ParHijo
p1.mostrar(); // se envía mensaje al método mostrar() de p1
}

// funcion principal
int main()
{
test00();
cin.get();
return 0;
}
Funciones

Definiendo una función

Una función es un conjunto de líneas de código que realizan una tarea específica y puede
retornar un valor. Las funciones pueden tomar parámetros que modifiquen su
funcionamiento. Las funciones son utilizadas para descomponer grandes problemas en
tareas simples y para implementar operaciones que son comúnmente utilizadas durante un
programa y de esta manera reducir la cantidad de código. Cuando una función es invocada
se le pasa el control a la misma, una vez que esta finalizó con su tarea el control es devuelto
al punto desde el cual la función fue llamada.

<tipo> [clase::] <nombre> ( [Parámetros] ).


{
cuerpo;
}

Ejemplo de una función

Para comenzar, vamos a considerar el caso en el cual se desea crear la función cuadrado(),
que deberá devolver el cuadrado de un número real (de punto flotante), es decir, cuadrado()
aceptará números de punto flotante y regresará una respuesta como número flotante.

Nota: aunque para la función que veremos el tipo de retorno coincide con el tipo de
parámetro pasado, algunas veces las cosas pueden cambiar, es decir, no es obligatorio que
una función reciba un parámetro de un tipo y que tenga que regresar una respuesta de dicho
tipo.
// regresar el cuadrado de un número
double Cuadrado(double n)
{
return n*n;
}

Parámetros
Normalmente, las funciones operan sobre ciertos valores pasados a las mismas ya sea como
constantes literales o como variables, aunque se pueden definir funciones que reciban
parámetros. Existen dos formas en C++ de pasar parámetros a una función; por referencia o
por valor. El hecho es que si en una declaración de función se declaran parámetros por
referencia, a los mismos no se les podrá pasar valores literales ya que las referencias
apuntan a objetos (variables o funciones) residentes en la memoria; por otro lado, si un
parámetro es declarado para ser pasado por valor, el mismo puede pasarse como una
constante literal o como una variable. Los parámetros pasados por referencia pueden ser
alterados por la función que los reciba, mientras que los parámetros pasados por valor o
copia no pueden ser alterados por la función que los recibe, es decir, la función puede
manipular a su antojo al parámetro, pero ningún cambio hecho sobre este se reflejará en el
parámetro original.

Parámetros por valor

La función cuadrado() (ver arriba) es un clásico ejemplo que muestra el paso de


parámetros por valor, en ese sentido la función cuadrado() recibe una copia del parámetro
n. En la misma función se puede observar que se realiza un calculo ( n*n ), sin embargo el
parámetro original no sufrirá cambio alguno, esto seguirá siendo cierto aún cuando dentro
de la función hubiera una instrucción parecida a n = n * n; o n*=n;.

Parámetros por referencia

Para mostrar un ejemplo del paso de parámetros por referencia, vamos a retomar el caso de
la función cuadrado, salvo que en esta ocasión cambiaremos ligeramente la sintaxis para
definir la misma. Veamos:

// regresar el cuadrado de un número


double cuadrado2(double &n)
{
n *= n;
return n;
}

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

Llamar a una función


para llamar a la función cuadrado() vista anteriormente, podemos emplear:

cout << cuadrado(25);


cout << cuadrado(X);
R = cuadrado(X); // guardar en R el cuadrado de X

Funciones void
Bajo ciertas circunstancias se deseará escribir funciones que no regresen valor alguno (esto
sería algo parecido a escribir procedures en Pascal) y para ello podemos declarar a la
función como void. La palabra reservada void es utilizada para declarar funciones sin valor
de retorno y también para indicar que una función específica no requiere de parámetros. Por
ejemplo, la función pausa() que se verá en seguida, no devolverá valor alguno y la misma
no requiere de parámetros.

// esta función requiere de la librería iostream


void pausa(void)

== Funciones anidadas ==
A diferencia de '''Pascal''', el lenguaje C, C++ no permite anidar
funciones, sin embargo, dentro de una funcíon puede existir la llamada a
una o más funciones declaradas previamente, que determinara en cierto
punto un resultado para que sea ejecutado cuando el programador lo desee.

== Funciones de tipo puntero (*) ==


En muchas ocasiones se desea que ciertas funciones regresen una
referencia o puntero hacia un tipo (sea este estructurado o no)
específico de dato en lugar de un valor específico. En tales casos, la
función se deberá declarar como para que regrese un puntero. Por ejemplo,
supongamos que deseamos crear una función para convertir un número entero
en notación decimal a una cadena de caracteres en forma de números
binarios, luego, la función mencionada podría escribirse para que reciba
el número entero como parámetro y regrese un puntero a una cadena de
caracteres conteniendo la conversión. Para ser más puntuales, vamos a
escribir un programa en donde se verá la función '''binstr()''', y cuyo
objetivo será precisamente convertir números decimales en cadenas
binarias.
<div style="background:#FFFFBB; padding:16px; border-color:#0000FF;
border-style:"solid">
Nota: observe que en la sintaxis para declarar funciones tipo puntero se
debe de poner el símbolo '''*''' despues del tipo y antes del nombre de
la función que se está declarando. Esto se puede ver en el programa, ya
que la función binstr se declara como: '''char *binstr(unsigned
int);'''</div>

<source lang="cpp">
#include <iostream>
#include <string.h>

using namespace std;

// declaración de prototipo
char *binstr(unsigned int);
// punto de prueba
int main()
{
int n = 128;
cout << "decimal = " << n << ", binario = " << binstr(n) << endl;
cin.get();
}

// definición de función binstr()


// nota: esta funcion requiere de la librería estándar string
char *binstr(unsigned int n)
{
static char buffer[65];
int i = 0;

strcpy(buffer, "0");

if (n > 0) {
while (n > 0) {
buffer[i] = ( n & 1 ) + '0';
i++;
n >>= 1;
}
buffer[i] = '\0';
strrev(buffer);
} // fin (n > 0)
return buffer;
}

Variables estáticas y automáticas


Dentro de una función, las variables y/o constantes pueden ser declaradas como: auto (por
omisión) o como static. Si una variable dentro de una función es declarada como estática
significa que la misma retendrá su valor entre las llamadas a la función. Por otro lado, la
variables automáticas pierden su valor entre las llamadas. En el programa anterior puede
verse que el arreglo de caracteres (buffer[65]) se ha declarado como estático para garantizar
que dicho buffer retenga los datos aún despues de que la función termine. En el mismo
ejemplo, si el buffer no se declara como estático, el contenido del mismo podría ser
destruido al salir de la función y los resultados obtenidos podrían ser no deseados.

Parámetros constantes
Los parámetros usados por una función pueden declararse como constantes ( const ) al
momento de la declaración de la función. Un parámetro que ha sido declarado como
constante significa que la función no podrá cambiar el valor del mismo ( sin importar si
dicho parámetro se recibe por valor o por referencia ).

Ejemplo:

int funcionX( const int n );


void printstr( const char *str );
Parámetros con valor por defecto
Los parámetros usados por una función pueden declararse con un valor por defecto. Un
parámetro que ha sido declarado con valor por defecto es opcional a la hora de hacer la
llamada a la función. Ejemplo: Dada la función:

void saludo( char* mensaje = "Hola Copa America 2015" );

la misma puede ser invocada como:

saludo(); // sin parámetro


saludo("Sea usted bienvenido a C++"); // con parámetro

Para ver un ejemplo más, vamos a considerar el caso de la función binstr() del programa
funciones01. Ahora, vamos modificar dicha función, salvo que esta ocasión nos interesa
que la misma sirva para convertir números decimales en cadenas numéricas y cuya base de
conversión sea pasada como parámetro. Es decir, la función de la que estamos hablando
podrá convertir números decimales a: binario, octal, decimal, hexadecimal, etc.; y la única
condición será que la base indicada esté entre el 2 y el 36, inclusive.

Nota: Ya que la función servirá para convertir números a cualquier representación la


nombraremos como numstr() en lugar de binstr(). Si la función es invocada sin el
parámetro base regresará una cadena de digitos decimales.

#include <iostream>
#include <stdlib.h>

using namespace std;

// declaración de prototipo
char *numstr(unsigned int, const int base = 10);

// punto de prueba
int main()
{
int n = 128;
cout << "decimal = " << n << ", binario = " << numstr(n, 2) << endl;
cout << "decimal = " << n << ", octal.. = " << numstr(n, 8) << endl;
cin.get();
}

// definición de función numstr()


// nota: esta funcion requiere de la librería stdlib.h
char *numstr(unsigned int n, const int base)
{
static char buffer[65];
itoa(n, buffer, base);
return buffer;
}

Parámetros de tipo puntero


Anteriormente se mencionó que en C++ los parámetros a una función pueden pasarse por
valor o por referencia, al respecto, podemos agregar que los parámetros también pueden
pasarse como punteros. El paso de parámetros de punteros es bastante parecido al paso de
parámetros por referencia, salvo que el proceso de los datos dentro de la función es
diferente. Por ejemplo, las funciones:

void referencia( int &X ) { X = 100; }


void puntero( int *X ) { *X = 100; }
ambas reciben un puntero o referencia a un objeto de tipo entero, por lo tanto cualquiera de las
funciones del ejemplo puede cambiar el valor de la variable entera apuntada por X, la diferencia
radica en la forma en que cada una de las mismas lleva cabo la tarea. Si en la función puntero() en
lugar de usar *X = 100; se usara X = 100; se le asignaría 100 al puntero X, más no al objeto
apuntado por X, y esto podría ser la causa de que el programa se terminara de manera abrupta.

Parámetros estructurados
Al igual que cualquier otro tipo los parámetros de tipo estruturado pueden pasarse por valor
o por referencia, sin embargo, podría ser que si una estructura es pasada por valor el
compilador mostrara una advertencia ( warning ) indicando que se pasado por valor una
estructura, puesto que el paso de estructuras por valor es permitido usted puede ignorar la
advertencia, pero lo mejor es pasar estructuras por referencia. Si una estructura es pasada
por valor y si esta es muy grande podria ser que se agotara la memoria en el segmento de
pila ( Stack Segment ), aparte de que la llamada a la función sería más lenta.

Para ver un ejemplo, consideremos el caso del siguiente tipo estructurado:

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

Ahora, pensemos que deseamos escribir una función para imprimir variables del tipo
empleado. Así, la función puede escribirse de las tres maneras siguientes:

void ImprimeEmpleadoV( empleado e)


{
cout << "Nombre: " << e.nombre << endl;
cout << "Edad: " << e.edad << endl;
cout << "Sexo: " << e.sexo << endl;

// Parametro empleado pasado por referencia


void ImprimeEmpleadoR( empleado &e )
{
cout << "Nombre: " << e.nombre << endl;
cout << "Edad: " << e.edad << endl;
cout << "Sexo: " << e.sexo << endl;
}

// Parametro empleado pasado como puntero


void ImprimeEmpleadoP( empleado *e )
{
cout << "Nombre: " << e->nombre << endl;
cout << "Edad: " << e->edad << endl;
cout << "Sexo: " << e->sexo << endl;
}

Funciones sobrecargadas
C++, a diferencia del C estándar, permite declarar funciones con el mismo nombre y a esto
se conoce como sobrecarga de funciones. Las funciones sobrecargadas pueden coincidir
en tipo, pero al menos uno de sus parámetros tiene que ser diferente. En todo caso, si usted
trata de declarar funciones sobrecargadas que coincidan en tipo y número de parámetros el
compilador no se lo permitirá. Para poner un ejemplo vamos a considerar el caso de dos
funciones cuyo nombre será divide, ambas regresarán el cociente de dos números, salvo
que una de ellas operará sobre números enteros y la otra lo hará sobre números reales ( de
punto flotante ).

Nota: cuando en los programas se hace una llamada a una función sobrecargada, el
compilador determina a cual de las funciones invocar en base al tipo y número de
parámetros pasados a la función.

#include <iostream.h>
#include <stdlib.h>

using namespace std;

// divide enteros
int divide(int a, int b)
{
cout << "división entera" << endl;
return ( (b != 0) ? a/b : 0);
}

// divide reales
double divide(double a, double b)
{
cout << "división real" << endl;
return ( (b != 0) ? a/b : 0);
}
// punto de prueba
int main()
{
cout << divide(10, 3) << endl;
cout << divide(10.0, 3.0) << endl;
cin.get();
}
Número variable de parámetros
En C,C++ se pueden crear funciones que operen sobre una lista variable de parámetros, es
decir, en donde el número de parámetros es indeterminado. En esta sección se mostrará un
ejemplo de la manera en que podemos crear funciones para manejar tales asuntos, y para
ello haremos uso de tres macros soportadas por C++:

1. va_list puntero de argumentos


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

La sintaxis que usaremos para declarar funciones con lista de parámetros variables es:

1) tipo nombrefuncion(...)
2) tipo nombrefuncion(int num, ...)

donde:

1. tipo es el tipo regresado por la función


2. nombrefuncion es el nombre de la función
3. int num es el número de parámetros que la función procesará
4. ... esta notación se emplea para indicar que el número de parámetros es variable

Nota: observe que la primera forma de declaración es realmente variable el número de


parámetros a procesar y en estos casos se debe establecer el mecanismo para determinar
cuando se ha procesado el último de los argumentos, en el segundo tipo de declaración el
número total de parámetros a procesar es igual al valor del parámetro num.

En el siguiente programa, por ejemplo, se define una función ( printstr ) que despliega una
lista variable de cadenas de caracteres.

#include <iostream.h>
#include <stdarg.h>

// despliega una lista de cadenas, la ultima debe ser NULL


void printstr(...)
{
va_list ap;
char *arg;
va_start(ap, 0);
while ( (arg = va_arg(ap, char*) ) != NULL) {
cout << arg;
}
va_end(ap);
}

int main()
{
printstr("Hola, ", "Esta es\n", "una prueba\n", NULL);
cin.get();
return 0;
}

En el programa que se listará en seguida, se define la función suma(), misma que operará
sobre listas de números enteros, la función devolverá la suma de dichos números.

#include <iostream>//entrada y salida


#include <stdarg.h>

using namespace std;

// Esta función opera sobre una lista variable de números enteros


int suma( int num, ... )
{
int total = 0;
va_list argptr;
va_start( argptr, num );

while( num > 0 ) {


total += va_arg( argptr, int );
num--;
}

va_end( argptr );
return( total );
}

int main()
{
cout << suma(4, 100, 200, 300, 400) << endl;
cin.get();
return 0;
}

o

Streams, entrada y salida de datos

En este capítulo abordaremos el tema de la manipulación de datos a través de los


dispositivos de entrada y salida estándar por medio de ciertos componentes lógicos
conocidos como: streams. A manera de definición, un stream es una especie de canal a
través del cual fluyen los datos. Técnicamente, un stream es el enlace lógico utilizado por el
programador en C, C++ para leer o escribir datos desde y hacia los dispositivos estándar
conectados a la PC. Normalmente, el dispositivo estándar para manipular entradas es el
teclado y este, en C++, está asociado al objeto cin; el dispositivo estándar de salida está
asociado (generalmente) con la pantalla o monitor de despliegue de la PC y, el mismo, en
C++ se puede acceder por medio del objeto cout. El dispositivo estándar para mensajes de
error es el cerr, y el mismo está asociado por defecto con la pantalla o monitor de
despliegue. Otro dispositivo estándar es la impresora, pero este último no es soportado por
los objetos de la iostream. Los programadores que hayan tenido alguna experiencia al
programar en C estándar, notarán que los tres objetos mencionados coinciden con los
dispositivos: stdin, stdout y stderr. La tabla que se muestra en seguida puede servirnos de
base.

Tabla I/O : 01, dispositivos estándar de I/O

C estandar C++

stdin cin

stdout cout

stderr cerr

--- clog

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

Streams automáticos
Si usted usa la directiva #include <iostream.h> o #include <iostream> en sus programas,
automáticamente la iostream pone a su disposición los objetos cin, cout, clog y cerr en el
ámbito estándar (std), de tal manera que usted puede comenzar a enviar o recibir
información a través de los mismos sin siquiera preocuparse de su creación. Asi, un sencillo
ejemplo del uso de los objetos mencionados se muestra en seguida.

// De nuevo con el hola mundo...


#include <iostream.h>
int main()
{
std::cout << "Hola mundo"; // imprimir mensaje (en la pantalla)
std::cin.get(); // lectura ( entrada del teclado )
return 0;
}

Operadores de direccionamiento
Los operadores de direccionamiento son los encargados de manipular el flujo de datos
desde o hacia el dispositivo referenciado por un stream específico. El operador de
direccionamiento para salidas es una pareja de símbolos de "menor que" <<, y el operador
de direccionamiento para entradas es una pareja de símbolos de "mayor que" >>. Los
operadores de direccionamiento se colocan entre dos operandos, el primero es el Stream y
el segundo es una variable o constante que proporciona o recibe los datos de la operación.
Por ejemplo, en el siguiente programa y en la instrucción cout << "Entre su nombre: "; la
constante "Entre su nombre: " es la fuente o quien proporciona los datos para el objeto
cout. Mientras que en la instrucción cin >> nombre la variable nombre es el destino o quien
recibe los datos provenientes del objeto cin.

// De nuevo con el hola mundo...


#include <iostream.h>
int main()
{
char nombre[80];
cout << "Entre su nombre: ";
cin >> nombre;
cout << "Hola," << nombre;
cin.get();
return 0;
}

Observe que si en una misma línea de comando se desea leer o escribir sobre varios campos
a la vez, no es necesario nombrar más de una vez al stream. Ejemplos:

cout << "Hola," << nombre;


cin >> A >> B >> C;

Banderas de I/O
En esta sección abordaremos de manera más directa el tema sobre el control de formato
para los stream de C++. Específicamente, veremos las tres diferentes formas que existen en
C++ para manipular las banderas relacionadas a los stream y que nos permitirán gobernar
de una manera más precisa la forma para representar datos de salida y/o entrada. En ese
sentido, veremos que la primera de las forma que nos permitirá el formateo sera a través de
las funciones flags(), setf() y unsetf(), la segunda y la tercera forma las encontraremos en
ciertos manipuladores directos definidos en las bibliotecas <iostream> y <iomanip>.

Banderas de formato:

C++ define algunas banderas de formato para entradas y salidas estándar, las cuales pueden
ser manipuladas a través de la funciones (métodos) flags(), setf(), y unsetf(). Por ejemplo,

cout.setf(ios::left);

activa la justificación a la izquierda para todas las salidas dirigidas hacia cout.

A continuación se muestra una tabla de referencia de las banderas de I/O.

Tabla I/O : 02, banderas de formato

Bandera Descripción

boolalpha Los valores booleanos pueden ser leídos/escritos usando las palabras "true" y "false"

dec Los valores numéricos se muestran en formato decimal

fixed Números de punto flotante se despliegan en forma normal

hex Los valores numéricos se muestran en formato hexadecimal


left La salida es justificada por la izquierda

oct Los valores numéricos se muestran en formato octal

right La salida es justificada por la derecha

scientific Números de punto flotante se despliegan en notación científica

showbase Despliega la base de todos los valores numéricos

Despliega el punto decimal y extra ceros, aún cuando no sean


showpoint
necesarios
showpos Despliega el símbolo de más antes de valores positivos

Descarta caracteres de espaciado (espacios, tabuladores, nuevas líneas) cuando se


skipws
lee desde un stream

unitbuf Descarga el buffer después de cualquier inserción

Despliega la "e" en notaciones científicas y la "x" en notaciones decimales como


uppercase
letras mayúsculas

Manipulando la lista de banderas de I/O de C++ (mostrada arriba) se pueden controlar los
aspectos relacionados a la forma con la cual se desean presentar los datos en la salida. Por
ejemplo, el programa que se muestra en seguida, activa la bandera boolalpha para mostrar
los resultados de operaciones booleanas como "true" o "false" en lugar de "0" o "1" como
es lo normal.

// Programación con C++


// programa Banderas01.cpp;
// probado en Dev-Cpp Versión 4.9.9.2

#include <iostream>

using namespace std;

int main(int argc, char *argv[])


{
cout <<"\n0 > 1 ? "<<"\t"<<(0>1)<< endl;
cout <<"\n5 > 1 ? "<<"\t"<<(5>1)<< endl;
cout.setf(ios::boolalpha); // activar bandera
cout <<"\n0 > 1 ? "<<"\t"<<(0>1)<< endl;
cout <<"\n5 > 1 ? "<<"\t"<<(5>1)<< endl;

cin.get();
return 0;
}

Manipuladores

Las banderas también pueden manipularse directamente usando los siguientes


manipuladores. Seguramente usted ya estará familiarizado con el manipulador endl, el
mismo puede darle una idea de cómo son usados los manipuladores. Por ejemplo, usted
puede establecer la bandera de números decimales usando el comando:

cout << dec;

La tabla que se muestra en seguida corresponde a los manipuladores definidos en


<iostream>.

Tabla I/O : 03, manipuladores en <iostream>

Manipulador Descripción Entrada Salida

boolalpha Activa la bandera boolalpha X X

dec Activa la bandera dec-imal X X

endl Escribe carácter de cambio de línea --- X

ends Escribe el carácter null --- X

fixed Activa la bandera fixed (para números reales) --- X

flush Descargar el stream --- X

hex Activa la bandera hex-adecimal X X

internal Activa la bandera interna --- X


left Activa la bandera left (izquierda) --- X

noboolalpha Desactiva la bandera boolalpha X X

noshowbase Desactiva la bandera showbase --- X

noshowpoint Desactiva la bandera showpoint --- X

noshowpos Desactiva la bandera showpos --- X

noskipws Desactiva la bandera skipws X ---

nounitbuf Desactiva la bandera unitbuf --- X

nouppercase Desactiva la bandera uppercase --- X

oct Activa la bandera oct-al X X

right Activa la bandera de justificar derecha --- X

scientific Activa la bandera scientific --- X

showbase Activa la bandera showbase --- X

showpoint Activa la bandera showpoint --- X

showpos Activa la bandera showpos --- X

skipws Activa la bandera skipws X ---

unitbuf Activa la bandera unitbuf --- X

uppercase Activa la bandera uppercase --- X

ws Limpiar cualquier espacio al inicio X ---


El programa que se muestra en seguida es un ejemplo de como emplear manipuladores
directos para formatear salidas al stream estándar de salida ( cout ). En el mismo, se emplea
el manipulador boolalpha para que los resultados de la operaciones logicas sean textuales,
o sea, true o false y los manipuladores dec, hex y oct.

// Programación con C++


// programa Banderas02.cpp;
// probado en Dev-Cpp Versión 4.9.9.2

#include <iostream>

using namespace std;

int main(int argc, char *argv[])


{
cout << boolalpha;
cout << "0 > 1 ? " << '\t' << (0 > 1) << endl;
cout << "5 > 1 ? " << '\t' << (5 > 1) << endl;

cout << dec << 2048 << endl;


cout << hex << 2048 << endl;
cout << oct << 2048 << dec << endl;

cin.get();
return 0;
}

Por último, y para terminar esta sección, hablaremos de los manipuladores definidos en la
biblioteca <iomanip>. Estos operan directamente igual que los que se vierón
anteriormente, salvo que son parametrizados, es decir, operan en línea de salida mediante el
operador <<, pero los mismos operan a manera de funciones, o sea con parámetros.

Tabla I/O : 04, manipuladores en <iomanip>

Manipulador Descripción Entrada Salida

resetioflags( long f ) Desactiva las banderas especificadas por f X X

setbase( int base ) Establece la bases numérica a base --- X

setfill( int ch ) Establece carácter de relleno a ch --- X


setioflags( long f ) Activa las banderas especificadas por f X X

setprecision( int p ) Establece el número de digitos de precisión a p --- X

setw( int w ) Establece la longitud de campo a w --- X

Atendiendo a las tablas de banderas, así como a las tablas de manipuladores para las
mismas mostradas arriba, usted puede (con practica y perceverancia) lograr salidas con
muy buena presentación hacia los dispositivos estándar. Por ejemplo, el programa que se
mostrará a continuación muestra una de las formas de emplear manipuladores para
formatear números de punto flotante, en el programa se despliega una lista de valores
numéricos y se establece el campo de salida a una longitud de 12 caracteres y dos
decimales.

// Programación con C++


// programa Banderas03.cpp;
// probado en Dev-Cpp Versión 4.9.9.2

#include <iostream>
#include <iomanip>
using namespace std;

int main()
{
double data[] = { 347.25, 45.75, 124.50, 456.80, 1500.90 };
double total = 0;
int ancho = 12;

cout.precision(2);
cout.setf(ios::fixed);

for (int c = 0; c < 5; c++) {


cout << setw(ancho) << data[c] << endl;
total += data[c];
}
cout.fill('-');
cout << setw(ancho) << "" << endl;
cout.fill(' ');
cout << setw(ancho) << total << endl;

cout << "Por favor oprime Enter...";


cin.get();
return 0;
}
Streams para archivos o ficheros

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

Tabla I/O : 05, métodos para objetos istream, ostream y fstream

Función Descripción

bad true si ha ocurrido un error

clear limpia las banderas de estado (status flags)

close cierra un stream

eof true si se alcanzó el fin de archivo

fail true si ha ocurrido un error

fill establecer manipulador de carácter de relleno

flags accesa o manipula las banderas de formato de un stream

flush vaciar el buffer de un stream

gcount número de caracteres leidos durante la última operación de entrada


get lectura de caracteres

getline lectura de una línea de caracteres

good true si no ha ocurrido un error

ignore leer y descartar caracteres

open abrir un stream de entrada y/o salida

peek verifica la siguiente entrada de carácter

precision manipula la precisión del stream

put escritura de caracteres

putback regresar caracteres al stream

rdstate regresa la bandera de estado de stream

read lee datos de un stream hacia un buffer

seekg realiza acceso aleatorio sobre un stream de entrada

seekp realiza acceso aleatorio sobre un stream de salida

setf cambiar las banderas de formato

tellg lee el puntero del stream de entrada

tellp lee el puntero del stream de salida

unsetf limpiar las banderas de formato

width accesa y manipula la longitud minima del campo


write escritura datos desde un buffer hacia un stream

Abrir y cerrar archivo


A diferencia de los streams para dispositivos estándar, los cuales son creados y abiertos de
manera automática, para trabajar con archivos en discos se debe primeramente "abrir el
archivo", y luego de haber terminado de leer o escribir datos en el mismo, se debe "cerrar el
archivo". En C++, en orden de trabajar con archivos en disco, podemos emplear las clases
fstream, ifstream, y ofstream. Si usted desea abrir un archivo específico en modo de
lectura y escritura use un objeto de la clase fstream, por el contrario, si desea abrir un
archivo solo para lectura o solo para escritura use objetos de la clase ifstream y ofstream,
respectivamente.

La sintaxis para crear objetos de las tres clase mencionadas es:

'''streams para lectura y escritura'''


fstream();
fstream(const char*, int, int = filebuf::openprot);
fstream(int);
fstream(int _f, char*, int);

'''streams solo para lectura'''


ifstream();
ifstream(const char*, int, int = filebuf::openprot);
ifstream(int);
ifstream(int _f, char*, int);

'''streams solo para escritura'''


ofstream();
ofstream(const char*, int, int = filebuf::openprot);
ofstream(int);
ofstream(int _f, char*, int);

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

1. El primer método constructor crea un objeto que no está (aún) asociado a un archivo en
disco, en estos casos, se tendrá que usar el método stream.open("nombre de archivo");
para establecer la conexión entre el stream y el archivo en disco.
2. El segundo método constructor crea un objeto asociado a un archivo en disco, en estos
casos, el archivo en el disco queda abierto y asociado al stream. En este, el parámetro char
* apunta a una cadena de caracteres con el nombre del archivo.
3. El tecer método crea un stream a raiz de un identificador de archivo.
4. El cuarto método crea un stream a raiz de un identificador de archivo, un buffer y tamaño
de buffer específicos.
Para nuestro primer ejemplo usaremos el segundo de los constructores mencionados. Así, el
programa que se muestra en seguida realiza las siguientes tareas:

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


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

Para el caso que se presenta se debe prestar atención a los metodos:

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


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

// Ejemplo de ifstream y ofstream


// En este programa se demuestra la forma de crear un archivo
// en disco por medio de objeto ofstream, y la manera abrir y
// leer un archivo por medio de un objeto ifstream.

#include <iostream>
#include <fstream>
#include <string.h>

using namespace std;

char *filename = "test.$$$";


char *data = "Esta línea de texto se guardará en el archivo test.$$$";

// crear un archivo en disco cuyo nombre es dado por filename


int crearArchivo(char *filename)
{
ofstream fichero(filename); // crear o rescribir archivo
// verificar la creación del archivo
if ( fichero.bad() ) {
cout << "Error al tratar de abrir archivo";
cin.get();
return 1;
}

// escribir datos al archivo


for (unsigned int t = 0; t < strlen(data); t++ )
fichero.put(data[t] );

fichero.close();
cout << "archivo creado exitosamente" << endl;
return 0;
}

// abrir un archivo en disco cuyo nombre es dado por filename


int leerArchivo(char *filename)
{
ifstream fichero(filename); // abrir archivo para lectura
// verificar la apertura del archivo
if ( fichero.bad() ) {
cout << "Error al tratar de abrir archivo";
cin.get();
return 1;
}

// lectura de datos
while ( ! fichero.eof() ) cout << (char)fichero.get();
fichero.close();
cout << endl << "archivo leido exitosamente" << endl;
return 0;
}

int main()
{
crearArchivo(filename);
leerArchivo(filename);
cout << endl << "Presione <Enter>...";
cin.get();
return 0;
}

Usando operadores de redirección ( <<, >> )


sobre streams asociados con archivos en disco.

No hay que olvidar que si un archivo es asociado a un stream las operaciones de entrada y/o
salida para dicho archivo se hacen a travez del stream y, por lo tanto, se puede usar, no solo
los operadores de redirección, sino también todos los metodos, manipuladores y atributos
que se ha mencionado hasta este momento. Así, el objetivo del siguiente programa es
demostrar como hacer uso del operador << sobre un archivo asociado a un stream de salida,
y del operador >> sobre un archivo asociado a un stream de entrada. El ejemplo que se
mostrará no es realmente útil, sin embargo cumple con su cometido, es decir, el punto de
interes del mismo está en las líneas de código:

while ( ! fin.eof() ) {
fin >> temp;
fout << temp ;
}

Encargadas de leer datos del archivo de entrada y de escribir los mismos en el archivo de
salida.

#include <iostream.h>
#include <fstream.h>

char *filename = "test.$$$";


// abre y lee datos de un archivo en disco cuyo nombre es dado por
filename
int leerArchivo(char *filename)
{
ifstream fichero(filename); // abrir archivo para lectura

// verificar la apertura del archivo


if ( fichero.bad() ) {
cout << "Error al tratar de abrir archivo";
cin.get();
return 1;
}

// lectura de datos
while ( ! fichero.eof() ) cout << (char)fichero.get();
fichero.close();
cout << endl << "archivo leido exitosamente" << endl;
return 0;
}

// crea una copia del archivo test.$$$ hacia test.bak


// Nota: ningún carácter de espaciado en el origen le es tranferido al
destino
int backup(char *filename)
{
char newname[80];
int i = 0;

// copia el nombre del archivo original y remplaza la extensión por


"BAK".
while (filename[i] != '.' ) newname[i] = filename[i++];
newname[i] = '\0';
strcat(newname, ".BAK");

ifstream fin( filename ); // abrir archivo de entrada


ofstream fout( newname, ios::trunc ); // abrir archivo de salida

char temp;
while ( ! fin.eof() ) {
fin >> temp;
fout << temp ;
}

fin.close();
fout.close();
return 0;
}

int main()
{
backup(filename);
leerArchivo("test.bak");
cout << endl << "Presione <Enter>...";
cin.get();
return 0;
}

Arrays y cadenas de texto


Los arrays son usados extensamente por los programadores para contener listas de datos en
la memoria, por ejemplo, los datos almacenados en un disco suelen leerse y ponerse dentro
de un array con el objetivo de facilitar la manipulación de dichos datos, ya que los datos en
memoria pueden ser modificados, clasificados, marcados para su eliminacion, etc. para
luego ser reescritos al disco. Otro ejemplo podría ser el de un menú de opciones que se
desplegarán dentro de una ventana para que el usuario pueda elegir una de éstas, en tales
casos y cuando las opciones son numerosas, solamente se ponen unas cuantas de ellas
dentro de la ventana pero se le da al usuario la oportunidad de poder subir y bajar a su
antojo para ver el resto de opciones que, aunque no se vean en la ventana, forman parte del
menú o array de opciones.

Array:

Un array es un conjunto de datos del mismo tipo ordenados de forma líneal uno después de
otro. Los componentes de un array se han de referenciar por medio del nombre del array y
un índice de desplazamiento para indicar el componente deseado.

Indices de un array
Los índices son números que se utilizan para identificar a cada uno de los componentes de
un array. A modo de ejemplo, podemos pensar que los índices son como los números de
habitaciones de un hotel, es decir, para poder dirigirnos a un hotel específico es necesario
saber el nombre del mismo, luego, si queremos llegar a una habitación específica de dicho
hotel necesitaremos, además del nombre del hotel, el número de habitación deseado.

Dimensiones de un array
De acuerdo a la forma en que se construye o declara un array, éste puede ser clasificado
como: unidimensional, bidimensional y multidimensional. Los arrays que se emplean con
mucha más frecuencia son los estructurados a manera de vector ( array unidimensional ) y
los estructurados a manera de matriz ( array bidimensional ), así, aunque en C++ se pueden
crear estructuras multidimensionales, en este capítulo solo trataremos con vectores y
matrices.

Array unidimensional

Una array uni-dimensional es aquel en donde los componentes son accesibles por medio de
uno y solamente un índice que apunte al componente requerido. Los arrays de este tipo son
conocidos también con el nombre de vectores. Conceptualmente, podemos pensar en un
array unidimensional como una lista compuesta de datos, donde para referirnos a uno de
ellos emplearemos un número para indicar la posición del mismo dentro de la lista. Por
ejemplo, consideremos el caso de la tabla o array VentaSemanal, la cual está pensada para
registrar las ventas de cada uno de los días de la semana. De manera conceptual podemos
ver el array como se muestra a continuación:

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

array: VentaSemanal

+------+
| dato | <-- componente 0, ( fila 0 )
|------|
| dato | <-- componente 1, ( fila 1 )
|------|
| dato | ...
|------|
| dato | ...
|------|
| dato | ...
|------|
| dato | ...
|------|
| dato | <-- componente 6, ( fila 6 )
|------|

Si en el array VentaSemanal queremos que el elemento 4 ( por ejemplo ) contenga el valor


de 8987 lo podemos lograr con la instrucción: VentaSemanal[4] = 8987; y el estado del
array sería:

array: VentaSemanal

+------+
| dato |
|------|
| dato |
|------|
| dato |
|------|
| dato |
|------|
| 8987 | <--- componente 4
|------|
| dato |
|------|
| dato |
|------|

Array bidimensional
Un array bi-dimensional es aquel en donde los componentes son accesibles por medio de
una pareja de índices que apunten a la fila y a la columna del componente requerido. Los
arrays de este tipo son conocidos también con el nombre de matrices. Conceptualmente,
podemos pensar en un array bidimensional como en una lista compuesta de filas y
columnas, en donde para referirnos a una de ellas emplearemos un número para indicar la
posición de fila y otro número para indicar la posición de la columna del componente
deseado. Por ejemplo, consideremos el caso de la tabla o array VentaSemanaQ, la cual está
pensada para registrar las ventas de cada uno de los días de la semana por cuatro semanas, o
sea, una tabla de 7 x 4 elementos. De manera conceptual podemos ver el array como se
muestra a continuación:

array: VentaSemanaQ

C O L U M N A S

+--- componente ( 0, 0 )
|
+------+------+------+------+
| dato | dato | dato | dato |
|------|------|------|------|
F | dato | dato | dato | dato |
|------|------|------|------|
I | dato | dato | dato | dato |
|------|------|------|------|
L | dato | dato | dato | dato |
|------|------|------|------|
A | dato | dato | dato | dato |
|------|------|------|------|
S | dato | dato | dato | dato |
|------|------|------|------|
| dato | dato | dato | dato |
+------+------+------+------+
|
+---- componente ( 6, 3 )

Si en el array VentaSemanaQ queremos que el elemento de la fila 4, columna 3 ( por


ejemplo ) contenga el valor de 5000 lo podemos lograr con la instrucción:
VentaSemanaQ[4][3] = 5000; y el estado del array sería:

array: VentaSemanaQ

+--- componente ( 0, 0 )
|
+------+------+------+------+
| dato | dato | dato | dato |
|------|------|------|------|
| dato | dato | dato | dato |
|------|------|------|------|
| dato | dato | dato | dato |
|------|------|------|------|
| dato | dato | dato | dato |
|------|------|------|------|
| dato | dato | dato | 5000 | <-- componente ( 4, 3 )
|------|------|------|------|
| dato | dato | dato | dato |
|------|------|------|------|
| dato | dato | dato | dato |
+------+------+------+------+
|
+---- componente ( 6, 3 )

Declaración de arrays en C, C++


En C, C++ para declarar un array se emplea la sintaxis:

tipo identificador [tamaño] = { lista de inicialización } ;

donde,

 tipo se refiere al tipo de datos que contendrá el array. El tipo puede ser cualquiera
de los tipos estándar (char, int, float, etc.) o un tipo definido por el usuario. Es más,
el tipo del array puede ser de una estructura creada con: struct, union y class.

 identificador se refiere al nombre que le daremos al array.

 tamaño es opcional e indica el número de elementos que contendrá el array. Si un


array se declara sin tamaño, el mismo no podrá contener elemento alguno a menos
que en la declaración se emplee una lista de inicialización.

 lista de inicialización es opcional y se usa para establecer valores para cada uno de
los componentes del array. Si el array es declarado con un tamaño específico, el
número de valores inicializados no podrá ser mayor a dicho tamaño.

Ejemplos:

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

Iteraciones dentro de un array (vector)

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

Nota: por motivos de simplificación el programa está escrito al estilo de C estándar. Sin
embargo puede ser compilado y ejecutado en un compilador de C++.
#include <stdio.h>
#include <stdlib.h>

#define FILAS 7

int main()
{
float ventas[FILAS] = {
123.50, 234.60, 345.45, 321.40, 345.00, 456.65, 0.0 };

float total = 0;
int i;

puts("Ventas de la semana");
puts("-------------------");

for (i=0; i<FILAS; i++) {


total += ventas[i];
printf( "%8.2f\n", ventas[i] );
}

puts("--------");
printf("%8.2f\n", total );

system("pause");
return 0;
}

Esta es la salida del programa:


Ventas de la semana
-------------------
123.50
234.60
345.45
321.40
345.00
456.65
0.00
--------
1826.60

Iteraciones dentro de un array (matriz)

Con el fin de leer o escribir sobre cada uno de los componentes de una matriz se deben
crear dos ciclos de iteración. Así, para poner un ejemplo reconsideremos el caso de la tabla
VentaSemanaQ (vista en una sección anterior), y que dicho sea de paso es un array de 4 x
4 elementos de tipo double. Luego, vamos a mostrar como ejemplo un programa completo
en el cual se declara el array mencionado con valores inicializados, que serán mostrados en
pantalla y al final la suma de estos. Observe que en este caso se utilizan dos variables, una
para iterar sobre las filas y otra para iterar sobre las columnas de la matriz.

#include <stdio.h>
#include <stdlib.h>
#define FILAS 7
#define COLS 4

int main()
{
float VentaSemanaQ[FILAS][COLS] = {
123.50, 234.60, 345.45, 321.40,
345.00, 456.65, 123.50, 234.60,
345.45, 321.40, 345.00, 456.65,
123.50, 234.60, 345.45, 321.40,
345.00, 456.65, 123.50, 234.60,
345.45, 321.40, 345.00, 456.65,
0.0, 0.0, 0.0, 0.0 };

float totales[COLS] = { 0.0, 0.0, 0.0, 0.0 };


float grantotal = 0;

int f, c, t = 0 ; /* indices para filas, columnas y totales */

puts("Ventas de cuatro semanas");


puts("------------------------");

for (f=0; f<FILAS; f++) {


for (c=0; c<COLS; c++) {
totales[c] += VentaSemanaQ[f][c];
printf("%8.2f ", VentaSemanaQ[f][c] );
}
puts("");
}

puts("--------------------------------------");
for (t=0; t<COLS; t++) {
printf("%8.2f ", totales[t] );
grantotal += totales[t];
}

printf("\n\nGran total: %10.2f\n", grantotal);


system("pause");
return 0;
}

Salida del programa:

Ventas de cuatro semanas


------------------------
123.50 234.60 345.45 321.40
345.00 456.65 123.50 234.60
345.45 321.40 345.00 456.65
123.50 234.60 345.45 321.40
345.00 456.65 123.50 234.60
345.45 321.40 345.00 456.65
0.00 0.00 0.00 0.00
--------------------------------------
1627.90 2025.30 1627.90 2025.30

Gran total: 7306.40


Cadenas de caracteres
En C, C++ las cadenas de caracteres no son más que arrays de caracteres, salvo que a este
tipo de arrays el compilador les da un tratamiento especial. Usted puede manipular las
cadenas de caracteres de la misma manera en que manipula cualquier otro tipo de array, sin
embargo, es preferible hacer uso de una librería estándar especialmente escrita para
manipulacion de cadenas de caracteres, me refiero a la librería <string.h> y que viene
incluida con todo compilador de C, C++.

Para comenzar y antes de ver algunas de las funciones de la mencionada librería, tenemos
los siguientes ejemplos:

1. char nombre[] = "Oscar";


2. char nombre2[] = { 'O', 's', 'c', 'a', 'r', '\0' };

 En el ejemplo 1 se está declarando la variable nombre como una cadena de


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

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

La biblioteca string

Los compiladores de C, C++ dan soporte a la biblioteca de funciones <string.h>, a la que


accede por medio de la directiva #include <string.h>. No veremos en detalle todas las
funciones contenidas en dicha biblioteca, y nos limitaremos a mostrar algunos ejemplos de
ciertas funciones importantes.

strlen(): Obtener longitud de cadenas

Sintaxis: size_t strlen(const char *s);


Comentarios: La función strlen() devuelve la longitud de la cadena s.

Ejemplo:

char *nombre = "Oscar E. Palacios";


cout << strlen(nombre) << endl;

strcpy(): Copiar cadenas


Sintaxis: char *stpcpy(char *dest, const char *src);
Comentarios: stpcpy copia la cadena src hacia dest, la función termina hasta haber
encontrado en src el carácter de terminación null.

Ejemplo:

char *nombre = "Oscar E. Palacios";


char copia[80];
strcpy(copia, nombre);
cout << copia << endl;

strcat(): Concatenar cadenas

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


Comentarios: strcat agrega la cadena src a dest, la función termina hasta haber
encontrado en src el carácter de terminación null.

Ejemplo:

char nombre[] = "Oscar E.";


char copia[80] = " Palacios";
strcat(copia, nombre);
cout << copia << endl;

strlwr(): Convertir a minúsculas.

Sintaxis: char *strlwr(char *dest);


Comentarios: strlwr convierte todos los caracteres alfabéticos ( 'A' .. 'Z' ) en dest a
sus correspondientes caracteres alfabéticos ( 'a' .. 'z' ).

Ejemplo:

char nombre[] = "Oscar E. Palacios";


strlwr(nombre);
cout << nombre << endl;

strupr(): Convertir a mayúsculas.

Sintaxis: char *strupr(char *dest);


Comentarios: strupr convierte todos los caracteres alfabéticos ( 'a' .. 'z' ) en dest a
sus correspondientes caracteres alfabéticos ( 'A' .. 'Z' ).

strchr(): Buscar carácter ( hacia adelante )

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


Comentarios: strchr busca en s el caracter c. La busqueda se lleva a cabo desde el
inicio hasta el final de s.
Regreso: si la operación es exitosa strchr regresa un puntero hacia la primera
ocurrencia de c en s, en caso contrario strchr regresa null.

Ejemplo:

char nombre[] = "Oscar E. Palacios";


char *p;

p = strchr(nombre, 'E');
if (p) {
cout << "nombre contiene a E" << endl;
cout << "indice = " << (p - nombre) << endl;
}
else cout << "E no está en nombre" << endl;

strrchr(): Buscar carácter ( hacia atras )

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


Comentarios: strchr busca en s el caracter c. La busqueda se lleva a cabo desde el
final hasta el inicio de s.
Regreso: si la operación es exitosa strchr regresa un puntero hacia la última
ocurrencia de c en s, en caso contrario strchr regresa null.

Ejemplo:

char nombre[] = "Oscar E. Palacios";


char *p;

p = strrchr(nombre, 'E');
if (p) {
cout << "nombre contiene a E" << endl;
cout << "indice = " << (p - nombre) << endl;
}
else cout << "E no está en nombre" << endl;

strstr(): Buscar subcadena

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


Comentarios: strstr busca en s1 la subcadena s2. La búsqueda se lleva a cabo desde
el inicio hasta el final de s1.
Regreso: si la operación es exitosa strstr regresa un puntero hacia la primera
ocurrencia de s2 en s1, en caso contrario strstr regresa null.

Ejemplo:

char s[] = "Un barco de tristeza";


char *p;
p = strstr(s, "barco");
if (p) {
cout << "barco está en s" << endl;
cout << "indice = " << (p - s) << endl;
}
else cout << "barco no está en s" << endl;

Cadenas en C++
En la sección anterior descubrimos algunas funciones para trabajar con cadenas de
caracteres al estilo de C estándar, si bien no está de más tener tal conocimiento, también es
cierto que C++ es un lenguaje de programacíón orientado a objetos, de tal manera que
ciertos compiladores ( como el gcc, utilzado por Bloodshed Dev-C++ y otros tantos
entornos de desarrolo ) dan soporte a la clase cstring, que no debe confundirse con la
<string.h>.

Nota: Bloodshed Dev-C++ es un IDE (Editor con Depurador Integrado) para


programar en C++ en un ambiente gráfico para Windows, distibuido gratuitamente
bajo licencia GPL GNU y usted puede encontrarlo aquí: www.bloodshed.net.
Actualmente (febrero de 2008) se recomienda bajar la versión Dev-C++ 4.9.9.2.
Nota:Como el Dev-c++ ya esta descontinuado, es recomendable usar su extensión
Wx Dev-C++ que esta actualmente activa y es recomendable para muchos
proyectos bajo el lenguaje C++, este programa tambien es licencia GPL, podras
descargarlo desde=wxdsgn.sourceforge.net, y encontraras informacion de este
aqui=es.wikipedia.org/wiki/WxDev-C%2B%2B.

Una de las ventajas que ofrece la clase cstring es que, a diferencia de las cadenas estándar,
ésta posee la capacidad de crecer o disminuir su tamaño en tiempo de ejecución. Además,
entre otras caracteristicas destacables, la clase string soporta operaciones de asignación
tales como: =, +, +=, etc.; y de comparación tales como: ==, <=, etc.

Para tener una idea básica sobre las cadenas en C++ veamos el siguiente programa:

Nota: en el programa se debe de observar el uso del operador de asignación +=,


algo que no es posible hacer con las cadenas estándar.

Un estudio exhaustivo sobre la clase string requiere de un capítulo completo, ya que la


misma, según el manual de referencia de C++, posee aproximadamente 33 métodos y unos
7 constructores; además de los atributos.
Arrays en C++
Así como C++ da aternativas elegantes para la manipulación de cadenas de caracteres,
también da el soporte para la manipulacíon de arrays dinámicos. Este tema será ampliado
en el capítulo Libreria de Plantillas Estándar STL, sin embargo para tener una idea de lo
que vendrá mostraremos aquí un ejemplo sencillo en donde se usará la clase plantilla
vector.
Desarrollo Orientado a Objetos
Antes de empezar a programar con objetos, es necesario entender ciertos conceptos básicos.

Terminología

El paradigma de Programación Orientada a Objetos utiliza algunos términos básicos que se


explicarán a continuación.

 Objeto. Es una entidad que tiene atributos o propiedades y que presenta un


comportamiento. Puede ser un objeto del mundo real, o una abstracción al mundo
informático. Ejemplo, un automóvil, una persona, un animal, etc.

 Clase. Los humanos percibimos el mundo como un entorno compuesto de cosas,


objetos, les llamamos. Interactuamos con tantos, que lo natural es agruparlos de
acuerdo a sus características más relevantes o generales. En otras palabras, los
clasificamos en clases. Todos los objetos que pertenecen a una clase, contiene todos
los elementos comunes a todos los objeto y no particulariza en los elementos que
distinguen a los objetos entre sí; ejemplo, la clase automóvil, comprende todos los
automóviles que circulan en este momento por las calles. Incluiría elementos que
asociamos a los automóviles, como llantas, puertas, asientos, volante, etcétera, pero
no particularizaría en cuestiones como color, número de registro o características
individuales a cada auto.

 Propiedad. Una característica asociada a una clase. Ejemplo, todos los autos tienen
un color, o un registro aunque esta propiedad varíe entre objetos.

 Método. Posible acción que los objetos de una clase pueden realizar. En términos
informáticos, un algoritmo que pueden ejecutar. Ejemplo: todos los autos pueden
acelerar, frenar, etc.

Características de la programación orientada a objetos


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

 Polimorfismo. Una vez que aprendemos a conducir un auto, podemos manejar


prácticamente cualquier auto. Esto se debe a que la interfaz que presentan los
objetos auto a los usuarios, es más o menos la misma para cada auto. Todos tiene
métodos para acelerar, y si bien, posiblemente los pormenores de como acelera cada
auto, sean sustancialmente diferentes; el usuario hace referencia a un mismo método
en todos los autos. En términos informáticos, objetos de clases diferentes; incluso
en la misma clase; pueden tener métodos homónimos, que hagan cosas equivalentes
aunque los detalles del como, sean diferentes.

 Herencia. La clase auto es una clase muy general; se puede hacer un desglose más
pormenorizado, por ejemplo, si hacemos subclases de autos deportivos, utilitarios,
compactos, manuales, automáticos, de lujo, etc. Cada subclase toma de su clase
base (o superclase, en la jerga informática) las propiedades y métodos más
generales y agrega las propias. Así, como todos los autos, tienen color y registro, así
que éstas estarían contenidas en la superclase auto. Propiedades como Capacidad en
toneladas, sistema estéreo o cantidad de velocidades, estarían delegadas a las
subclases más particulares.
Clases y Objetos
Este capítulo introduce a las clases en C++. La clase es la fundación de C++ para el soporte
de la programación orientada a objetos, y se encuentra en el núcleo de muchas de sus más
avanzadas características. La clase es la unidad básica de C++ de la encapsulación y esta
provee el mecanismo por el cual los objetos son creados.

Fundamentos de Clases
Vamos a comenzar definiendo los términos de clase y objeto. Una clase define un nuevo
tipo de dato que especifica la forma de un objeto. Una clase incluye los datos y el código
que operará sobre esos datos. Además, una clase enlaza datos y código. C++ usa una
especificación de una clase para construir objetos. Los objetos son instancias de una clase.
Además, una clase es esencialmente una serie de planes que especifican cómo construir un
objeto. Es importante tener claro esto: Una clase es una abstracción lógica.

No es sino hasta que un objeto de esa clase sea creado que la representación física de la
clase existe en la memoria. Cuando se define una clase, se declaran los datos que ésta
contiene y el código que opera en esos datos. Aunque clases muy simples pueden contener
sólo código o sólo datos, la mayoría de las clases contienen ambos. En conjunto, los datos
se almacenan en las variables y el código en las funciones. Colectivamente, las funciones y
variables que constituyen una clase son llamados 'miembros' de la clase. Una variable
declarada dentro de una clase es llamada 'variable miembro', y una función declarada en
una clase es llamada 'función miembro'. En ocasiones el término 'variable de instancia' es
usado en lugar de variable miembro.

Una clase es creada con la palabra clave class. La declaración de una clase es similar
sintácticamente a una estructura (y tienen muchísimo que ver). Aquí tenemos un ejemplo.
La siguiente clase define un tipo llamado CRender, el cual es usado para implementar
operaciones de renderizado en este caso.

// Esto define la clase CRender


class CRender {
char buffer[256];
public:
void m_Renderizar();
};

Veamos más de cerca esta declaración de la clase.

Todos los miembros de CRender son declarados dentro de la declaración 'class'. La


variables miembro de CRender es buffer. La función miembro es m_Renderizar.

NOTA: Por defecto los miembros de una clase son privados.


Una clase puede contener tanto miembros privados como públicos. Por defecto, todos los
elementos definidos en una clase son privados. Por ejemplo la variable buffer es privada.
Esto significa que sólo pueden acceder a ella otros miembros de la clase CRender, cosa que
no podrá hacer ninguna otra parte del programa. Es una forma de lograr la encapsulación:
se puede controlar el acceso a ciertos elementos de datos manteniéndolos privados. Aunque
no hay ninguna en este ejemplo, se pueden definir funciones privadas, las cuales pueden ser
llamadas solamente por otros miembros de la clase.

Para hacer pública una parte de la clase (accesible a otras partes del programa), se debe
declarar con la palabra clave public. Todas las variables o funciones definidas después de
la declaración pública son accesibles por todas las demás funciones en el programa. En
nuestra clase CRender, la función m_Renderizar() es pública. Típicamente, su programa
accederá a los miembros privados de una clase a través de sus funciones públicas. Note que
la palabra clave public es seguida con : . Mantenga en mente que un objeto forma una
relación entre código y datos. Una función miembro tiene acceso a los elementos privados
de su clase. Esto significa que m_Renderizar tiene acceso a buffer en nuestro ejemplo. Para
añadir una función miembro a la clase, debe especificar su prototipo en la definición de la
misma.

Una vez que se ha definido una clase, se puede crear un objeto de ese tipo usando el
nombre de la clase. El nombre de la clase se convierte en un especificador del nuevo tipo.
Por ejemplo la siguiente declaración crea 2 objetos llamados render1 y render2 del tipo
CRender.

CRender render1, render2;

Cuando un objeto de la clase es creado, éste tendrá su propia copia de las variables
miembros que contiene la clase. Esto significa que render1 y render2 tendrán su propia e
independiente copia de buffer. Los datos asociados con render1 son distintos y separados de
los datos asociados con render2.

Recordemos: En C++, una clase es un nuevo tipo de dato que puede ser usado para crear
objetos. Específicamente, una clase crea una consistencia lógica que define una relación
entre sus miembros. Cuando se declara una variable de una clase, se está creando un objeto.
Un objeto tiene existencia física, y es una instancia específica de una clase. ( Esto es, un
objeto ocupa espacio de memoria, pero una definición de tipo no ). Además, cada objeto de
una clase tiene su propia copia de los datos definidos dentro de esa clase.

Dentro de la declaración de CRender, el prototipo de una función es especificado. Ya que


las funciones miembros son prototipadas dentro de la definición de la clase, no necesitan
ser prototipadas en otro lugar cualquiera.

Para implementar una función que es un miembro de una clase, debe indicarle al
compilador a cual clase pertenece la función calificando el nombre de la función con el
nombre de la clase. Por ejemplo, esta es una manera de codificar la función
m_Renderizar().
void CRender::m_Renderizar()
{
strcpy(buffer, "C++ en wikibooks");
return;
}

Resolución de ámbito

El :: es llamado el operador de resolución de ámbito. Esencialmente le dice al compilador


que esta versión de m_Renderizar pertenece a la clase CRender. Dicho de otra forma, ::
declara que m_Renderizar se encuentra en el ámbito de CRender. Varias clases diferentes
pueden usar los mismos nombres de función. El compilador sabe cuál función pertenece a
cuál clase y esto es posible por el operador de resolución de ámbito y el nombre de la clase.

Acceso a las funciones

Las funciones miembros de una clase sólo pueden ser llamadas relativas a un objeto
específico. Para llamar a una función miembro desde alguna parte del programa que se
encuentre fuera de la clase, se debe usar el nombre del objeto y el operador de
direcionamiento '.' ( punto ). Por ejemplo, lo siguiente llama a m_Renderizar() en el objeto
objeto1.

CRender objeto1, objeto2;

objeto1.m_Renderizar();

La invocación de objeto1.m_Renderizar() causa a m_Renderizar() operar en los datos de la


copia de objeto1. Mantenga en mente que objeto1 y objeto2 son 2 objetos separados. Esto
significa, por ejemplo, que inicializar objeto1 no causa que objeto2 sea inicializado, La
única relación que objeto1 tiene con objeto2 es que es un objeto del mismo tipo.

Cuando una función miembro llama a otra función miembro de la misma clase, puede
hacerlo directamente, sin usar un objeto y el operador '.' En este caso, el compilador ya
conoce en cuál objeto se está operando. Solamente cuando una función miembro es llamada
por código que se encuentra fuera de la clase es cuando debe utilizarse el nombre del objeto
y el operador '.' Por la misma razón, una función miembro puede referirse directamente a
una variable miembro, pero código fuera de la clase debe referenciarse a la variable a través
de un objeto y el operador '.'

El programa siguiente muestra aquí todas las piezas juntas y detalles perdidos, e ilustra la
clase CRender.

Ejemplo
// Programa OPP01.CPP
#include <iostream>
#include <cstring>

using std::cout;
using std::endl;

// Esto define la clase CRender


class CRender {
public:
char buffer[255];
void m_Renderizar(const char *cadena);
};

/* implementar m_Renderizar() para la c;*/


void CRender::m_Renderizar(const char *cadena){
strcpy(buffer, cadena);//copia la cadena
return;
}

int main (int argc, char **argv){


// crear 2 objetos CRender
CRender render1, render2;

render1.m_Renderizar("Inicializando el objeto render1");


render2.m_Renderizar("Inicializando el objeto render2");

cout << "buffer en render1: ";


cout << render1.buffer << endl; // tenemos acceso a buffer ya que
es publico.

cout << "buffer en render2: ";


cout << render2.buffer << endl;

return (0);
}

Este programa imprime:

buffer en render1: Inicializando el objeto render1

buffer en render2: Inicializando el objeto render2

Miembros de una clase (métodos y atributos)


En el lenguaje coloquial de la programación orientada al objeto es común escuchar
términos tales como: métodos, atributos, herencia, polimorfismo, etc. En esta sección
nos encargaremos de hablar de los dos primeros.

Métodos:
En comparación con la programación tradicional, un método es lo mismo que una
función cualquiera, salvo que como los métodos se declaran para pertenecer a una
clase específica, se dice que todos los métodos de dicha clase son miembros de la
misma. Por lo demás, la declaración y definición de los métodos es exactamente
igual que declarar y definir cualquier otra función.

Atributos:

En comparación con la programación tradicional, un atributo es lo mismo que una


variable cualquiera, salvo que como los atributos se declaran para pertenecer a una
clase específica, se dice que todos los atributos de dicha clase son miembros de la
misma. Por lo demás, la declaración de los atributos es exactamente igual que
declarar cualquier otra variable.

Miembros:

A partir de este momento usaremos la palabra miembro para referirnos al hecho de


que un método o un atributo pertenece a tal o cual clase.

Por Ejemplo, en el programa OOP01.CPP (visto anteriormente) la Clase CRender posee


dos miembros, buffer que es un atributo; y m_Renderizar que es un método.

class CRender {
public:
char buffer[256]; // atributo
void m_Renderizar(const char *cadena); // método
};

Visibilidad de los miembros de una clase


Por visibilidad se entiende al acto de acceder a los miembros de una clase. En este sentido,
los miembros de una clase pueden ser: públicos, privados y protegidos.

 Un miembro público significa que el acceso al mismo puede darse dentro del
interior de la clase, dentro de una subclase, y desde un objeto instanciado de
cualquiera de estas. Por ejemplo, los miembros de la clase CRender son accesibles
dentro de la misma y podrán accederse desde cualquier otra clase que se derive de
CRender, así como desde cualquier objeto instanciado de estas.

 Un miembro privado significa que el acceso al mismo puede darse solamente dentro
del interior de la clase que lo posee. Normalmente, el programador creador de una
clase declara a los atributos de la clase como privados y a los métodos como
públicos, esto con la idea de que el usuario de la clase no pueda tener acceso a los
atributos sino es a traves de los métodos definidos para el caso.
 Un miembro protegido se comporta de manera parecida a un miembro privado,
salvo que estos son accesibles dentro de la clase que lo posee y desde las clases
derivadas, pero no desde los objetos instanciados a raiz de dichas clases.

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

En la clase Pareja que se verá en seguida, se declaran dos atributos y cuatro métodos para
la manipulación de dichos atributos. Observe que los atributos son privados(por defecto),
mientras que los métodos se declaran públicos.

class Pareja
{
// atributos
double a, b;

public:
// métodos
double getA();
double getB();
void setA(double n);
void setB(double n);
};

// implementación de los métodos de la clase Pareja


//
double Pareja::getA() { return a; }
double Pareja::getB() { return b; }
void Pareja::setA(double n) { a = n; }
void Pareja::setB(double n) { b = n; }

Subclases
Una subclase es una clase que se deriva de otra. La clase que sirve de base suele conocerse
como parent (padre), y a la subclase se le llama child (hija). En C++ cada clase que es
creada se convierte en candidata para servir de base de donde se deriven otras. Por ejemplo,
la clase Pareja es candidata para convertirse en la base para las subclases Suma, Resta,
Multiplica, Divide, y otras posibles subclases en donde se utilice un par de valores
numéricos.

Para poner un ejemplo, pensemos en que deseamos crear la clase Suma, misma que será
utilizada para obtener la suma de dos números. Puesto que la clase Pareja posee dos
atributos númericos puede ser usada como base para la clase que estamos proyectando. Así,
el siguiente ejemplo se constituye en un caso de clases derivadas.

Nota: Observe que la sintaxis para crear una subclase es:


class hija : [public | private] padre // ¿Qué significan public y private
aquí?
{
...
};
Donde padre es la clase base e hija es la subclase.
class Suma : public Pareja
{
// atributos de Suma
double resultado;

public:
// métodos de Suma
double calcular();
};

// implementación de Suma
//
double Suma::calcular() { return getA() + getB(); }

Probando las clases Pareja y Suma


// programa OOP02.CPP
// Este programa pone a prueba el uso de las clase Suma,
// misma que es una subclase de la clase Pareja (ambas definidas
anteriormente).

#include <iostream.h>

int main()
{
Suma s;
s.setA(80);
s.setB(100);
cout << s.getA() << " + " << s.getB() << " = " << s.calcular() <<
endl;
cin.get();
return 0;
}

Herencia
La herencia es uno de los mecanismos más útiles de la programación orientada al objeto,
ya que por medio de la misma se puede llevar a cabo la reutilización de código. Es decir,
puesto que toda clase definida se convierte en candidata para ser usada como base de donde
se deriven otras, esto da como resultado que las clases derivadas hereden todos los
miembros de la clase base. Por ejemplo, la clase Suma vista en la sección anterior, hereda
todos los miembros de la clase Pareja puesto que Suma es una extensión de Pareja. En ese
sentido, podemos decir que existen dos tipos de herencia, por extensión y por agregación o
composición. En el caso de las clases Pareja y Suma, se dice que Suma es una extensión de
Pareja. Vista gráficamente, la herencia por extensión se puede representar así:

Herencia por extensión


Herencia

Al tipo de diagrama mostrado arriba (Herencia por extensión) se le conoce como UML [1]
y es utilizado para mostrar de forma grafica la relación existente entre una clase hija con la
clase padre. En el caso del ejemplo, se muestra que la clase Suma es una extensión de la
clase Pareja y, en consecuencia, Suma posee a los miembros { a, b, getA(), getB(), setA(),
setB() } heredados de la clase Pareja. Observe como la clase Suma posee otros dos
miembros no heredados, { resultado, y calcular() }, y es precisamente a este tipo de
situación por lo que se dice que Suma es una extensión de Pareja, ya que Suma, además de
poseer a todos los miembros de Pareja, se extiende para poseer o adquirir otros dos
miembros.

Agregacion o composición

La composición se da en los casos en donde una clase posee un objeto que es una instancia
de otra clase. Por ejemplo, la clase Suma podría escribirse de la siguiente forma:

class Suma
{
// atributo privado
double resultado;

public:
// método público
double calcular();

// atributo público
Pareja p;
};
// implementación del metodo calcular de la clase Suma.
double Suma::calcular() { return p.getA() + p.getB(); }

Luego, si usted presta atención, notará que el miembro p de la clase Suma es un objeto o
instancia de la clase Pareja, en consecuencia, la clase Suma puede acceder a los miembros
de la clase Pareja a través de la variable p. También se debe observar que la
implementación del método calcular() es diferente que el mismo de la clase Suma original.
Si usted desea poner a prueba a la nueva clase Suma, compile y ejecute el siguiente
programa.

// programa herencia_por_composicion.CPP

#include <iostream>
using namespace std;

class Pareja
{
// atributos
double a, b;

public:
// métodos
double getA();
double getB();
void setA(double n);
void setB(double n);
};

// implementación de los métodos de la clase Pareja


double Pareja::getA() { return a; }
double Pareja::getB() { return b; }
void Pareja::setA(double n) { a = n; }
void Pareja::setB(double n) { b = n; }

class Suma
{
// atributo privado
double resultado;

public:
// método público
double calcular();
// atributo público
Pareja p;
};
// implementación del metodo calcular de la clase Suma.
double Suma::calcular() { return p.getA() + p.getB(); }

int main()
{
Suma s;
s.p.setA(80);
s.p.setB(100);
cout << s.p.getA() << " + " << s.p.getB() << " = " << s.calcular() <<
endl;
cin.get();
return 0;
}

////SALIDA////

80 + 100 = 180

Constructores
Un constructor es un método que pertenece a una clase y el cual (en C++) debe tener el
mismo nombre de la clase a la que pertenece. A diferencia de los otros métodos de la clase,
un constructor deberá ser del tipo void, es decir, el mismo no regresará valor alguno. Una
clase puede tener más de un método constructor. Cada clase debe tener al menos un
constructor, tanto así que si el programador creador de una clase no define un método
constructor para la misma, el sistema, o sea el compilador, creará de manera automática un
constructor nulo.

El objetivo principal del constructor es establecer las condiciones necesarias dentro de la


memoria y crear una copia del objeto mismo dentro de la memoria.

Los constructores suelen usarse para la inicialización de los atributos de los objetos
instanciados. Por ejemplo, con las instrucciones:

Suma s;
s.setA(80);
s.setB(100);

del programa OOP02.CPP, se declara el objeto s de la clase Suma y luego se inicializan los
atributos del objeto por medio de los métodos setA() y setB(). En este caso, es necesario
hacer uso de dichos métodos debido al hecho de que no se ha definido un constructor para
la clase Suma. Ahora bien, para evitar este tipo de situaciones podemos definir un método
constructor para Suma y que este se encargue de inicializar los atributos. Veamos.

Notas: ya que Suma es una extensión de Pareja, se ha definido el método constructor de


Pareja y éste a la vez es invocado por el constructor de Suma. Otro punto de interés es el
hecho de la definición de un constructor base para Pareja, ya que de acuerdo con las reglas
de programación en C++ toda clase en donde se defina un constructor parametrizado deberá
definir un constructor base; esta regla se aplica en los casos en donde la clase proyectada
servirá de base para otras.

// programa OOP04.CPP
// Ejemplo: clases Pareja y Suma, ambas con constructor

#include <iostream>
using namespace std;

//------------------------
class Pareja
{
// atributos
double a, b;

public:
// constructor de base (null)
Pareja() {}

// constructror parametrizado
Pareja(double x, double y) : a(x), b(y) {}

// métodos
double getA();
double getB();
void setA(double n);
void setB(double n);
};

// implementación de los métodos de la clase Pareja


//
double Pareja::getA() { return a; }
double Pareja::getB() { return b; }
void Pareja::setA(double n) { a = n; }
void Pareja::setB(double n) { b = n; }

//------------------------
class Suma : public Pareja
{
// atributos de Suma
double resultado;

public:
// constructor
Suma(double a, double b) : Pareja(a, b) {}

// métodos de Suma
double calcular();
};

// implementación de Suma
//
double Suma::calcular() { return getA() + getB(); }

//------------------------
int main()
{
Suma s(80, 100);
cout << s.getA() << " + " << s.getB() << " = " << s.calcular() <<
endl;
cin.get();
return 0;
}
Destructores
Un destructor es un método que pertenece a una clase y el cual (en C++) debe tener el
mismo nombre de la clase a la que pertenece. A diferencia de los otros métodos de la clase,
un destructor deberá ser del tipo void, es decir, el mismo no regresará valor alguno. Para
diferenciar a un método destructor de un método constructor, al nombre del destructor se le
debe anteponer el caracter ~ (Alt + 126).

El objetivo principal del destructor es el de retirar de la memoria al objeto, o sea, el


destructor hace todo lo contrario que el constructor.

Los destructores suelen usarse para liberar memoria que haya sido solicitada por el objeto a
través de las ordenes malloc(), new, etc. En tales casos se deberá incluir dentro del método
destructor la orden free, delete, etc., según sea el caso.

// clase Pareja con constructor y destructor


class Pareja
{
// atributos
double a, b;

public:
// constructor de base (nulo)
Pareja() {}

// constructor parametrizado
Pareja(double x, double y) : a(x), b(y) {}

// destructor
~Pareja() {}

// métodos
double getA();
double getB();
void setA(double n);
void setB(double n);
};
Sobrecarga de operadores
La sobrecarga de operadores es uno de los mecanismos que nos permite ampliar las
capacidades de los lenguajes de programación orientados a objetos. En C++, la declaración
y definición de una sobrecarga de operador es muy similar a la declaración y definición de
una función cualquiera. El ejemplo más sencillo de una sobrecarga de operadores nos lo da
el lenguaje mismo, es decir, en una operación aritmética (por ejemplo, una suma ) el
compilador determina el tipo de operación requerida de acuerdo con el tipo de datos
involucrados. Vamos a suponer que se tienen las variables: int A, B; double X, Y; int R; y
las siguientes instrucciones:

R = A + B;
R = A + X;
R = X + Y;

Ahora bien:

 en el primer caso el compilador determina que el tipo de operación requerida es una


suma de enteros debido a que los dos miembros ( A y B ) de la suma son del tipo
entero.
 en el segundo caso parece ser que las cosas no son tan claras, ya que en este los
miembros involucrados en la suma son de tipos diferentes, sin embargo el
compilador determinará el tipo de operación requerida y depositará en el resultado (
R )el valor resultante (no redondeado sino el inmediato anterior).
 en el tercero y último de los casos el compilador determina que el tipo de operación
requerida es una suma de reales debido a que los dos miembros ( X e Y ) de la suma
son del tipo double. También en este caso el resultado de la suma no se redondea
sino que pone el inmediato anteior.

Mi primera sobrecarga
Para poner un ejemplo práctico de sobrecarga del operador de suma ( + ) vamos a
considerar el caso de la clase pareja mostrada enseguida:

class Pareja {
public:
double a, b;

// constructor parametrizado
Pareja(const double a,const double b)
{
this->a = a;
this->b = b;
}
};

int main()
{
Pareja A(50, 75 );
Pareja B(150, 175 );
Pareja C = A + B;

return 0;
}

Si usted trata de compilar el programa anterior descubrirá que el compilador se queja, es


decir, el compilador no sabe qué hacer en el caso de la instrucción Pareja C = A + B;
aunque es evidente que se trata de la declaracion del objeto C y que el mismo se espera que
sea igual a la suma de los objetos A y B. El error se debe al hecho de que el tipo Pareja es
en realidad una clase y no un tipo primitivo y, en consecuencia, se debe de adiestrar al
compilador para que éste sepa de qué manera hará la suma de dos objetos del tipo Pareja.
Así, para solucionar el error vamos a sobrecargar el operador + para trabajar la suma de dos
de dichos objetos. Veamos:

// Titulo..: programa sobrecarga01.cpp


// Objetivo: demostración de sobrecarga de operadores
// Autor...: Oscar E. Palacios
#include <iostream>
using namespace std;

class Pareja {
public:
double a, b;

// constructor parametrizado
Pareja(const double a,const double b)
{
this->a = a;
this->b = b;
}
};

// Sobrecarga del operador +


Pareja& operator +(const Pareja &p1,const Pareja &p2)
{
return *(new Pareja(p1.a + p2.a, p1.b + p2.b) );
}

int main()
{
Pareja A(50, 75 );
Pareja B(150, 175 );
Pareja C = A + B;

cout << "A = " << A.a << ',' << A.b << "\n";
cout << "B = " << B.a << ',' << B.b << "\n";
cout << "C = " << C.a << ',' << C.b << "\n";

return 0;
}
El ejemplo anterior tiene un problema al reservar memoria dinámica que no es liberada
posteriormente. El código, por tanto, tiene pérdidas de espacio de almacenamiento. La
implementación correcta en C++ de este operador sería:
// Sobrecarga del operador +
Pareja operator +(const Pareja &p1,const Pareja &p2)
{
Pareja res( p1.a + p2.a, p1.b + p2.b );
return res;
}

Sintaxis general
Tal y como usted lo habrá notado, la declaración de sobrecarga en el programa anterior es
lo más parecido a una función, es decir, la sintaxis general para sobrecargar un operador
cualquiera es:

tipo operator + (lista de parámetros);

en donde,

1. tipo se refiere al tipo regresado por el operador


2. operator es una palabra reservada y debe aparecer en toda declaración de sobrecarga
de operadores
3. el simbolo + está en representación de cualquier operador
4. lista de parámetros indica los argumentos sobre los que operarará la función de
sobrecarga

Nota: en el caso de que el operador sobrecargado se hace para una clase específica la
sintaxis se extiende de la siguiente manera:

tipo nombre_clase::operator + (lista de parámetros);

Regresando al caso de la clase Pareja, específicamente al operador + de sobrecarga para


dicha clase, nos damos cuenta que dicho operador opera sobre dos objetos de dicha clase y
también devuelve un resultado de la misma clase. Esto es lo más recomendable ya que si
usted recuerda el compilador regresa un resultado entero si es que (por ejemplo) se le da
una instrucción como: R = 100 + 23;. En todo caso, el resultado devuelto por los
operadores sobrecargados es responsabilidad del que esta programando

Sobrecarga permitida de operadores


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

Sobrecarga del operador << ( iostream )


Normalmente cuando se escribe una clase y se desea que el stream estándar de salida (
cout ) pueda mostrar una representación de su valor se debe de sobrecargar el operador <<.
Para mostrar un ejemplo retomemos el programa sobrecarga01.cpp ( visto arriba ), en el
mismo se imprimen por medio de cout los miembros ( a y b ) de los objetos A, B y C.

cout << "A = " << A.a << ',' << A.b << "\n";
cout << "B = " << B.a << ',' << B.b << "\n";
cout << "C = " << C.a << ',' << C.b << "\n";

Usted puede ver cómo para cada uno de los miembros de los objetos se debe de usar el
operador de dirección ( . ), pues bien, nuestro objetivo es lograr que dado un objeto de la
clase Pareja éste pueda ser desplegado por cout y para ello haremos la sobrecarga de
operador << con el fin de que pueda operar con objetos de la clase mencionada. Veamos:

// Titulo..: programa sobrecarga03.cpp


// Objetivo: demostración de sobrecarga de operadores
// Autor...: Oscar E. Palacios
#include <iostream>
using namespace std;

class Pareja {
public:
double a, b;

// constructor parametrizado
Pareja(const double a,const double b)
{
this->a = a;
this->b = b;
}
};

// Sobrecarga del operador +


Pareja& operator +(const Pareja &p1,const Pareja &p2)
{
return *(new Pareja(p1.a + p2.a, p1.b + p2.b) );
}

// Sobrecarga del operador << para la clase Pareja


ostream& operator << (ostream &o,const Pareja &p)
{
o << "(" << p.a << ", " << p.b << ")";
return o;
}

int main()
{
Pareja A(50, 75 );
Pareja B(150, 175 );
Pareja C = A + B;

cout << "A = " << A << "\n";


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

Sobrecarga del operador >> ( istream )


Así como el operador << debe ser sobrecargado, lo mismo es cierto para el operador >>
para poder ser usado con el stream estándar de entrada ( cin ). Retomaremos nuevamente a
la clase Pareja para dar un ejemplo.

// Titulo..: programa sobrecarga03.cpp


// Objetivo: demostración de sobrecarga de operadores
// Autor...: Keylord Maldonado Maciel y Arlette Any Conchas Vergara
#include <iostream>
using namespace std;

// Arcad. inserte aquí la clase Pareja y los operadores sobrecargados


vistos
// mientras en el programa anterior.
// entonces Se deberá agregar un constructor en
// la clase
// Pareja como
// el siguiente
// luego de insertar todo el codigo bien
// a continuación como se muestra
/*Pareja()
{
this->a=0;
this->b=0;
}*/

// Sobrecarga del operador >> para la clase Pareja


istream& operator >> (istream &i, Pareja &p)
{
cout << "Introducir valores para ( a, b) : ";
i >> p.a >> p.b;
i.ignore();
return i;
}

int main()
{
Pareja A(50, 75 );
Pareja B;
cin >> B;
Pareja C = A + B;

cout << "A = " << A << "\n";


cout << "B = " << B << "\n";
cout << "C = " << C << "\n";

return 0;
}
Operadores amigos ( friend )
Un operador amigo, al igual que una función amiga, es aquel que aún cuando no es
miembro de una clase tiene todos los privilegios de acceso a los miembros de dicha clase.

En la sobrecarga de los operadores +, << y >> para la clase Pareja de los programas
anteriores, se puede notar que dichos operadores no son parte de la clase Pareja, sino que
más bien éstos operan sobre objetos de dicha clase y es a través de dichos objetos que
pueden manipular a los miembros de la clase. Ahora bien, no hemos tenido ningún
problema debido a que todos los miembros de Pareja han sido declarados como públicos
(public:), pero ¿qué sucede si un operador o una función que no sea parte de la clase trata
de acceder a los miembros privados o protegidos de ésta? En estos casos, el compilador
reportaría el error indicando que tal o cual miembro es privado dentro de cierto contexto.

Si queremos que un operador o función que no es miembro de una clase pueda acceder a
los miembros públicos, privados o protegidos deberemos declarar a dicho operador o
función como amigo (friend) dentro de la clase específica. Para mostrar un ejemplo
modificaremos la clase Pareja, donde sus atributos serán privados.

// Titulo..: programa sobrecarga04.cpp


// Objetivo: demostración de sobrecarga de operadores
// Autor...: Oscar E. Palacios
#include <iostream>
using namespace std;

class Pareja {

private:
double a, b;

public:
// constructor parametrizado
Pareja(const double a,const double b)
{
this->a = a;
this->b = b;
}

friend Pareja& operator +(const Pareja &p1,const Pareja &p2);


friend ostream& operator << (ostream &o,const Pareja &p);
};

// Sobrecarga del operador + para la clase Pareja


Pareja& operator +(const Pareja &p1,const Pareja &p2)
{
return *(new Pareja(p1.a + p2.a, p1.b + p2.b) );
}

// Sobrecarga del operador << para la clase Pareja


ostream& operator << (ostream &o,const Pareja &p)
{
o << "(" << p.a << ", " << p.b << ")";
return o;
}

int main()
{
Pareja A(50, 75 );
Pareja B(150, 175 );
Pareja C = A + B;

cout << "A = " << A << "\n";


cout << "B = " << B << "\n";
cout << "C = " << C << "\n";

return 0;
}

Sobrecarga de operadores dentro de una clase


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

1. Los operadores binarios se declaran con un solo parámetro, ya que el primer


parámetro es pasado por el programa como this, es decir, un puntero al mismo
objeto.
2. Los operadores unarios se declaran sin paramétros, ya que el único parámetro es
pasado por el programa como this.

Nota:
Los operadores binarios son aquellos que poseen dos partes ( izquierda y
derecha),
por ejemplo, una operación de suma requiere dos operandos ( o1 + o2 ).

Los operadores unarios son aquellos que poseen solo una parte, por
ejemplo, una operación
de incremento ( o1 ++ ).

Con el propósito de ver un ejemplo práctico vamos a retomar una vez más la tan famosa
clase Pareja, salvo que en esta ocasión vamos a sobrecargar dentro de la misma a los
operadores binarios: + (suma), - (resta), * (multiplicación) y / (división); el operador de
asignación (=); el operador de incremento (++) y el operador de comparación (==).

// Programa: sobrecarga05.cpp
// Objetivo: mostrar sobrecarga de operadores
// Autor...: Oscar Edmundo Palacios.
#include <iostream>

using namespace std;

class Pareja {
private:
int a, b;

public:
// constructor base
Pareja() : a(0), b(0) {}

// constructor parametrizado
Pareja(const int a,const int b) {
this->a = a;
this->b = b;
}

// constructor de copia
Pareja(const Pareja&);

// operadores miembros
Pareja& operator + (const Pareja &p);
Pareja& operator - (const Pareja &p);
Pareja& operator * (const Pareja &p);
Pareja& operator / (const Pareja &p);
Pareja& operator = (const Pareja &p);
Pareja& operator ++();
bool operator ==(const Pareja &p) const;

// operadores no miembros
friend ostream& operator << (ostream &o,const Pareja &p);
friend istream& operator >> (istream &o, Pareja &p);
};

// implementacion de los operadores para la clase Pareja


//....................................
Pareja::Pareja(const Pareja &p)
{
*this=p;
}
//....................................
Pareja& Pareja::operator + (const Pareja &p)
{
this->a += p.a;
this->b += p.b;
return *this;
}
//....................................
Pareja& Pareja::operator - (const Pareja &p)
{
this->a -= p.a;
this->b -= p.b;
return *this;
}
//....................................
Pareja& Pareja::operator * (const Pareja &p)
{
this->a *= p.a;
this->b *= p.b;
return *this;
}
//....................................
Pareja& Pareja::operator / (const Pareja &p)
{
if (p.a != 0) this->a /= p.a;
if (p.b != 0) this->b /= p.b;
return *this;
}
//....................................
Pareja& Pareja::operator = (const Pareja &p)
{
if(this!=&p){ //Comprueba que no se esté intentanod igualar un objeto
a sí mismo
if (p.a != 0) this->a = p.a;
if (p.b != 0) this->b = p.b;
}
return *this;
}

//....................................
Pareja& Pareja::operator ++ ()
{
this->a ++;
this->b ++;
return *this;
}

//....................................
bool Pareja::operator == (const Pareja &p) const
{
return this->a == p.a && this->b == p.b;
}

// implemetaci¢n de operadores no miembros


ostream& operator << (ostream &o,const Pareja &p)
{
o << "(" << p.a << ", " << p.b << ")";
return o;
}

istream& operator >> (istream &i, Pareja &p)


{
cout << "Introducir valores para ( a, b) :";
i >> p.a >> p.b;
i.ignore();
return i;
}

// prueba para la clase Pareja


int main()
{
Pareja A(50, 75);
Pareja B(100, 15);
Pareja C;

cout << "A = " << A << "\n";


cout << "B = " << B << "\n";
cout << "........................." << endl;
C = A * B;
cout << "A = " << A << "\n";
cout << "C = " << C << endl;
cout << "........................." << endl;

++C;
cout << "C = " << C << endl;
cout << "A == B " << ( (A==B) ? "Si": "No" );
cin.get();
return 0;
}

Das könnte Ihnen auch gefallen