Está en la página 1de 98

LENGUAJE DE PROGRAMACIÓN

ORIENTADA A OBJETOS
DEV C++
Dev-C++ es un IDE (entorno de desarrollo integrado) que facilita herramientas para la creación y depuración
de programas en C y en C++. Además, la instalación estándar cuenta también con un sistema de compilación
para los lenguajes C y C++ (el MinGW) que se instala conjuntamente y se puede utilizar de una forma
transparente para el usuario. Dev-C++ (y Mingw) son distribuidos gratuitamente y se pueden conseguir en la
página web

Entre sus principales características está que permite crear fácilmente distintos tipos de aplicaciones (de
consola, DLL’s, etc.), así como proyectos de C y C++ indistintamente, permite editar los archivos de la
compilación, añadir librerías a proyectos concretos, ....

Interfaz de Dev-C++
Al ejecutar Dev-C++ por primera vez, uno puede escoger, entre otras opciones, el idioma de instalación,
ciertas opciones de compleción automática de funciones y métodos disponibles en las librerías estándar de
C y C++, y el aspecto externo del interfaz de Dev-C++.

Una vez configuradas estas opciones, al ejecutar Dev-C++ por primera vez, se encuentra un entorno de
trabajo similar al siguiente:

Desde este entorno está permitido abrir, modificar y guardar tanto proyectos como archivos desarrollados
en C y C++.

En el explorador de proyectos y clases


(Opción Ver -> Explorador de Proyectos y Clases) que aparece en la columna izquierda, aparece tanto la
estructura del proyecto como de las clases y sus métodos definidos en nuestros ficheros. En ella aparecerán
tanto el proyecto principal como los ficheros del mismo que se encuentran abiertos en un momento
determinado. Una de las mayores restricciones de Dev-C++ es que sólo permite tener un proyecto abierto
en cada IDE. Si queremos trabajar con varios proyectos al mismo tiempo, tendremos que abrir tantas veces
el IDE como proyectos queramos gestionar.

El espacio central del entorno de trabajo está reservado para mostrar los ficheros en C o C++ con los que
nos encontramos trabajando.

1
La parte inferior de la interfaz gráfica
(Opción Ver -> Explorador de Proyectos Suelto (no anclado)) es la que utiliza el programa para devolver los
resultados de la compilación y depuración de los proyectos.

Ahora trataremos de ilustrar la utilización del interfaz por medio de la creación de un nuevo proyecto:

Creación de un nuevo proyecto


Una vez situados dentro de la aplicación, la forma de crear un proyecto es:

Archivo -> Nuevo -> Proyecto


Tras realizar dicha operación se obtiene el siguiente cuadro de diálogo, en el cual deberemos elegir qué tipo
de proyecto queremos crear, y si el mismo es de C o de C++:

En la pestaña Basic nos encontramos con 5 opciones (generalmente, el lenguaje de programación elegido
por nosotros durante el curso, salvo que sea explícitamente indicado, será En C++, aunque también se
puede elegir En C):

Windows Application: para crear aplicaciones para Windows (por defecto se crea un archivo de nombre
main.cpp que contiene algunas órdenes básicas que deberían ir incluidas en un programa que se vaya a
ejecutar en un entorno Windows)

Console Application: para crear aplicaciones que abren una consola de MSDOS al terminar su ejecución (por
defecto se crea un archivo main.cpp que contiene algunas órdenes básicas que permiten mostrar una
consola de MSDOS)

Static Library: creación de una librería estática (archivo .a)

DLL: para crear DLL’s (Dynamic Link Library, "Biblioteca de vínculos dinámicos", es un archivo que contiene
funciones que se pueden llamar desde aplicaciones u otras DLL) en C o C++.

Empty Project: para proyectos que el usuario va a definir completamente.


(Esta será la opción que utilizaremos nosotros más comúnmente)

En el campo Nombre del Proyecto podemos elegir la denominación que le daremos a nuestro proyecto. En
caso contrario, Dev-C++ le asignará un nombre automáticamente.

Hay algunas opciones adicionales en las pestañas de la ventana que no trataremos ahora.
Con todas estas decisiones se crea un nuevo proyecto que inicialmente estará formado por las herramientas
que considera Dev-C++ básicas para crear el tipo de proyecto que nosotros hemos elegido. Por ejemplo, en
el caso de que nosotros creemos un proyecto llamado Hola Mundo que sea del tipo Console Application
Y En C++ (que se correspondería con el siguiente cuadro de diálogo:

2
El proyecto que generará automáticamente Dev-C++ tendrá el siguiente aspecto:

Vemos que se ha creado, en primer lugar, un proyecto llamado Hola Mundo, y que por defecto, Dev-C++ le
ha añadido un archivo main.cpp con la información más básica para crear una aplicación que trabaje a
través de la ventana de comandos de MS-DOS.

Modificación de proyectos
Una vez que hemos definido un proyecto, el entorno de Dev-C++ posee multitud de herramientas que
permiten modificar el mismo. Las más importantes son las que nos permiten añadir nuevos archivos o
archivos ya existentes a nuestro proyecto, así como quitar archivos al mismo, o modificarlas propiedades
del proyecto (por ejemplo, el nombre). Estas opciones pueden ser modificadas con el puntero del ratón
situado encima del icono del proyecto en la ventana del Explorador de Proyectos/Clases:

3
o también directamente desde la pestaña Proyecto de la ventana del Dev-C++, con la opción Nuevo Código
Fuente:

Los diversos comandos que aparecen en la pestaña nos permiten:

Nuevo Código Fuente: Añadir un archivo nuevo (en blanco) a nuestro proyecto.
Añadir a Proyecto: Añadir un archivo ya existente de C, C++, o de cabeceras (un .h) a nuestro proyecto.
Quitar del Proyecto: Permite eliminar un archivo de nuestro proyecto (pero no borra ese archivo).
Opciones del Proyecto: aquí se pueden modificar múltiples opciones, como el nombre del proyecto, su tipo,
añadir nuevos directorios al mismo, instrucciones al compilador, y hasta modificar el icono del mismo.

Grabar un proyecto
Para grabar un proyecto en Dev-C++ la opción que hemos de utilizar es Archivo -> Guardar Proyecto como,
que nos permite grabar un proyecto, incluso cambiándole el nombre. Al mismo tiempo, Dev-C++ nos
ofrecerá la opción de grabar todos aquellos archivos pertenecientes al mismo que hayan sido modificados.

Grabar los archivos de un proyecto


Del mismo modo, se pueden grabar los distintos archivos que forman parte de un proyecto por medio de la
opción Archivo -> Guardar mientras alguno de los archivos está seleccionado (y ha sido previamente
modificado). Por ejemplo, la siguiente imagen muestra como podríamos guardar el archivo main.cpp de
nuestro proyecto tras introducir algún cambio en el mismo:

4
El comando
Archivo -> Guardar Como. Permite modificar el nombre del archivo a la vez que lo salva.

Finalmente, el comando
Archivo -> Guardar Todo. Salva todos los archivos que hayan sido modificados durante nuestra sesión de
trabajo.

Compilación y ejecución de proyectos


Una vez que hemos generado un proyecto y hemos sido capaces de salvar el mismo, el próximo paso será
intentar compilarlo y ejecutarlo. El sistema de compilación que utiliza Dev-C++ (por defecto) es MinGW1,
que consiste en un conjunto de herramientas incluyendo los compiladores gcc y g++, así como un sistema
de cabeceras y librerías que permiten crear aplicaciones para Windows que no requieren de DLL’s externas.

Al crear un proyecto, o modificando sus propiedades, se puede definir si un proyecto va a ser desarrollado
en C o en C++, y de esa forma Dev-C++ decide si debe aplicar el compilador presente en MinGW para C (gcc)
o el de C++ (g++), así como las órdenes de “linkado” del mismo; por lo tanto, a la hora de ejecutar, nosotros
como usuarios todo lo que debemos hacer es activar la opción Ejecutar -> Compilar como se observa en la
siguiente imagen:

o directamente dirigirnos al icono de acceso rápido en la barra de herramientas Compilar y


Ejecutar.

En Dev-C++ el proceso de compilación y “linkado” se tratan como si fueran sólo uno, y el programador no
puede intervenir durante el mismo.

Si el resultado de la compilación y el “linkado” es satisfactorio, Dev-C++ generará un archivo ejecutable con


el nombre del proyecto activo (en este caso, Hola Mundo.exe).

En caso contrario, la pestaña Resultado de la compilación se desplegará mostrando los fallos que se han
encontrado en el proceso de compilación y “linkado”.

Si el proyecto ha sido compilado satisfactoriamente, Dev-C++ habrá generado varios ficheros auxiliares en la
misma carpeta que se encuentran el proyecto y sus archivos. Por ejemplo, por cada archivo fuente de

5
código C o C++, la compilación genera un archivo con el mismo nombre y extensión “.o”, un fichero objeto,
que luego son linkados (junto con las librería necesarias) para crear el ejecutable. Por ejemplo, en nuestro
caso, del archivo main.cpp se generará un archivo objeto de nombre main.o.

El ejecutable generado (Hola Mundo.exe) puede ser utilizado independientemente de los archivos del
proyecto. Su ejecución se puede hacer a través de la pestaña Ejecutar -> Ejecutar como se muestra en la
siguiente imagen:

o también por medio del botón de la barra de herramientas Compilar y Ejecutar.

Otras herramientas
Dev-C++ ofrece otras muchas opciones al usuario en los distintos menús, del estilo de las que se pueden
encontrar en cualquier IDE, como el menú “Edición”, que cuenta con las opciones habituales de “Cortar”,
“Copiar”, “Seleccionar todo”, ...proyectos. En el menú “Ver” se nos permite modificar las barras de
herramientas visibles en el entorno de trabajo. En “Herramientas” podemos modificar algunas de las
opciones referentes al compilador (aunque generalmente dejaremos las que tiene por defecto) o a la
interfaz con el usuario.

6
7
PROGRAMACIÓN ORIENTADA A OBJETOS

La programación orientada a objetos (POO), permite descomponer un problema en subgrupos


relacionados (método: divide y vencerás). Cada subgrupo pasa a ser un objeto auto contenido que contiene
sus propias instrucciones y datos que le relacionan con ese objeto. Todos los lenguajes POO comparten tres
características:

Encapsulación, Polimorfismo y Herencia.

ENCAPSULACIÓN: Es el mecanismo que agrupa el código y los datos que maneja. Los mantienen protegidos
frente a cualquier interferencia y mal uso.

Cuando el código y los datos están enlazados de esta manera se ha creado un objeto. Ese código y datos
pueden ser privados para ese objeto o públicos para otras partes del programa.

POLIMORFISMO: Es la cualidad que permite que un nombre se utilice para dos o más propósitos
relacionados pero técnicamente diferentes. El propósito es poder usar un nombre para especificar una clase
general de acciones.

Por ejemplo en C tenemos tres funciones distintas para devolver el valor absoluto. Sin embargo en C++
incorpora Polimorfismo y a cada función se puede llamar abs(). El Polimorfismo se puede aplicar tanto a
funciones como a operadores.

HERENCIA: Proceso mediante el cual un objeto puede adquirir las propiedades de otro objeto. La
información se hace manejable gracias a la clasificación jerárquica.

OBJETO: Conjunto de variables y funciones pertenecientes a una clase encapsulados. A este


encapsulamiento es al que se denomina objeto. Por tanto la clase es quien define las características y
funcionamiento del objeto.

MENSAJE: Representa una acción a tomar por un determinado objeto. En el ejemplo anterior, la orden "que
comience la proyección de la película" podría ser un mensaje dirigido a un objeto del tipo "sala de cine".
Otro mensaje podría ser la orden de "desalojo de la sala" en funciones no-continuas. Notemos que el
mensaje se refiere únicamente a la orden, y no tiene que ver con la forma como ésta es respondida.

CLASE: Equivale a la generalización o abstracción de un tipo específico de objetos. Los polígonos con tres
lados iguales podrían ser generalizados, por ejemplo, en una clase que llamaremos "triángulo Equilátero".
INSTANCIA: Es la concreción de una clase. Una instancia de la clase "SalaDeCine" sería, por ejemplo, el
objeto "cineParadox". El concepto de instancia, en realidad, une la noción de objeto con la de clase. Esto
quiere decir que en el esquema de OOP, a semejanza de lo que ocurre en el mundo real (aunque
normalmente de forma inadvertida), debemos identificar en primer lugar una abstracción general, delimitar
las características comunes de un grupo de objetos, y luego poner nombre (o identificar especialmente) a
uno o más de ellos.

8
MÉTODO: Consiste en la implementación en una clase de un protocolo de respuesta a los mensajes
dirigidos a los objetos de la misma. La respuesta a tales mensajes puede incluir el envío por el método de
mensajes al propio objeto y aun a otros, también como el cambio del estado interno del objeto.

En C++ los métodos están implementados como DEFINICIONES de funciones miembro de una clase,
representando el conjunto de mensajes al que los objetos de tal clase pueden responder. Revisando el
ejemplo que examinábamos al hablar de "mensajes", podemos decir que la respuesta al mensaje "empezar
la Proyección" podría significarse en la serie de instrucciones tendentes a efectuar la acción de visionado de
la película: apagar las luces, encender el proyector, etc.

ENVÍO DE MENSAJES A OBJETOS


Hemos visto que mientras que la programación estructurada se basa, sustancialmente, en llamadas de alto
nivel a determinadas funciones y rutinas, la OOP consiste, básicamente, en las relaciones entre objetos
(instancias de clases en C++) que responden a mensajes de acuerdo con los métodos (funciones miembro en
C++) establecidos en el protocolo de descripción de sus respectivas clases. Bueno, esto se va complicando,
pero es inevitable: no se puede evitar indefinidamente la endiablada terminología "de objetos". Y no hemos
hecho más que empezar.

El "envío de un mensaje" a un objeto equivale, en C++, como ya he indicado, a una llamada a una función
miembro de la clase correspondiente al objeto. En realidad las expresiones "métodos", "envío de
mensajes", "instancias de variables", etc. pertenecen originariamente al lenguaje
Smalltalk. Volvamos al ejemplo del cine y "lancemos mensajes":

SalaDeCine cineParadox;
SalaDeCine *punteroASalaDeCine;

“Todo programa hecho en C puede correr en C++, pero no todos los programas hechos en C++ corren en
C, para empezar habría que cambiar su extensión de .cpp a .c”

9
CARACTERÍSTICAS DE C++.

Comenzaremos con algunas características de C++ que no tienen que ver directamente con la programación
orientada a objetos.

En C++ los comentarios pueden ser además de una sola línea:

// este es un comentario en C++

Acabando el comentario al final de la línea, lo que quiere decir que el programador no se preocupa por
cerrar el comentario.

FLUJO DE ENTRADA/SALIDA

En C, la salida y entrada estándar estaba dada por printf y scanf principalmente (o funciones similares) para
el manejo de los tipos da datos simples y las cadenas. En C++ se proporcionan a través de la librería
iostream.h, la cual debe ser insertada a través de un #include. Las instrucciones son:

 cout Utiliza el flujo salida estándar. Que se apoya del operador <<, el cual se conoce como operador de
inserción de flujo "colocar en"

 cin Utiliza el flujo de entrada estándar. Que se apoya del operador >>, conocido como operador de
extracción de flujo "obtener de"

Los operadores de inserción y de extracción de flujo no requieren cadenas de formato (%s, %f), ni
especificadores de tipo. C++ reconoce de manera automática que tipos de datos son extraídos o
introducidos.

En el caso del operador de extracción (>>) no se requiere el operador de dirección &.

De tal forma un código de desplegado con printf y scanf de la forma:

printf("Número: ");
scanf("%d", &num);
printf("El valor leído es: " %d\n", num);

Sería en C++ de la siguiente manera:

cout << "Número";


cin >> num;
cout << "El valor leído es: " << num << '\n';

10
REGLAS DE ALCANCE DE UNA VARIABLE

El alcance de un identificador o variable es la porción del programa en el cual dicho identificador puede ser
referenciado. Cuando declaramos una variable local en un bloque, esta puede ser referenciada solamente
en dicho bloque o en los bloques anidados dentro de dicho bloque.

<tipo> <nombre1>=<valor>,<nombre2>=<valor>,<nombre3>=<valor>;

Tipo de datos:
TIPO TAMAÑO RANGO DE VALORES
char 1 byte -128 a 127
int 2 bytes -32768 a 32767
float 4 bytes 3'4 E-38 a 3'4 E+38
doubl 8 bytes 1'7 E-308 a 1'7 E+308
é
Calificadores de tipo
Los calificadores de tipo tienen la misión de modificar el rango de valores de un determinado tipo de
variable. Estos calificadores son cuatro:

Signen: Le indica a la variable que va a llevar signo. Es el utilizado por defecto.

Tamaño Rango de valores


signed 1 byte -128 a 127
char
signed int 2 bytes -32768 a 32767

Unsigned: Le indica a la variable que no va a llevar signo (valor absoluto).

Tamaño Rango de valores


unsigned char 1 byte 0 a 255
unsigned int 2 bytes 0 a 65535

Short: Rango de valores en formato corto (limitado). Es el utilizado por defecto.

Tamaño Rango de valores


short 1 byte -128 a 127
char
short int 2 bytes -32768 a 32767

Long: Rango de valores en formato largo (ampliado).

Tamaño Rango de valores


long char 4 bytes -2.147.483.648 a 2.147.483.647
long int 10 bytes -3'36 E-4932 a 1'18 E+4932

11
USO DE LOS APUNTADORES EN C:

Un Apuntador es una variable que contiene una dirección de memoria, la cual corresponderá a un dato
o a una variable que contiene el dato. Los apuntadores también deben de seguir las mismas reglas que
se aplican a las demás variables, deben tener nombre únicos y deben de declararse antes de usarse
Operadores de Indirección y Dirección.

Hay 2 operadores que se usan cuando trabajan con direcciones en un programa C; el Operador de
Indirección ( * ) y el de Dirección (&). Estos operadores son diferentes de los tratados anteriormente.

El Operador de Dirección (&) regresa la dirección de una variable. Este operador está asociado con la
variable a su derecha: &h; Esta línea regresa la dirección de la variable h.
El Operador de Indirección (*) trabaja a la inversa del operador de Dirección.

También está asociado con la variable a su derecha, toma la dirección y regresa el dato que contiene esa
dirección. Por ejemplo, la siguiente línea determina la dirección de la variable h y luego usa el operador
de Indirección para accesar la variable y darle un valor de 42.

Ejemplo: *(&h)=42;

La declaración de un puntero de manera general es:

Tipo *nombre de apuntador;

Tipo : Especifica el tipo de objeto apuntado y puede ser cualquier tipo.


Nombre de apuntador: Es el identificador del apuntador

Instrucción estática->consola: (ie->c)


En los programas de C algunas veces después de ejecutar el programa la consola se cierra muy rápido y
no permite visualizar en un tiempo razonable el resultado del programa por consola, entonces es
conveniente agregar al final de este una instrucción de modo que la consola dure un tiempo adecuado
abierta esta es:

system("PAUSE");
return EXIT_SUCCESS;

Esta se coloca justo antes de la última llave del main.

Programa estándar hecho en C++

#include <stdlib.h> //libreria de C


#include <iostream>
#include <stdio.h> //libreria de C
#include <cstdlid>
/*variables globales, estructuras, funciones, clases, acciones*/

12
using namespace std; // (ie->c)
main(int argc, char *argc) // main() en C/C++ da igual
{
/*todo el bloque del programa*/
system("PAUSE"); // (ie->c)
return EXIT_SUCCESS; //puede no ser necesaria aveces(ie->c)
}

/*se hace un enter después de la última llave para indicar un “separador” al compilador de C++, en la
mayoría de los casos, si el programa se hace en lenguaje C++ el cuerpo de las funciones se declaran arriba
del cuerpo del main() desde donde son llamadas*/

CÓMO CREAR NUEVOS TIPOS DE DATOS EN C++

C++ proporciona la capacidad de crear nuevos tipos definidos por el usuario mediante el uso de la palabra
reservada enum, la palabra reservada struct, la palabra reservada union y la palabra reservada class.

Por ejemplo, las enumeraciones en C++ son declaradas mediante la palabra enum y se convierte en un tipo
de dato nuevo. Luego, para declarar una variable del nuevo tipo de dato ya no es necesaria la palabra
reservada enum, sino que se utiliza directamente el nombre del nuevo tipo de dato.

Ejemplo:

enum boolean {FALSE, TRUE};


struct Nombre {char primer [10];char segundo[10];};

DIRECTIVAS DEL PREPROCESADOR-> TYPEDEF

La palabra reservada typedef proporciona un mecanismo para la creación de sinónimos o alias para tipos de
datos anteriormente definidos. Al crear un nuevo nombre utilizando typedef no se crea un nuevo tipo;
typedef simplemente crea un nuevo nombre de tipo que puede ser utilizado como un seudónimo para un
nombre de tipo ya existente, lo cual ayuda a autodocumentar el programa.

Ejemplo:
typedef int IntArray10 [10];

La directiva del preprocesador-> #include

La directiva del preprocesador #include permite que se incluya una copia del archivo especificado. Las dos
formas de la directiva son:

#include <filename>
#include “filename”

13
La diferencia entre estas formas estriba en la localización donde el preprocesador buscará el archivo a
incluirse. Si el nombre del archivo está encerrado entre comillas, el preprocesador buscará el archivo en el
mismo directorio que el archivo que se está compilando (este método se utiliza normalmente para incluir
archivos de cabecera definidos por el programador). Si el nombre del archivo está encerrado entre
corchetes angulares (< y >) la búsqueda se llevará a cabo en los directorios predesignados (por ejemplo
cuando se incluyen los archivos de cabecera de la biblioteca estándar).

ARREGLOS UNIDIMENSIONALES (VECTORES)


Un vector es un arreglo unidimensional, es decir, sólo utiliza un índice para referenciar a cada uno de los
elementos. Su declaración será:

<tipo> <nombre> [<tamaño>]; //tamaño es una variable por decir “x”

El tipo puede ser cualquiera de los ya conocidos y el tamaño indica el número de elementos del vector (se
debe indicar entre corchetes [ ]). En el ejemplo se puede observar que la variable i es utilizada como índice,
el primer for sirve para rellenar el vector y el segundo para visualizarlo.
Como se aprecia, las posiciones van de 0 a 9 (total 10 elementos).

Ejemplo:
/* Declaración de un arreglo. */

#include <stdio.h>
main()
{
int vector[10],i;
for (i=0;i<10;i++) vector[i]=i;
for (i=0;i<10;i++) printf(" %d", vector[i]);
}

Podemos inicializar (asignarle valores) un vector en el momento de declararlo(igual que en Java). Si lo


hacemos así no es necesario indicar el tamaño. Su sintaxis es:

<tipo> <nombre> []={<valor_1>,<valor_2>,...}

Ejemplos:

int vector[]={1,2,3,4,5,6,7,8};
char vector[]="programador";
char vector[]={'p','r','o','g','r','a','m','a','d','o','r'};

Una particularidad con los vectores de tipo char (cadena de caracteres), es que deberemos indicar en que
elemento se encuentra el fin de la cadena mediante el caracter nulo (\0). Esto no lo controla el compilador,
y tendremos que ser nosotros los que insertemos este carácter al final de la cadena.

Por tanto, en un vector de 10 elementos de tipo char podremos rellenar un máximo de 9, es decir, hasta
vector[8]. Si sólo rellenamos los 5 primeros, hasta vector[4], debemos asignar el caracter nulo a vector[5].

Es muy sencillo:
vector[5]='\0';

14
Ahora veremos un ejemplo de cómo se rellena un vector de tipo char.

/* Vector de tipo char. */

#include <stdio.h>
main()
{
char cadena[20];
int i;
for (i=0;i<19 && cadena[i-1]!=13;i++)
cadena[i]=getche( );
if (i==19)
cadena[i]='\0';
else
cadena[i-1]='\0';
printf("\n%s",cadena);
}

Podemos ver que en el for se encuentran dos condiciones:

1.- Que no se hayan rellenado todos los elementos (i<19).


2.- Que el usuario no haya pulsado la tecla ENTER, cuyo código ASCII es 13.

También podemos observar una nueva función llamada getche( ), que se encuentra en conio.h. Esta función
permite la entrada de un caracter por teclado.

Después se encuentra un if, que comprueba si se ha rellenado todo el vector. Si es cierto, coloca el caracter
nulo en el elemento 20 (cadena[19]). En caso contrario tenemos el else, que asigna el caracter nulo al
elemento que almacenó el carácter ENTER.

En resumen: al declarar una cadena deberemos reservar una posición más que la longitud que queremos
que tenga dicha cadena.

LLAMADAS A FUNCIONES CON ARREGLOS

Como ya se comentó en el tema anterior, los arreglos únicamente pueden ser enviados a una función por
referencia. Para ello deberemos enviar la dirección de memoria del primer elemento del arreglo. Por tanto,
el argumento de la función deberá ser un puntero.

Ejemplo:

/* Envío de un arreglo a una función. */

#include <stdio.h>
void visualizar(int []); /* prototipo */
main()

15
{
int array[25],i;
for (i=0;i<25;i++)
{
printf("Elemento nº %d",i+1);
scanf("%d",&array[i]);
}
visualizar(&array[0]);
}
void visualizar(int array[]) /* desarrollo */
{
int i;
for (i=0;i<25;i++) printf("%d",array[i]);
}

En el ejemplo se puede apreciar la forma de enviar un arreglo por referencia. La función se podía haber
declarado de otra manera, aunque funciona exactamente igual:

declaración o prototipo
void visualizar(int *);
desarrollo de la función
void visualizar(int *arreglo)

ARREGLOS MULTIDIMENSIONALES (MATRICES)

Una matriz es un arreglo multidimensional. Se definen igual que los vectores excepto que se requiere un
índice por cada dimensión.

Su sintaxis es la siguiente:
<tipo> <nombre> [<tamaño_1>][<tamaño_2>],...,[<tamaño_n>];

Una matriz bidimensional se podría representar gráficamente como una tabla con filas y columnas. La
matriz tridimensional se utiliza, por ejemplo, para trabajos gráficos con objetos 3D. En el ejemplo puedes
ver como se rellena y visualiza una matriz bidimensional. Se necesitan dos bucles para cada una de las
operaciones. Un bucle controla las filas y otro las columnas.

Ejemplo:

/* Matriz bidimensional. */

#include <stdio.h>
main()
{
int x, i, numeros[3][4];

/* rellenamos la matriz */

16
for (x=0;x<3;x++)
for (i=0;i<4;i++)
scanf("%d",&numeros[x][i]);

/* visualizamos la matriz */

for (x=0;x<3;x++)
for (i=0;i<4;i++)
printf("%d",numeros[x][i]);
}

Si al declarar una matriz también queremos inicializarla, habrá que tener en cuenta el orden en el que los
valores son asignados a los elementos de la matriz.
Veamos algunos ejemplos:

int numeros[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};

Quedarían asignados de la siguiente manera:


numeros[0][0]=1
numeros[0][1]=2
numeros[0][2]=3
numeros[0][3]=4
numeros[1][0]=5
numeros[1][1]=6
numeros[1][2]=7
numeros[1][3]=8
numeros[2][0]=9
numeros[2][1]=10
numeros[2][2]=11
numeros[2][3]=12

También se pueden inicializar cadenas de texto:

char dias[7][10]={"lunes","martes", … ,"viernes","sábado","domingo"};

Para referirnos a cada palabra bastaría con el primer índice:

printf("%s",dias[i]);

REGISTROS

Concepto de registro
Un registro es un conjunto de una o más variables, de distinto tipo, agrupadas bajo un mismo nombre para
que su manejo sea más sencillo. Su utilización más habitual es para la programación de bases de datos, ya
que están especialmente indicadas para el trabajo con registros o fichas.

17
La sintaxis de su declaración es la siguiente:
struct <nombre_tipo_estructura>

{
<TDD> <nombre_variable1>;
<TDD> <nombre_variable2>;
<TDD> <nombre_variable3>;
};

Donde tipo_estructura es el nombre del nuevo tipo de dato que hemos creado. Por último, tipo_variable y
nombre_variable son las variables que forman parte del registro.

Para definir variables del tipo que acabamos de crear lo podemos hacer de varias maneras, aunque las dos
más utilizadas son éstas:

Una forma de definir el registro es:

struct trabajador
{
char nombre[20];
char apellidos[40];
int edad;
char puesto[10];
};
struct trabajador fijo, temporal;

Otra forma es:

struct trabajador
{
char nombre[20];
char apellidos[40];
int edad;
char puesto[10];
} fijo, temporal;

En el primer caso declaramos el registro, y en el momento en que necesitamos las variables, las declaramos.
En el segundo las declaramos al mismo tiempo que al registro. El problema del segundo método es que no
podremos declarar más variables de este tipo a lo largo del programa. Para poder declarar una variable de
tipo registro, el mismo tiene que estar declarado previamente. Se debe declarar antes de la función main.

El manejo de los registros es muy sencillo, así como el acceso a los campos (o variables) de estos registros.
La forma de acceder a estos campos es la siguiente:

<variable>.<campo>;
Donde variable es el nombre de la variable de tipo registro que hemos creado, y campo es el nombre de la
variable que forma parte del registro. Lo veremos mejor con un ejemplo basado en el registro definido
anteriormente:

18
temporal.edad = 25;
Lo que estamos haciendo es almacenar el valor 25 en el campo edad de la variable temporal de tipo
trabajador. Otra característica interesante de los registros es que permiten pasar el contenido de un registro
a otro, siempre que sean del mismo tipo naturalmente: (Véase: “Operaciones con Objetos en C++”)
fijo=temporal;
Al igual que con los otros tipos de datos, también es posible inicializar variables de tipo registro en el
momento de su declaración:

struct trabajador fijo={"Pedro","Hernández Suárez", 32, "gerente"};


Si uno de los campos del registro es un arreglo de números, los valores de la inicialización deberán ir entre
llaves:

struct notas
{
char nombre[30];
int notas[5];
};
struct notas alumno={"Carlos Pérez",{8,7,9,6,10}};

REGISTROS Y FUNCIONES

Podemos enviar un registro a una función de las dos maneras conocidas:

1. Por valor: su declaración sería:

void visualizar(struct trabajador);

Después declararíamos la variable fijo y su llamada sería:

visualizar(fijo);

Por último, el desarrollo de la función sería:

void visualizar(struct trabajador datos)

Ejemplo:

/* Paso de un registro por valor. */

#include <stdio.h>
struct trabajador
{
char nombre[20];
char apellidos[40];
int edad;
char puesto[10];
};
void visualizar(struct trabajador);

19
main()
{
struct trabajador fijo;
printf("Nombre: ");
scanf("%s",fijo.nombre);
printf("\nApellidos: ");
scanf("%s",fijo.apellidos);
printf("\nEdad: ");
scanf("%d",&fijo.edad);
printf("\nPuesto: ");
scanf("%s",fijo.puesto);
visualizar(fijo);
}
void visualizar(struct trabajador datos)
{
printf("Nombre: %s",datos.nombre);
printf("\nApellidos: %s",datos.apellidos);
printf("\nEdad: %d",datos.edad);
printf("\nPuesto: %s",datos.puesto);
}

2. Por referencia: su declaración sería:

void visualizar(struct trabajador*);

Después declararemos la variable fijo y su llamada será:


visualizar (&fijo);

Por último, el desarrollo de la función será:

void visualizar(struct trabajador *datos)

Fíjense que en la función visualizar, el acceso a los campos de la variable datos se realiza mediante el
operador ->, ya que lo tratamos con un puntero. En estos casos siempre utilizaremos el operador ->. Se
consigue con el signo menos seguido de mayor que.

Ejemplo:
/* Paso de un registro por referencia. */

#include <stdio.h>
struct trabajador
{
char nombre[20];
char apellidos[40];
int edad;
char puesto[10];
};
void visualizar(struct trabajador *);

20
main()
{
struct trabajador fijo;
printf("Nombre: ");
scanf("%s",fijo.nombre);
printf("\nApellidos: ");
scanf("%s",fijo.apellidos);
printf("\nEdad: ");
scanf("%d",&fijo.edad);
printf("\nPuesto: ");
scanf("%s",fijo.puesto);
visualizar(&fijo);
}
void visualizar(struct trabajador *datos)
{
printf("Nombre: %s",datos->nombre);
printf("\nApellidos: %s",datos->apellidos);
printf("\nEdad: %d",datos->edad);
printf("\nPuesto: %s",datos->puesto);
}

ARREGLOS DE REGISTROS
Es posible agrupar un conjunto de elementos de tipo registro en un arreglo.
Esto se conoce como arreglo de registros:

struct trabajador
{
char nombre[20];
char apellidos[40];
int edad;
};
struct trabajador fijo[20];

Así podremos almacenar los datos de 20 trabajadores. Ejemplos sobre como acceder a los campos y sus
elementos: para ver el nombre del cuarto trabajador, fijo[3].nombre;. Para ver la tercera letra del nombre
del cuarto trabajador, fijo[3].nombre[2];. Para inicializar la variable en el momento de declararla lo
haremos de esta manera:

struct trabajador fijo[20] = { {"José", "Herrero Martínez", 29 }, {


"Luis", "García Sánchez", 46 } };

Definición de tipos

El lenguaje 'C' dispone de una declaración llamada typedef que permite la creación de nuevos tipos de
datos. Ejemplos:

typedef int entero; /*acabamos de crear un tipo de dato llamado entero*/


entero a, b = 3; /*declaramos dos variables de este tipo*/

21
Su empleo con registros está especialmente indicado. Se puede hacer de varias formas:

Una forma de hacerlo:


struct trabajador
{
char nombre[20];
char apellidos[40];
int edad;
};
typedef struct trabajador datos;
datos fijo, temporal;

Otra forma:

typedef struct
{
char nombre[20];
char apellidos[40];
int edad;
}
trabajador datos;
datos fijo, temporal;

Archivos
Por último veremos la forma de almacenar datos que podremos recuperar cuando deseemos. Estudiaremos
los distintos modos en que podemos abrir un archivo, así como las funciones para leer y escribir en él.

APERTURA DE ARCHIVOS
Antes de abrir un archivo necesitamos declarar un puntero de tipo FILE, con el que trabajaremos durante
todo el proceso. Para abrir el archivo utilizaremos la función fopen().

Su sintaxis es:
FILE *<nombre_del_puntero>;
puntero = fopen (<nombre del archivo>, "<modo de apertura>" );

Donde puntero es la variable de tipo FILE, nombre del archivo es el nombre que daremos al archivo que
queremos crear o abrir. Este nombre debe ir encerrado entre comillas. También podemos especificar la ruta
donde se encuentra o utilizar un arreglo que contenga el nombre del archivo (en este caso no se pondrán
las comillas).

Algunos ejemplos:

puntero = fopen("DATOS.DAT","r");
puntero = fopen("C:\\TXT\\SALUDO.TXT","w");

Un archivo puede ser abierto en dos modos diferentes, en modo texto o en modo binario. A continuación lo
veremos con más detalle.

22
MODO TEXTO
W crea un archivo de escritura. Si ya existe lo crea de nuevo.
w crea un archivo de lectura y escritura. Si ya existe lo crea de nuevo.
+
a abre o crea un archivo para añadir datos al final del mismo.
a+ abre o crea un archivo para leer y añadir datos al final del mismo.
r abre un archivo de lectura.
r+ abre un archivo de lectura y escritura.

MODO BINARIO
Wb crea un archivo de escritura. Si ya existe lo crea de nuevo.
w+ crea un archivo de lectura y escritura. Si ya existe lo crea de nuevo.
b
ab abre o crea un archivo para añadir datos al final del mismo.
a+b abre o crea un archivo para leer y añadir datos al final del mismo.
Rb abre un archivo de lectura.
r+b abre un archivo de lectura y escritura.

La función fopen devuelve, como ya hemos visto, un puntero de tipo FILE. Si al intentar abrir el archivo se
produjese un error (por ejemplo si no existe y lo estamos abriendo en modo lectura), la función fopen
devolvería NULL. Por esta razón es mejor controlar las posibles causas de error a la hora de programar.

Un ejemplo:

FILE *pf;
pf=fopen("datos.txt","r");
if (pf == NULL) printf("Error al abrir el archivo");

La función freopen cierra el archivo apuntado por el puntero y reasigna este puntero a un archivo que será
abierto.

Su sintaxis es:
freopen(<nombre del archivo>,"<modo de apertura>",<nomb_puntero>);
Donde nombre del archivo es el nombre del nuevo archivo que queremos abrir, luego el modo de apertura,
y finalmente el puntero que va a ser reasignado.

CIERRE DE REGISTROS
Una vez que hemos acabado nuestro trabajo con un archivo es recomendable cerrarlo. Los archivos se
cierran al finalizar el programa pero el número de estos que pueden estar abiertos es limitado. Para cerrar
los archivos utilizaremos la función fclose();.

Esta función cierra el archivo, cuyo puntero le indicamos como parámetro. Si el archivo se cierra con éxito
devuelve 0.

23
fclose(<nomb_puntero>);

Un ejemplo ilustrativo es:

FILE *pf;
pf = fopen("AGENDA.DAT","rb");
if ( pf == NULL )
printf ("Error al abrir el archivo");
else
fclose(pf);

ESCRITURA Y LECTURA DE REGISTROS


A continuación veremos las funciones que se podrán utilizar dependiendo del dato que queramos escribir
y/o leer en el archivo.

Un carácter:

fputc(<variable_caracter>,<puntero_archivo>);

Escribimos un caracter en un archivo (abierto en modo escritura).

Un ejemplo:

FILE *pf;
char letra='a';
if (!(pf=fopen("datos.txt","w"))) /* forma de controlar si hay un
error */
{
printf("Error al abrir el archivo");
exit(0); /* abandonamos el programa */
}
else
fputc(letra,pf);
fclose(pf);

La función fgetc(<puntero_archivo>), lee un caracter de un archivo (abierto en modo lectura). Deberemos


guardarlo en una variable.

Un ejemplo:

FILE *pf;
char letra;
if (!(pf=fopen("datos.txt","r"))) /* controlamos si se produce un error */
{
printf("Error al abrir el archivo");
exit(0); /* abandonamos el programa */
}
else

24
{
letra=fgetc(pf);
printf("%c",letra);
fclose(pf);
}

UN NÚMERO ENTERO:

putw(<variable_entera>,<puntero_archivo>);

Escribe un número entero en formato binario en el archivo.

Ejemplo:

FILE *pf;
int num=3;
if (!(pf=fopen("datos.txt","wb"))) /* controlamos si se produce un
error */
{
printf("Error al abrir el archivo");
exit(0); /* abandonamos el programa */
}
else
{
fputw(num,pf); /* también podíamos haber hecho:
fputw(3,pf); */
fclose(pf);
}

La función getw( puntero_archivo ), lee un número entero de un archivo, avanzando dos bytes después de
cada lectura.

Un ejemplo:

FILE *pf;
int num;
if (!(pf=fopen("datos.txt","rb"))) /* controlamos si se produce un error */
{
printf("Error al abrir el archivo");
exit(0); /* abandonamos el programa */
}
else
{
num=getw(pf);
printf("%d",num);
fclose(pf);
}

25
UNA CADENA DE CARACTERES:

fputs(<variable_array>,<puntero_archivo>);

Escribe una cadena de caracteres en el archivo.

Ejemplo:

FILE *pf;
char cad="Me llamo Vicente";
if (!(pf=fopen("datos.txt","w")))
/* controlamos si se produce un error */
{
printf("Error al abrir el archivo");
exit(0); /* abandonamos el programa */
}
else
{
fputs(cad,pf);
/* o también así: fputs("Me llamo Vicente",pf); */
fclose(pf);
}

La función fgets( variable_array, variable_entera, puntero_archivo ), lee una cadena de caracteres del
archivo y la almacena en variable_array. La variable_entera indica la longitud máxima de caracteres que
puede leer.

Un ejemplo:

FILE *pf;
char cad[80];
if (!(pf=fopen("datos.txt","rb")))
/* controlamos si se produce un error */
{
printf("Error al abrir el archivo");
exit(0); /* abandonamos el programa */
}
else
{
fgets(cad,80,pf);
printf("%s",cad);
fclose(pf);
}

26
Con formato:
fprintf(<puntero_archivo>,<formato>,<argumentos>);

Funciona igual que un printf pero guarda la salida en un archivo.

Ejemplo:
FILE *pf;
char nombre[20]="Santiago";
int edad=34;
if (!(pf=fopen("datos.txt","w")))
/* controlamos si se produce un error */
{
printf("Error al abrir el archivo");
exit(0); /* abandonamos el programa */
}
else
{
fprintf(pf,"%20s%2d\n",nombre,edad);
fclose(pf);
}
La función fscanf(<puntero_archivo>,<formato>,<argumentos>), lee los argumentos del archivo. Al igual
que con un scanf, deberemos indicar la dirección de memoria de los argumentos con el símbolo &
( ampersand ).

Un ejemplo:

FILE *pf;
char nombre[20];
int edad;
if (!(pf=fopen("datos.txt","rb")))
/* controlamos si se produce un error */
{
printf("Error al abrir el archivo");
exit(0); /* abandonamos el programa */
}
else
{
fscanf(pf,"%20s%2d\",nombre,&edad);
printf("Nombre: %s Edad: %d",nombre,edad);
fclose(pf);
}

REGISTROS

fwrite( *buffer,<tamaño>,<nº de veces>,<puntero_archivo>);

27
Se utiliza para escribir bloques de texto o de datos, registros, en un archivo. En esta función, *buffer será la
dirección de memoria de la cual se recogerán los datos; tamaño, el tamaño en bytes que ocupan esos datos
y nº de veces, será el número de elementos del tamaño indicado que se escribirán.

fread( *buffer,<tamaño>,<nº de veces>,<puntero_archivo>);

Se utiliza para leer bloques de texto o de datos de un archivo. En esta función, *buffer es la dirección de
memoria en la que se almacenan los datos; tamaño, el tamaño en bytes que ocupan esos datos y nº de
veces, será el número de elementos del tamaño indicado que se leerán.

Otras funciones para archivos:


rewind( <puntero_archivo> ): Sitúa el puntero al principio del archivo.
fseek(<puntero_archivo>,long <posición>,int <origen>): Sitúa el puntero en la posición que le indiquemos.
Como origen podremos poner:

0 = SEEK_SET, el principio del archivo


1 = SEEK_CUR, la posición actual
2 = SEEK_END, el final del archivo

rename(<nombre1>,<nombre2>): Su función es exactamente la misma que la que conocemos en MS-DOS.


Cambia el nombre del archivo nombre1 por un nuevo nombre, nombre2.

remove(<nombre>): Como la función del DOS del, podremos eliminar el archivo indicado en nombre.

DETECCIÓN DE FINAL DE ARCHIVO


La función feof(<puntero_archivo>), siempre deberemos controlar si hemos llegado al final de archivo
cuando estemos leyendo, de lo contrario podrían producirse errores de lectura no deseados. Para este fin
disponemos de la función

feof(). Esta función retorna 0 si no ha llegado al final, y un valor diferente de 0 si lo ha alcanzado.


Pues con esto llegamos al final del tema. Espero que no haya sido muy pesado. No es necesario que te
aprendas todas las funciones de memoria. Céntrate sobre todo en las funciones fputs(), fgets(), fprintf(),
fwrite() y fread(). Con estas cinco se pueden gestionar los archivos perfectamente.

Prototipos de función y verificación de tipo de dato


Los prototipos de función le permiten al compilador verificar por tipo la exactitud de las llamadas de
función. En C++ los prototipos de función son requeridos para todas las funciones. Sin embargo, una función
definida en un archivo, antes de cualquier llamada a la misma, no requiere de un prototipo de función, ya
que el encabezado de función actúa como tal. Todos los parámetros de la función deberán declararse entre
los paréntesis del prototipo y de la definición de función. En caso que no existan parámetros, bastará con
colocar paréntesis vacíos.

Ejemplo:
int potencia (int, int);

Operador de resolución de alcance unario

28
Es posible declarar variables locales y globales con un mismo nombre. El operador de resolución de alcance
unario (::) permite tener acceso a una variable global cuando está en alcance una variable local con el
mismo nombre. El operador de resolución de alcance unario no puede ser utilizado para tener acceso a una
variable con el mismo nombre en un bloque externo. Se puede tener acceso a una variable global de forma
directa, sin usar el operador de resolución de alcance unario, siempre que no exista una variable local en
alcance con el mismo nombre.

Cómo llamar funciones: parámetros por valor y parámetros por referencia.


Cuándo los argumentos se pasan en una llamada de una función por valor se efectúa una copia del valor del
argumento y este se pasa a la función llamada. Las modificaciones a la copia no afectarán el valor original de
la variable del llamador.

Cuando un argumento es pasado por referencia, el compilador pasa la dirección de memoria del valor del
parámetro a la función. Cuando se modifica el valor del parámetro (la variable local), este valor queda
almacenado en la misma dirección de memoria, por lo que al retornar a la función llamadora la dirección de
memoria donde se almacenó el parámetro contendrá el valor modificado. Para declarar una variable
parámetro como paso por referencia, el símbolo & debe preceder al nombre de la variable. Por ejemplo la
función de intercambio, que intercambia los valores de dos variables:

void intercambio ( int &a, int &b)


{
int aux = a;
a = b;
b = aux;
}
Los parámetros a y b son parámetros por referencia. Por consiguiente, cualquier cambio efectuado en el
interior de la función se transmitirá al exterior de la misma.

Para llamar a la función intercambio () por referencia, simplemente se pasan las variables a intercambiar,
por ejemplo:

Ejemplo:

int i = 3, j = 50;
intercambio (i, j);

También un parámetro por referencia es un seudónimo (“alias”) de su argumento correspondiente. Para


indicar que un parámetro de función es pasado por referencia, solo hay que colocar un ampersand (&)
después del tipo del parámetro en el prototipo de función.

Debemos usar apuntadores para pasar argumentos que pudieran ser modificados por la función llamada, y
usar referencias a constantes para pasar argumentos extensos que no serán modificados.

Las variables de referencia deben ser inicializadas en sus declaraciones, y no pueden ser reasignadas como
seudónimos a otras variables.
Cuando se regresa un apuntador o una referencia a una variable declarada en la función llamada, la variable
deberá ser declarada static dentro de dicha función.

Las referencias pueden ser usadas como argumentos de funciones y regresar valores.

29
Ejemplo:
int ix; // ix es una variable en entera
int &rx=ix; // rx es el “alias” de ix
ix=1; // tambien rx ==1
rx=2; // tambien ix==2
Las referencias se pueden usar para proveer una función con un alias de un argumento real de llamada de
función. Este permite cambiar el valor del argumento de llamada de función tal como se conoce de otros
lenguajes de programación de llamada por referencia:

/*U se pasa por valor y S por referencia*/


void mira (int U, int &S)
{
U=42;
S=42;
}
void mar()
{
int U,S;
U=S=1; //inicializacion en una linea
mirar(U,S);
//en este punto S=42 y U=1;
}

RESUMEN SOBRE LAS LIBRERÍAS DE C

Las clases estándar de C++ para entrada/salida


Las primeras versiones de C++ desarrolladas en 1980 por Bjarne Stroustrup se basaban en el sistema de
entrada y salida de su lenguaje padre C. Aunque todavía se puede usar en C++ a través de la librería
<cstdio>, nos centraremos en cambio en el sistema de E/S basado en canales, estas son las clases de la
librería <iostream>.

Declaración, apertura y cierre de canales para archivo


Para que un programa pueda leer o escribir valores de un archivo de texto, debe construir un objeto
ifstream o ofstream, respectivamente.

ifstream canal_entrada;
ofstream canal_salida;

Estas instrucciones declaran los objetos canal_entrada y canal_salida como canales de archivo sin inicializar
como conexiones potenciales entre el programa y los archivos-. Se convierten en conexiones reales cuando
se envía un mensaje open():

canal_entrada.open(“nombre_archivo_texto_entrada”);
canal_salida.open(“nombre_archivo_texto_salida”);

Estos dos pasos –declarar y abrir- se pueden combinar usando declaraciones de inicialización:

ifstream canal_entrada(“nombre_archivo_texto_entrada”);

30
ofstream canal_salida(“nombre_archivo_texto_salida”);

Por defecto la apertura de un ofstream hacia un archivo es destructiva, es decir, si el archivo existe, su
contenido es destruido. Para evitar esto, se puede enviar un mensaje de open() con un segundo argumento
de modo, que puede ser cualquiera de los siguientes:
Modo Descripción

ios::in Es el modo por defecto de los objetos ifstream. Abre un archivo para lectura, de forma no
destructiva, colocándose para leer al principio del archivo.

ios::trunc Abre un archivo y borra su contenido.

ios::out Es el modo por defecto de los objetos ofstream. Abre un archivo para escritura, usando ios::trunc.

ios::app Abre un archivo para escritura, pero de forma no destructiva, colocándose para escribir al final del
archivo (es decir, para concatenar).

ios::ate Abre un archivo existente colocándose para leer (en los objetos ifstream) o para escribir (en los
objetos ofstream) al final del archivo.

ios::binary Abre un archivo sobre el que se hará E/S en modo binario en lugar de en modo texto. Usando
read() se leen bytes del archivo y se guardan en arreglos de char, y usando write() se escriben bytes desde
arreglos de char en el archivo.

Al finalizar el tratamiento con el archivo debe romperse la conexión entre el programa y el archivo
enviándole el mensaje close():

canal_entrada.close();
canal_salida.close();

Las clases ifstream, ofstream y fstream son subclases de la clase istream, ostream y iostream,
respectivamente. Por lo tanto, todas las operaciones para E/S interactiva se pueden usar para E/S a través
de fichero. Por ejemplo, una vez que se haya declarado el objeto canal_entrada de tipo ifstream y se ha
conectado a un archivo de texto, se puede leer un valor de él exactamente de la misma forma que hicimos
para la E/S interactiva:

canal_entrada >> variable;

COMO CREAR CLASES EN C/C++

En C y en otros lenguajes de programación procedurales, la programación tiende a ser orientada a acción,


en tanto que en la programación C++ tiende a ser orientada al objeto. En C, la unidad de programación es la
función. En C++ es la clase a partir de la cual eventualmente los objetos son producidos (es decir, son
creados). Los programadores en C++ se concentran en crear sus propios tipos de datos, conocidos como
clases. Para ello, deben definir el formato general utilizando la palabra reservada class. Cada clase contiene
datos junto con un conjunto de funciones que manipula dichos datos.

31
Los componentes de datos de una clase se llaman miembros de datos.
Los componentes de función de una clase se llaman funciones miembros. Al igual que un ejemplo de un tipo
incorporado int se conoce como una variable, un ejemplo de un tipo definido por un usuario (es decir, una
clase) se conoce como un objeto.

class <nombre_clase>
{
// miembros de datos
public: //puede ser private o protected
// funciones / objetos miembros
} // lista_objetos

Control de acceso a miembros (CDA)


Se utilizan tres especificadores de acceso para controlar el acceso a los miembros de la clase, estos son:
public, private y protected. El especificador public define miembros públicos, que son aquellos a los que se
puede acceder por cualquier función. A los miembros que siguen al especificador private sólo se puede
acceder por funciones miembro de la misma clase o por funciones y clases amigas. A los miembros que
siguen al especificador protected se puede acceder por funciones miembro de la misma clase o de clases
derivadas de la misma, así como amigas. Los especificadores public, private y protected pueden aparecer
en cualquier orden. Si se omite el especificador de acceso, el acceso por defecto es privado.

class <nombre_clase>
{
//datos y funciones privadas
public:
//datos y funciones públicas
} //lista_objetos

Una función amigo de una clase se define por fuera del alcance de dicha clase, pero aun así tiene el derecho
de acceso a los miembros private y protected de la clase.

Se puede declarar una función o toda una clase como un friend de otra clase.

Para declarar una función como friend de una clase, en la definición de clase preceda el prototipo de
función con la palabra reservada friend. Para declarar una clase B como amigo de la clase A entre los
miembros de la clase A debe existir la siguiente declaración:

friend B;

Esto significa que la amistad es concedida y no tomada, es decir, para que la clase B sea un amigo de la clase
A, la clase A debe declarar que la clase B es su amigo.

También la amistad no es ni simétrica ni transitiva, es decir, si la clase A es un amigo de la clase B, y la clase


B es un amigo de la clase C, no es correcto pensar que la clase B es un amigo de la clase A, que la clase C es
un amigo de la clase B , ni que A es un amigo de la clase C.

Archivos de cabecera

32
Cada biblioteca estándar tiene su archivo de cabecera correspondiente, que contiene los prototipos de
función de todas las funciones de dicha biblioteca, y las definiciones de varios tipos de datos y de constantes
requeridas por dichas funciones.

Por ejemplo la biblioteca estándar encargada de manipular la hora y fecha, se puede añadir a nuestro
programa incluyendo el archivo de cabecera <time.h> El programador también puede crear archivos de
cabecera para cada una de las clases que define en su programa. Para ello, el nombre de dicho archivo
deberá terminar en .h y podrá ser incluido utilizando la directriz de preprocesador #include. Este archivo
contendrá los miembros de datos de la clase y los prototipos de las funciones pertenecientes a dicha clase.

Declaración de las funciones


Al igual que las variables, las funciones también han de ser declaradas. Esto es lo que se conoce como
prototipo de una función. Para que un programa en C sea compatible entre distintos compiladores es
imprescindible escribir los prototipos de las funciones.

Los prototipos de las funciones pueden escribirse antes de la función main o bien en otro archivo. En este
último caso se lo indicaremos al compilador mediante la directiva #include.

En el ejemplo siguiente podremos ver la declaración de una función (prototipo).


Al no recibir ni retornar ningún valor, está declarada como void en ambos lados.

También vemos que existe una variable global llamada num. Esta variable es reconocible en todas las
funciones del programa. Ya en la función main encontramos una variable local llamada num. Al ser una
variable local, ésta tendrá preferencia sobre la global. Por tanto la función escribirá los números 10 y 5.

Ejemplo:

/* Declaración de funciones. */

#include <stdio.h>
void funcion(void); /* prototipo */
int num = 5; /* variable global */
main()
{
int num=10; /* variable local */
printf("%d\n",num);
funcion(); /* llamada */
}
void funcion(void)
{
printf("%d\n",num);
}

Paso de parámetros a una función


Como ya hemos visto, las funciones pueden retornar un valor. Esto se hace mediante la instrucción return,
que finaliza la ejecución de la función, devolviendo o no un valor. En una misma función podemos tener más
de una instrucción return. La forma de retornar un valor es la siguiente:

33
Sintaxis:
return (<valor o expresión>);

El valor devuelto por la función debe asignarse a una variable. De lo contrario, el valor se perderá. En el
ejemplo puedes ver lo que ocurre si no guardamos el valor en una variable. Fíjate que a la hora de mostrar
el resultado de la suma, en el printf, también podemos llamar a la función.

Ejemplo:
/* Paso de parámetros. */

#include <stdio.h>
int suma(int,int); /* prototipo */
main()
{
int a = 10, b = 25, t;
t = suma(a, b); /* guardamos el valor */
printf("%d=%d", suma(a, b), t);
suma(a, b); /* el valor se pierde */
}
int suma(int a, int b)
{
return (a + b);
}

FUNCIONES CONSTRUCTORAS

Después que los objetos son creados, sus miembros pueden ser inicializados mediante funciones
constructor. Un constructor es una función miembro de clase con el mismo nombre que la clase. El
programador proporciona el constructor que cada vez que se crea un objeto de dicha clase, es invocado
automáticamente. Los miembros de datos de una clase no pueden ser inicializados en la definición de la
clase. Más bien, los miembros de datos deben ser inicializados en un constructor de la clase o sus valores
definidos más adelante después que el objeto haya sido creado. Los constructores no pueden especificar
tipos de regreso y valores de regreso. Los constructores pueden ser sujetos de homonimia, para permitir
una variedad de maneras de inicializar objetos de una clase.

Sintaxis:
<nombre_clase> (<lista de parámetros>);

FUNCIÓN DESTRUCTORA
Un destructor es una función miembro especial de una clase, su nombre está basado en la tilde (~) seguida
por el nombre de la clase. Un destructor de una clase es llamado automáticamente cuando un objeto de
una clase se sale de alcance. De hecho el destructor mismo no destruye el objeto, más bien ejecuta trabajos
de terminación, antes de que el sistema recupere el espacio de memoria del objeto con el fin de que pueda
ser utilizado para almacenar nuevos objetos. Un destructor no recibe ningún parámetro, ni regresa ningún
valor. Una clase sólo puede tener un destructor – la homonimia de destructores no está permitida.

Sintaxis:

~ <nombre_clase> ();

34
Ejemplo:

class Punto
{
int _x,_y;
public:
Punto()
{
_x = _y = 0;
}
}
Punto(const int x,const int y)
{
_x = desde._x;
_y = desde._y;
}
˜Punto(){/*¡Nada que hacer*/}
void setX(const int val);
void setY(const int val);
int getX(){return _x;}
int getY(){return _y;}
};
Cuando son llamados los destructores y constructores:
Por lo general, son llamados de forma automática. En general, las llamadas de destructor se efectúan en
orden inverso a las llamadas del constructor.

Asignación por omisión en copia a nivel de miembro:


Operador de asignación ( = ) es utilizado para asignar un objeto a otro objeto del mismo tipo.

FUNCIONES EN LÍNEA:
Existen funciones en línea que ayudan a reducir la carga por llamadas de función especial para pequeñas
funciones .El compilador puede ignorar el calificador inline y típicamente así lo hará para todo, a excepción
de las funciones más pequeñas. El calificador inline deberá ser utilizado sólo tratándose de funciones
pequeñas, de uso frecuente. Usar funciones inline puede reducir el tiempo de ejecución, pero puede
aumentar el tamaño de programa. (36)

Ejemplo
Verificación de tipos en C++

La verificación o comprobación de tipos en C++ es más rígida (estricta) que en C.

Algunas consideraciones a tener en cuenta son:


Usar funciones declaradas. Esta acción es ilegal en C++, y está permitida en C:

int main()
{
//...
printf(x) //C int printf(x)

35
//C++ es ilegal, ya que printf no esta declarada
return 0;
}

 Fallo al devolver un valor de una función. Una función en C++ declarada con un tipo determinado de
retomo, ha de devolver un valor de ese tipo. En C, está permitido no seguir la regla.

 Asignación de punteros void. La asignación de un tipo void* a un puntero de otro tipo se debe hacer
con una conversación explícita en C++. En C, se realiza implícitamente.

 Inicialización de constantes de cadena. En C++ se debe proporcionar un espacio para el carácter de


terminación nulo cuando se inicializan constantes de cadena. En C, se permite la ausencia de ese
carácter

int main()
{
//......
char car[7] = “Cazorla“; // legal en C
//error en C++
//......
return 0;
}
Una solución al problema que funciona tanto en C como en C++ es: char car [] ="Cazorla";

¿Cómo poner en práctica un TDA con una clase?


Las clases permiten que el programador modele objetos que tienen atributos (miembros de datos) y
comportamientos u operadores (funciones miembros). Los tipos contienen miembros de datos y funciones
miembro, y son definidos mediante la palabra reservada class.

//Usamos la función inline para calcular el volumen de un //cubo.

#include <iostream.h>
inline float cubo(const float s){return s*s*s;}
main()
{
cout<<”Introduce la longitud del lado de un cubo: ”
float lado;
cin >>lado;
cout <<”El volumen del lado”;
cout<<volumen<<”es”<<cubo(lado)<<”\n”;
return 0;
}

Ejemplo:

36
Comentario
Comenzamos con la definición de una clase, indicándola con la palabra reservada class. El cuerpo de la
definición de clase se delimita mediante llaves. La definición de clase termina con un punto y coma. En el
cuerpo de la definición existen partes nuevas: la etiqueta public: y private: se conocen como especificadores
de acceso. Cualquier miembro de datos o función miembro declarado después del especificador de acceso
de miembro) es accesible, siempre que el programa tenga acceso a un objeto de la clase. Cualquier
miembro de datos o función miembro declarada después del especificador de acceso de miembro private:
(y hasta el siguiente especificador de acceso de miembro) sólo es accesible a las funciones miembro de la
clase.

Estos especificadores pueden aparecer varias veces en una definición de clase.


Por defecto, los elementos de las clases son privados ( private: ).

Por lo regular, los miembros de datos aparecen listados en la porción private: de una clase, y normalmente,
las funciones de miembro aparecen listadas en la porción public:, aunque pueden darse en caso contrario.
Los miembros de datos de una clase no pueden ser inicializados donde son declarados en el cuerpo de la
clase. Deberán ser inicializados por el constructor de la clase, o las funciones “ser” les asignar valores.

La función con el mismo nombre que la clase, pero precedido por un carácter tilde (˜) se llama el destructor
de dicha clase.

37
Cuando una función miembro se define por fuera de la definición de clase, el nombre de función es
antecedido por el nombre de la clase y por el operador de resolución de alcance binario (::). Dicha función
miembro queda dentro del alcance de la clase.

El objeto apunto puede usar estos métodos para establecer y para obtener información sobre sí mismo.

Un programa hecho en C++:

38
OPERACIONES CON OBJETOS EN C++

ASIGNACIÓN DE OBJETOS: Se puede asignar un objeto a otro a condición de que ambos objetos sean del
mismo tipo (misma clase). Cuando un objeto se asigna a otro se hace una copia a nivel de bits de todos los
miembros, es decir se copian los contenidos de todos los datos. Los objetos continúan siendo
independientes.

Sintaxis:
<objeto_destino>=<objeto_origen>;
)
EJEMPLO:
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class miclase
{
int a,b;
public:
void obtener(int i, int j){a=i;b=j;}
void mostrar(){cout << a << " "<< b << "\n";}
};
void main()
{
miclase o1,o2;
o1.obtener(10,4);
o2=o1;
o1.show();
o2.show();
getch();
}

ARRAY DE OBJETOS: Los objetos son variables y tienen las mismas capacidades y atributos que cualquier
tipo de variables, por tanto es posible disponerobjetos en un array. La sintaxis es exactamente igual a la
utilizada para declarar y acceder al array. También disponemos de arrays bidimensionales.

DECLARACIÓN:
<nombre_clase> <nombre_objeto>[<nº elementos>];
<nombre_clase> <nombre_objeto>[<nº elementos>]=
{<elemento_1>,<elemento_2>,…,<elemento_n>};

INICIALIZACIÓN:
<nombre_objeto>[<índice>].función(<valores>);

EJEMPLO: Unidimensional.

#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class ejemplo{

39
int a;
public:
void pasar(int x){a=x;}
int mostrar() {return a;}
};
void main()
{
ejemplo ob[4];
int indice;
clrscr();
for(indice=0;indice<4;indice++)
ob[indice].pasar(indice);
for(indice=0;indice<4;indice++)
{
cout << ob[indice].mostrar();
cout << "\n";
}
getch();
}

EJEMPLO: Bidimensional.

#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class bidi
{
int a,b;
public:
bidi(int n, int m){a=n;b=m;}
int pasa_a(){return a;}
int pasa_b(){return b;}
};
void main()
{
clrscr();
int fil,col;
bidi objeto[3][2]={
bidi(1,2),bidi(3,4),
bidi(5,6),bidi(7,8),
bidi(9,10),bidi(11,12)};
for(fil=0;fil<3;fil++)
{
for(col=0;col<2;col++)
{
cout << objeto[fil][col].pasa_a();
cout << " ";
cout << objeto[fil][col].pasa_b();
cout << "\n";

40
}
}
getch();
}

PASO DE OBJETOS A FUNCIONES: Los objetos se pueden pasar a funciones como argumentos de la misma
manera que se pasan otros tipos de datos. Hay que declarar el parámetro como un tipo de clase y después
usar un objeto de esa clase como argumento cuando se llama a la función. Cuando se pasa un objeto a una
función se hace una copia de ese objeto.

Cuando se crea una copia de un objeto porque se usa como argumento para una función, no se llama a la
función constructora. Sin embargo, cuando la copia se destruye (al salir de ámbito), se llama a la función
destructora.

PROTOTIPO DE FUNCIÓN:

<TDD_devuelto> <nombre_funcion>(<nombre_clase> <nombre_objeto>)


{
<cuerpo>;
}

LLAMADA A LA FUNCIÓN:
<nombre_funcion>(<objeto>);

EJEMPLO:
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class objetos{
int i;
public:
objetos(int n){i=n;}
int devol(){return i;}
};
int sqr(objetos o)
{
return o.devol()*o.devol();
}
void main()
{
objetos a(10), b(2);
cout << sqr(a);
cout << sqr(b);
getch();
}

OBJETOS DEVUELTOS POR FUCIONES: Al igual que se pueden pasar objetos, las funciones pueden devolver
objetos. Primero hay que declarar la función para que devuelva un tipo de clase. Segundo hay que devolver
un objeto de ese tipo usando la sentencia return.

41
Cuando un objeto es devuelto por una función, se crea automáticamente un objeto temporal que guarda el
valor devuelto. Este es el objeto que realmente devuelve la función. Después el objeto se destruye, esto
puede causar efectos laterales inesperados.

EJEMPLO:

#include <iostream.h>
#include <stdio.h>
#include <conio.h>
#include <string.h>
class ejemplo{
char cadena[80];
public:
void muestra(){cout<<cadena<<"\n";}
void copia(char *cad){strcpy(cadena,cad);}
};
ejemplo entrada()
{
char cadena[80];
ejemplo str;
cout<<"Introducir cadena: ";
cin>>cadena;
str.copia(cadena);
return str;
}
void main()
{
ejemplo ob;
ob=entrada();
ob.muestra();
getch();
}

PUNTEROS A OBJETOS: Hasta ahora se ha accedido a miembros de un objeto usando el operador punto. Es
posible acceder a un miembro de un objeto a través de un puntero a ese objeto. Cuando sea este el caso, se
emplea el operador de flecha (->) en vez del operador punto. Para obtener la dirección de un objeto, se
precede al objeto con el operador &. Se trabaja de igual forma que los punteros a otros tipos.

EJEMPLO:

#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class miclase{
int a;
public:
miclase(int x);
int get();
};

42
miclase::miclase(int x)
{
a=x;
}
int miclase::get()
{
return a;
}
void main()
{
clrscr();
miclase obj(200);
miclase *p;
p=&obj;
cout << "El valor del Objeto es " << obj.get();
cout << "El valor del Puntero es " << p->get();
getch();
}

Funciones Constructoras y Destructoras


En los programas hay partes que requieren inicialización. Esta necesidad de inicialización es incluso más
común cuando se está trabajando con objetos. Para tratar esta situación, C++ permite incluir una función
constructora. A estas funciones se las llama automáticamente cada vez que se crea un objeto de esa clase.
La función constructora debe tener el mismo nombre que la clase de la que es parte, no tienen tipo
devuelto, es ilegal que un constructor tenga un tipo devuelto.

Pero si es posible pasarle valores a modo de parámetros.

Prototipo de la función:
<nombre_función>(<parámetros>);
Desarrollo de la función:
<nombre_clase>::<nombre_función>(<parámetros>){cuerpo;}

EJEMPLO:

#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class miclase{
int a;
public:
miclase();
void show();
};
miclase::miclase()
{
a=100;
}
void miclase::show()

43
{
cout << a;
}
void main()
{
clrscr();
miclase obj;
obj.show();
getch();
}
El complemento de un constructor es la función destructora. A esta función se la llama automáticamente
cuando se destruye el objeto. El nombre de las funciones destructoras debe ser el mismo que el de la clase a
la que pertenece precedido del carácter ~ (alt+126). Los objetos de destruyen cuando se salen de ámbito
cuando son locales y al salir del programa si son globales. Las funciones destructoras no devuelven tipo y
tampoco pueden recibir parámetros.

Técnicamente un constructor y un destructor se utilizan para inicializar y destruir los objetos, pero también
se pueden utilizar para realizar cualquier otra operación. Sin embargo esto se considera un estilo de
programación pobre.

PROTOTIPO DE LA FUNCIÓN DESTRUCTOR:


~<nombre_función>(<parámetros>);

DESARROLLO DE LA FUNCION:
<nombre_clase>::<nombre_función>()
{
<cuerpo>;
}
EJEMPLO: Mismo programa de antes añadiendo una función destructora.

#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class miclase{
int a;
public:
miclase();
~miclase();
void show();
};
miclase::miclase()
{
a=100;
}
miclase::~miclase()
{
cout << "Destruyendo...\n";
getch();
}

44
void miclase::show()
{
cout << a;
}
void main()
{
clrscr();
miclase obj;
obj.show();
getch();
}

CONSTRUCTORES CON PARAMETROS: Es posible pasar argumentos a una función constructora. Para
permitir esto, simplemente añada los parámetros a la declaración y definición de la función constructora.
Después, cuando declare un objeto, especifique los parámetros como argumentos.

EJEMPLO:
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class miclase
{
int a;
public:
miclase(int x);
void mostrar();
};
miclase::miclase(int x)
{
cout << "Constructor";
a=x;
}
void miclase::miclase()
{
cout <<"El valor de a es: ";
cout << a;
}
void main()
{
miclase objeto(4);
ob.show();
getch();
}

SOBRECARGA DE FUNCIONES Y OPERADORES


En C++ dos o más funciones pueden compartir el mismo nombre en tanto en cuanto difierael tipo de sus
argumentos o el número de sus argumentos o ambos. Cuando comparten el mismo nombre y realizan
operaciones distintas se dice que están sobrecargadas. Para conseguir la sobrecarga simplemente hay que
declarar y definir todas las versiones requeridas.

45
También es posible y es muy común sobrecargar las funciones constructoras. Hay 3 razones por las que
sobrecargar las funciones constructoras. Primero ganar flexibilidad, permitir arrays y construir copias de
constructores

EJEMPLO:
#include <iostream.h>
#include <conio.h>
#include <stdio.h>
int abs(int numero);
long abs(long numero);
double abs(double numero);
void main()
{
clrscr();
cout <<"Valor absoluto de -10 "<< abs(-10) <<"\n";
cout <<"Valor absoluto de -10L "<< abs(-10L) <<"\n";
cout <<"Valor absoluto de -10.01 "<< abs(-10.01) <<"\n";
getch();
}
int abs(int numero)
{
return numero<0 ? -numero:numero;
}
long abs(long numero)
{
return numero<0 ? -numero:numero;
}
double abs(double numero)
{
return numero<0 ? -numero:numero;
}

/*EJEMPLO otro programa*/

#include <iostream.h>
#include <stdio.h>
#include <conio.h>
void fecha(char *fecha);
void fecha(int anno, int mes, int dia);
void main()
{
clrscr();
fecha("23/8/98");
fecha(98,8,23);
getch();
}
void fecha(char *fecha)
{

46
cout<<"Fecha: "<<fecha<<"\n";
}
void fecha(int anno,int mes,int dia)
{
cout<<"Fecha: "<<dia<<"/"<<mes<<"/"<<anno;
}

ARGUMENTOS IMPLICITOS: Otra característica relacionada con la sobrecarga es la utilización de


argumentos implícitos que permite dar un valor a un parámetro cuando no se especifica el argumento
correspondiente en la llamada a la función.

PROTOTIPO:
<TDD_devuelto>(<var_1>=<valor>,<var_2>=<valor>,<var_n>=<valor>);

EJEMPLO:

#include<iostream.h>
#include<stdio.h>
#include<conio.h>
void funcion(int a=0, int b=0)
{
cout<<"a: "<< a <<" b: "<< b <<"\n";
}
void main()
{
clrscr();
funcion();
funcion(10);
funcion(20,30);
getch();
}

Es muy similar a la sobrecarga de funciones, un operador siempre se sobrecarga con relación a una clase.
Cuando se sobrecarga un operador no pierde su contenido original, gana un contenido relacionado con la
clase. Para sobrecargar un operador se crea una función operadora que normalmente será una función
amiga a la clase.

PROTOTIPO:
<TDD_devuelto> <nombre_clase>::<operador> <operador>(<parámetros>)
{
<cuerpo>;
}

Se pueden realizar cualquier actividad al sobrecargar los operadores pero es mejor que las acciones de un
operador sobrecargado se ajusten al uso normal de ese operador.

La sobrecarga tiene dos restricciones, no puede cambiar la precedencia del operador y que el numero de
operadores no puede modificarse. También hay operadores que no pueden sobrecargarse.

47
OPERADORES
.
::
¿??
.*
Existen 3 tipos de sobrecarga de operadores. Operadores binarios, operadores lógicos-relacionales y
operadores unarios. Cada uno de ellos debe tratarse de una manera específica para cada uno de ellos.

BINARIOS: La función solo tendrá un parámetro. Este parámetro contendrá al objeto que este en el lado
derecho del operador. El objeto del lado izquierdo es el que genera la llamada a la función operadora y se
pasa implícitamente a través de this.

EJEMPLO:
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class opera
{
int x, y;
public:
opera() {x=0;y=0;}
opera(int i, int j) {x=i; y=j;}
void obtenxy(int &i,int &j) {i=x; j=y;}
opera operator+(opera obj);
};
opera opera::operator+(opera obj)
{
opera temp;
temp.x=x+obj.x;
temp.y=y+obj.y;
return temp;
}
void main()
{
opera obj1(10,10), obj2(5,3),obj3;
int x,y;
obj3=obj1+obj2;
obj3.obtenxy(x,y);
cout << "Suma de obj1 mas obj2\n ";
cout << "Valor de x: "<< x << " Valor de y: " << y;
getch();
}

LÓGICOS Y RELACIONALES: Cuando se sobrecargan dichos operadores no se deseará que las funciones
operadoras devuelvan un objeto, en lugar de ello, devolverán un entero que indique verdadero o falso. Esto

48
permite que los operadores se integren en expresiones lógicas y relacionales más extensas que admitan
otros tipos de datos.

EJEMPLO:

#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class opera
{
int x,y;
public:
opera() {x=0;y=0;}
opera(int i,int j) {x=i; y=j;}
void obtenerxy(int &i, int &j) {i=x; j=y;}
int operator==(opera obj);
};
int opera::operator==(opera obj)
{
if(x==obj.x && y==obj.y)
return 1;
else
return 0;
}
void main()
{
clrscr();
opera obj1(10,10), obj2(5,3);
if(obj1==obj2)
cout << "Objeto 1 y Objeto 2 son iguales";
else
cout << " Objeto 1 y objeto 2 son diferentes";
getch();
}

UNARIOS: El miembro no tiene parámetro. Es el operando el que genera la llamada a la función operadora.
Los operadores unarios pueden preceder o seguir a su operando, con lo que hay que tener en cuenta como
se realiza la llamada.

EJEMPLO:

#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class opera
{
int x, y;
public:
opera() {x=0;y=0;}

49
opera(int i, int j) {x=i;y=j;}
void obtenxy(int &i, int &j) {i=x; j=y;}
opera operator++();
};
opera opera::operator++()
{
x++;
y++;
}
void main()
{
clrscr();
opera objeto(10,7);
int x,y;
objeto++;
objeto.obtenxy(x,y);
cout<< "Valor de x: " << x <<" Valor de y: "<< y << "\n";
getch();
}

Prioridad de operadores:

() (Paréntesis) +
-(unario),/\,\/ (negación, y , o )
div,mod,*,/ (división en N, multiplicación y división)
+,- (suma, resta)
>=,<=,<,> (Relaciones de orden)
=,≠ (Relación)
o,y (operadores lógicos)
--> (Asignación) _

FUNCIONES INLINE Y AUTOMÁTICAS


La ventaja de las funciones insertadas es que se pueden ejecutar más rápidamente que las funciones
normales. La llamada y vuelta de una función normal tardan tiempo y si tienen parámetros incluso más.
Para declarar este tipo de funciones simplemente hay que preceder la definición de la función con el
especificador inline.

Sintaxis:
inline <TDD_devuelto> <nombre_función> ( <parámetros> ){<cuerpo>;}

Las llamadas a las funciones insertadas se realizan de la misma manera que cualquier función. Uno de los
requisitos es que se tiene que definir antes de llamarla, es decir definir y desarrollar antes de la función
main.
Si el compilador no es capaz de cumplir la petición, la función se compila como una función normal y la
solicitud inline se ignora. Las restricciones son cuatro, no puede contener variables de tipo static, una
sentencia de bucle, un switch o un goto.

50
EJEMPLO: En este programa utilizamos una función inline pasando valores.
No usa clases ni objetos.

#include <iostream.h>
#include <stdio.h>
#include <conio.h>
inline int valor(int x) { return ¡!(x%2);}
void main()
{
int a;
cout <<"Introducir valor: ";
cin >> a;
if (valor(a))
cout << "Es par ";
else
cout << "Es impar";
}

La característica principal de las funciones automáticas es que su definición es lo suficientemente corta y


puede incluirse dentro de la declaración de la clase. La palabra inline no es necesaria. Las restricciones que
se aplican a las funciones inline se aplican también para este tipo. El uso más común de las funciones
automáticas es para funciones constructoras.

Sintaxis

<valor_devuelto><nombre_función> (<parámetros>){<cuerpo>;}

EJEMPLO: Mismo programa anterior pero sin utilizar inline.

#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class ejemplo
{
public:
int valor(int x) { return ¡!(X%2);}
};
void main()
{
int a;
cout <<"Introducir valor: ";
cin >> a;
if (valor(a))
cout << "Es par ";
else
cout << "Es impar";
}

51
Utilización de Estructuras como Clases
En C++, la definición de una estructura se ha ampliado para que pueda también incluir funciones miembro,
incluyendo funciones constructoras y destructoras como una clase. De hecho, la única diferencia entre una
estructura y una clase es que, por omisión, los miembros de una clase son privados y los miembros de una
estructura son públicos.

struct <nombre>{
/*variables y funciones publicas; */
private: /*es un MDA*/
/*variables y funciones privadas;*/
};

Aunque las estructuras tienen las mismas capacidades que las clases, se reserva el uso de struct para
objetos que no tienen funciones miembro. Una de las razones de la existencia de las estructuras es
mantener compatibilidad con los programas hechos C.

EJEMPLO:

#include <iostream.h>
#include <stdio.h>
#include <string.h>
#include <conio.h>
struct tipo
{
tipo(double b, char *n);
void mostrar();
private:
double balance;
char nombre[40];
};
tipo::tipo(double b, char *n)
{
balance=b;
strcpy(nombre,n);
}
void tipo::mostrar()
{
cout << "Nombre: " << nombre;
cout << ": $" << balance;
if (balance<0.0) cout << "****";
cout << "\n";
}
void main()
{
clrscr();
tipo acc1(100.12,"Ricardo");
tipo acc2(-12.34,"Antonio");
acc1.mostrar();

52
getch();
clrscr();
acc2.mostrar();
getch();
}

Como ejemplo a lo anterior crearemos el primer programa utilizando objetos y clases para ver la teoría
llevada a la práctica. Seguiremos utilizando las mismas sentencias que usábamos en C, más adelante los
programas tomarán la estructura exclusiva de C++.

EJEMPLO:

#include <stdio.h>
#include <conio.h>
class miclase
{
int a;
public:
void pasar_a(int num);
int mostrar_a();
};
void miclase::pasar_a(int num)
{
a=num;
}
int miclase::mostrar_a()
{
return a;
}
void main()
{
miclase obj1, obj2;
clrscr();
obj1.pasar_a(10);
obj2.pasar_a(99);
printf("%d\n",obj1.mostrar_a());
printf("%d\n",obj2.mostrar_a());
getch();
}

This, New y Delete

This es un puntero que se pasa automáticamente a cualquier miembro cuando se invoca. Es un puntero al
objeto que genera la llamada, por tanto la función recibe automáticamente un puntero al objeto. A este
puntero se referencia como this y solo se pasa a los miembros punteros this.

Sintaxis:
<objeto.funcion>(); // a la función recibe automáticamente el puntero this.

53
EJEMPLO: El primero sin puntero this. El segundo utilizando el puntero this.

#include <iostream.h>
#include <stdio.h>
#include <conio.h>
#include <string.h>
class stock
{
char item[20];
double coste;
public:
stock(char *i,double c)
{
strcpy(item,i);
coste=c;
}
void muestra();
};
void stock::muestra()
{
cout<<item << "\n";
cout<<"PVP: " << coste;
}
void main()
{
clrscr();
stock obj("tuerca",5.94);
obj.muestra();
getch();
}

2-
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
#include <string.h>
class stock
{
char item[20];
double coste;
public:
stock(char *i,double c)
{
strcpy(this->item,i);
this->coste=c;
}
void muestra();
};
void stock::muestra() /*Esta es la continuación el código*/

54
{
cout<<this->item << "\n";
cout<<"PVP: " << this->coste;
}
void main()
{
clrscr();
stock obj("tuerca",5.94);
obj.muestra();
getch();
}

Hasta ahora si se necesitaba asignar memoria dinámica, se hacía con malloc y para liberar se utilizaba free.

En C++ se puede asignar memoria utilizando new y liberarse mediante delete. Estos operadores no se
pueden combinar unas con otras, es decir debe llamarse a delete solo con un puntero obtenido mediante
new. Los objetos también se les puede pasar un valor inicial con la sentencia new.

SINTAXIS:
<nombre_puntero> =new <tipo>;
delete <nombre_puntero>;
<nombre_puntero>=new <TDD>(<valor_inicial>);

También se pueden crear arrays asignados dinámicamente, estos arrays pueden utilizar la sentencia new. La
sintaxis general es:

DECLARACION DEL ARRAY:

SINTAXIS:

<nombre_puntero>=new <TDD>[<tamaño>]; /*sin inicializar*/

EJEMPLO:
#include<iostream.h>
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
class cosas
{
int i,j;
public:
void obten(int a,int b){i=a;j=b;}
int muestra(){return i*j;}
};

Ejemplo:
void main()
{

55
clrscr();
int *p_var;
p_var=new int;
//p_var=new int(9); se asigna un valor inicial.
cosas *p;
p=new cosas;
if(!p || !p_var)
{
cout<<"Error de asignacion\n";
exit(1);
}
*p_var=1000;
p->obten(4,5);
cout<<"El entero en p_var es: " <<*p_var;
cout<<"\nTotal: " <<p->muestra();
getch();
}

EJEMPLO: Array asignado dinámicamente.

#include<iostream.h>
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
void main(void)
{
int *p;
int i;
p=new int[5];
clrscr();
if(!p)
{
cout<<"Error de asignacion\n";
exit(1);
}
for(i=0;i<5;i++)
p[i]=i+1;
for(i=0;i<5;i++)
{
cout<<"Este es el entero en p["<<i<<"]:";
cout<<p[i]<<"\n";
}
delete[] p;
getch();
}

56
REFERENCIAS
C++ consta de una particularidad relacionada con los punteros, denominada referencia. Una referencia es
un puntero implícito que se comporta como una variable normal siendo un puntero. Existen tres modos de
utilizar una referencia. Se puede pasar a una función, ser devuelta de una función y crearse como una
referencia independiente. Lo que apunta una referencia no puede ser modificado. El caso de las referencias
independientes es muy poco común y casi nunca se utilizan, en este manual no se hace referencia a ellas.
En el ejemplo siguiente se compara un programa que utiliza un puntero normal y otro programa que realiza
las mismas operaciones utilizando una referencia que se pasa a una función.

EJEMPLO:

Utilizando punteros normal.

include <iostream.h>
#include <stdio.h>
#include <conio.h>
void f(int *n);
void main()
{
int i=0;
f(&i);
cout<<"valor i:" << i;
getch();
}
void f(int *n)
{
*n=100;
}
Utilizando referencias.

#include <iostream.h>
#include <stdio.h>
#include <conio.h>
void f(int &n);
void main()
{
int i=0;
f(i);
cout<<"valor i:"<< i;
getch();
}
void f(int &n)
{
n=100;
}

En el caso de las referencias devueltas por una función se puede poner el nombre de la función en el lado
izquierdo de la expresión. Es como asignar un valor a una variable. Hay que tener en cuenta el ámbito de la
variable que se comporta como una referencia.

57
EJEMPLO:

#include <iostream.h>
#include <stdio.h>
#include <conio.h>
int &f();
int x;
void main()
{
clrscr();
f()=100;
cout<<"Valor de x: " <<x;
getch();
}
int &f()
{
return x;
}

HERENCIA
Para empezar, es necesario definir dos términos normalmente usados al tratar la herencia. Cuando una
clase hereda otra, la clase que se hereda se llama clase base. La clase que hereda se llama clase derivada. La
clase base define todas las cualidades que serán comunes a cualquier clase derivada. Otro punto importante
es el acceso a la clase base. El acceso a la clase base pude tomar 3 valores, public, private y
protected.

Si el acceso es public, todos los atributos de la clase base son públicos para la derivada.
Si el acceso es private, los datos son privados para la clase base la derivada no tiene acceso.
Si el acceso es protected, datos privados para la base y derivada tiene acceso, el resto sin acceso.
La clase derivada se le conoce tambien como “clase hija” y a la clase base se le conoce tambien como “clase
madre”.

EJEMPLO: para comprobar los distintos tipos de acceso.

#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class miclase
{
int a;
protected:
int b;
public:
int c;
miclase(int n,int m)
{
a=n;b=m;
}
int obten_a(){return a;}

58
int obten_b(){return b;}
};
void main()
{
miclase objeto(10,20);
clrscr();
objeto.c=30;
// objeto.b=30; error,sin acceso.
// objeto.a=30; error,sin acceso.
cout<<objeto.obten_a() <<"\n";
cout<<objeto.obten_b() <<"\n";
cout<<objeto.c;
getch();
}

FORMATO DE “la clase hija” (sintaxis):

class <nombre_de_clase_hija>:<MDA>
<nombre_de_clase_madre>{
<cuerpo>;
};

EJEMPLO: Herencia pública.

#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class base
{
int x;
public:
void obten_x(int a){x=a;}
void muestra_x(){cout<< x;}
};
class derivada:public base{
int y;
public:
void obten_y(int b){y=b;}
void muestra_y(){cout<<y;}
};

Herencia Simple
Clase Madre:
“Conjunto de objetos”
Clase Hija:
“Prototipos de objetos heredados”
(Encapsulamiento)

59
void main() /*este código es la continuación de otro*/
{
derivada obj; /*obj es una variable de tipo derivada*/
clrscr(); /*se limpia la pantalla*/
obj.obten_x(10); /*se llama al método “obten_x” heredado en derivada*/
obj.obten_y(20); /*se llama al método “obten_y” propio de derivada*/
obj.muestra_x(); /*se llama al método “muestra_x” heredado en derivada*/
cout<<"\n"; /*imprime una salida*/
obj.muestra_y(); /*se llama al método “muestra_y” propio de derivada*/
getch(); /*se lee un carácter*/
}

EJEMPLO: Herencia con acceso privado.

#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class base
{
int x;
public:
void obten_x(int a){x=a;}
void muestra_x(){cout<<x <<"\n";}
};
class derivada:private base{
int y;
public:
void obten_xy(int a,int b){obten_x(a);y=b;}
void muestra_xy(){muestra_x();cout<<y<<"\n";}
};
void main()
{
clrscr();
derivada ob;
ob.obten_xy(10,20);
ob.muestra_xy();
// ob.obten_x(10); error,sin acceso.
// ob.muestra_x(); error,sin acceso.
getch();
}

HERENCIA MULTIPLE: Existen dos métodos en los que una clase derivada puede heredar más de una clase
base. El primero, en el que una clase derivada puede ser usada como la clase base de otra clase derivada,
creándose una jerarquía de clases. El segundo, es que una clase derivada puede heredar directamente más
de una clase base. En esta situación se combinan dos o más clases base para facilitar la creación de la clase
derivada.

SINTAXIS: Para construir la derivada mediante varias clases base.

60
class <nom_derivada>:<MDA> <nomb_base1>,<nomb_base2>,<nomb_baseN<{
<cuerpo>; /*<nomb_base_n> = <nombre_clase_base_n>*/
};

SINTAXIS: Para crear herencia múltiple de modo jerárquico.


class <nomb_derivada_1>:<MDA> <nomb_clase_base_1> {
<cuerpo>;
};
class <nomb_derivada_2>:<MDA> < nomb_derivada_1> {
<cuerpo>;
};
class <nomb_derivada_N>:<MDA> < nomb_derivada_(N-1)>{
<cuerpo>;
};

EJEMPLO: Herencia de tipo jerárquica.

#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class base_a{
int a;
public:
base_a(int x){a=x;}
int ver_a(){return a;}
};
class deriva_b:public base_a
{
int b;
public:
deriva_b(int x, int y):base_a(x){b=y;}
int ver_b(){return b;}
};

class deriva_c:public deriva_b


{
int c;
public:
deriva_c(int x,int y,int z):deriva_b(x,y){c=z;}
void ver_todo()
{
cout<<ver_a()<<" "<<ver_b()<<" "<<c;
}
};
void main()
{
clrscr();
deriva_c ob(1,2,3);
ob.ver_todo();

61
cout<<"\n";
cout<<ob.ver_a()<<" "<<ob.ver_b();
getch();
}

El caso de los constructores es un poco especial. Se ejecutan en orden descendente, es decir primero se
realiza el constructor de la clase base y luego el de las derivadas. En las destructoras ocurre en orden
inverso, primero el de las derivadas y luego el de la base.

EJEMPLO: Múltiple heredando varias clases base.

#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class B1{
int a;
public:
B1(int x){a=x;}
int obten_a(){return a;}
};
class B2{
int b;
public:
B2(int x){b=x;}
int obten_b(){return b;}
};
class C1:public B1,public B2{
int c;
(75)
public:
C1(int x,int y,int z):B1(z),B2(y)
{
c=x;
}
void muestra()
{
cout<<obten_a()<<" "<<obten_b()<<" ";
cout<<c<<"\n";
}
};
void main()
{
clrscr();
C1 objeto(1,2,3);
objeto.muestra();
getch();
}

62
FUNCIONES VIRTUALES
Una función virtual es miembro de una clase que se declara dentro de una clase base y se redefine en una
clase derivada. Para crear una función virtual hay que preceder a la declaración de la función la palabra
clave virtual. Debe tener el mismo tipo y numero de parametros y devolver el mismo tipo.

Cada redefinición de la función virtual en una clase derivada expresa el funcionamiento especifico de la
misma con respecto a esa clase derivada. Cuando se redefine una función virtual en una clase derivada NO
es necesaria la palabra virtual.

EJEMPLO:

#include<iostream.h>
#include<stdio.h>
#include<conio.h>
class base{
public:
int i;
base(int x){i=x;}
virtual void func(){cout<<i<<"\n";}
};
class derivada1:public base{
public:
derivada1(int x):base(x){};
void func(){ cout <<i*i<<"\n";}
};
(76)
class derivada2:public base{
public:
derivada2(int x):base(x){};
void func(){cout<<i+i;}
};
void main()
{
base obj1(10);
derivada1 obj2(10);
derivada2 obj3(10);
obj1.func();
obj2.func();
obj3.func();
getch();
}

Funciones Amigas
Habrá momentos en los que se quiera que una función tenga acceso a los miembros privados de una clase
sin que esa función sea realmente un miembro de esa clase. De cara a esto están las funciones amigas. Son
útiles para la sobrecarga de operadores y la creación de ciertos tipos de funciones E/S.

63
El prototipo de esta funciones viene precedido por la palabra clave friend, cuando se desarrolla la función
no es necesario incluir friend. Una función amiga no es miembro y no se puede calificar mediante un
nombre de objeto. Estas funciones no se heredan y pueden ser amigas de más de una clase.

PROTOTIPO: (sintaxis)
friend <TDD_devuelto> <nombre_función> (<parámetros>);

DESARROLLO:
<TDD_devuelto> < nombre_función> (<parámetros>)
{
<cuerpo>;
}

EJEMPLO:

#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class miclase{
int n,d;
public:
miclase(int i, int j){n=i;d=j;}
friend int factor(miclase ob);
};
int factor(miclase ob)
{
if (!(ob.n%ob.d))
return 1;
else
return 0;
}
void main()
{
miclase obj1(10,2), obj2(3,2);
if(factor(obj1))
cout << "es factor";
else
cout << "no es factor";
getch();
}

Tipo de Dato Referencia


Tambien llamado apuntador, o pointer. Una referencia no es más que un tipo de dato elemental que
representa una dirección de memoria en donde por lo general se encuentra un dato, sea elemental o
estructurado.
Los apuntadores son la base para la creación de la mayoría de las estructuras dinámicas, como listas,
árboles y grafos. La cantidad de memoria que ocupa cada variable tipo referencia es una palabra.

64
APUNTADORES EN C (TAMBIEN SE LES CONOCE COMO “PUNTEROS”)

Un Apuntador es una variable que contiene una dirección de memoria, la cual corresponderá a un dato o a
una variable que contiene el dato. Los apuntadores también deben de seguir las mismas reglas que se
aplican a las demás variables, deben tener nombre únicos y deben de declararse antes de usarse.

Cada variable que se utiliza en una aplicación ocupa una o varias posiciones de memoria. Estas posiciones
de memoria se acceden por medio de una dirección:

En la figura el texto Hello ésta guardado en memoria, comenzando en la dirección 1000. Cada carácter
ocupa un espacio de dirección único en memoria. Los apuntadores proporcionan un método para conservar
y llegar a estas direcciones en memoria. Los apuntadores facilitan el manejo de datos, debido a que
conservan la dirección de otra variable o ubicación de datos.

Por qué son Importantes los Apuntadores?

Los apuntadores dan flexibilidad a los programas en C++ y permiten que estos crezcan dinámicamente.
Utilizando un apuntador hacia un bloque de memoria que se asigna al momento de ejecución, un programa
puede ser más flexible que uno que asigna toda su memoria de una sola vez. También, un apuntador es más
fácil de guardar que una estructura grande o un objeto de una clase. Debido a que un apuntador sólo
guarda una dirección, puede fácilmente pasarse a una función. Uno de las desventajas que pueden
presentar los apuntadores es que un apuntador sin control o no inicializado puede provocar fallas en el
sistema, además de que su uso incorrecto puede generar fallas muy complejas de hallar.

Operadores de Indirección y Dirección:

Hay 2 operadores que se usan cuando trabajan con direcciones en un programa C; el Operador de
Indirección ( * ) y el de Dirección ( & ). Estos operadores son diferentes de los tratados anteriormente.

El Operador de Dirección ( & ) regresa la dirección de una variable. Este operador está asociado con la
variable a su derecha: &h; Esta línea regresa la dirección de la variable h.

El Operador de Indirección ( * ) trabaja a la inversa del operador de Dirección.


También esta asociado con la variable a su derecha, toma la dirección y regresa el dato que contiene esa
dirección. Por ejemplo, la siguiente línea determina la dirección de la variable h y luego usa el operador de
Indirección para acceder a la variable y darle un valor de 42:

65
*(&h)=42;

int n; // es un tipo de dato entero


int *p; // p es un puntero a un entero

/*Una vez declarado un puntero, se puede fijar la dirección o posición de memoria del tipo al que apunta.*/

p = &n; //p se fija a la dirección de a

Una vez que se ha declarado un puntero, p el objeto al que apunta se escribe *p y se puede tratar como
cualquier otra variable de tipo <NombreTipo>.

int *p, *q, o; // dos punteros a integer, y una variable integer


*P = 101; // *p apunta 101
*q= n + *p; // *q apunta a n mas el contenido de lo que apunta p

C++ trata los punteros a tipos diferentes como TDD diferentes,

int *ip;
double *dp;

Los punteros ip y dp son incompatibles, de modo que es un error escribir:

dp = ip;

Se pueden, sin embargo, realizar asignaciones entre contenidos, ya que serealizaría una conversión explícita
de tipos: *dp =ip.

Existe un puntero especial (nulo) que se suele utilizar con frecuencia en programas C++. El puntero NULL
tiene un valor cero, que lo diferencia de todas lasdirecciones válidas. El conocimiento nos permite
comprobar si un puntero p es el puntero NULL evaluando la expresión (p==0). Los punteros NULL se utilizan
sólo como señales de que ha sucedido algo. En otras palabras, si p es un puntero NULL, es incorrecto
referenciar *p.

SINTAXIS La declaración de un puntero de manera general es:

<tipo> *<nombre_de_apuntador>(<variable>);

<tipo>: Especifica el tipo de objeto apuntado y puede ser cualquier tipo.


<nombre de apuntador>: Es el identificador del apuntador.

El espacio de memoria requerido para un apuntador, es el número de bytesnecesarios para especificar una
dirección de memoria, debiendo apuntar siempre al tipo de dato correcto.

Advertencia sobre C: En el lenguaje C como cualquier otro lenguaje que manipula objetos cada uno de ellos
debe tener como mínimo un apuntador asignado, si un objeto queda sin un apuntador para él, queda

66
“aislado” en algún lugar de la memoria ocupando un espacio, motivo por el cual algunos lenguajes emplean
un Recolector de Basura de modo que cada cierto tiempo elimina los objeto que quedan aislados (sin
apuntador) generalmente por descuido del programador, excepto C, donde se tiene libre acceso a todas las
direcciones memoria. Por esto en Java se restringe el acceso a la memoria del computador por motivos de
seguridad, pero se puede acceder a su valor y se verifica siempre el espacio de memoria asignado al
programa que se construye a fin de que por algún descuido del programador, el programa no termine
modificando espacios en memoria asignados a otros datos reservados relacionados con códigos de otros
programas. Por esta razón, Java es más lento que C en la compilación. Si un programa en lenguaje C
sobrescribe su espacio de memoria asignado a él, podría causar daños en otros programas al alterar sus
códigos fuentes que están almacenados, e incluso asta del S.O. Si un apuntador en C apunta a una dirección
donde se encuentra un dato del sistema operativo y lo modifica esto pondría causar daños graves al S.O.
aunque algunos se beneficien de ello.

Considere el siguiente programa y observe a las variables de dirección e Indirección trabajar:

LA ARITMÉTICA DE APUNTADORES.
Generalidades:
Las operaciones más comunes son la asignación y las relacionales. Por lo general sólo interesa
comparaciones de = y !=; raramente necesitaremos comparar una dirección con otra con los operadores > y
<. Algunos lenguajes soportan aritmética de punteros, en donde podemos incrementar o decrementar una
dirección de memoria. En el caso de la asignación, es importante que ambas asignaciones apunten a
elementos del mismo tipo, pues de lo contrario se podrían acceder e interpretar datos de manera errónea.
Dos apuntadores son iguales si almacenan la En C solamente se pueden realizar operaciones de incremento
y decremento, y estos es de acuerdo a la longitud de su tipo de base. Por ejemplo supóngase que una

67
máquina particular utiliza direccionamiento de byte, un entero requiere 4 bytes y el valor de la variable pi
(declarada así: int *pi) es 100, es decir, se apunta al entero *pi en la localidad 100. Entonces el valor de pi-1
es 96, el de pi+1 es 104 y el de pi+2 es 108. El valor de *(pi-1) es el de los contenidos de los 4 bytes 96,97,98,
y 99 , el de *(pi+1) es el del contenido de los byte 104, 105,106 y 107, y el de *(pi+2) es el entero que esta
en los bytes 108,109,110 y 111.

De manera parecida, si el valor de la variable pc (declarada así: char *pc; ) es igual a 100, y un carácter tiene
un byte de longitud, pc-1 refiere a la localidad 99, pc+1 a la 101 y pc+2 a la 102.

Dado que los punteros son números (direcciones), pueden ser manipulados por los operadores aritméticos.
Las operaciones que están permitidas sobre punteros son:
suma, resta y comparación.

Así, si las sentencias siguientes se ejecutan en secuencia:

char *p; // p, contiene la dirección de un carácter


char a[l0]; // array de diez caracteres
p = &a[0]; // p, apunta al primer elemento del array
p++; // p, apunta al segundo elemento del array
p++; // p, apunta al tercer elemento del array
p--; // p, apunta al segundo elemento del array

Un elemento de comparación de punteros, es el siguiente programa:

#include <iostream.h>
main (void)
{
int *ptr1, *ptr2;
int a[2] = {l0,l0};
ptrl = a;
cout << “ptr1 es ” << ptr1 << ” *ptr1 es “ << *ptr1 << endl;
cout << “ptr2 es ” << ptr2 << ” *ptr2 es “ << *ptr2 << endl;
//comparar dos punteros
if (*ptrl == *ptr2)
cout << ”ptr1 es igual a *ptr2 \n”;
else
cout << “*ptrl no es igual a *ptr2 \n”;
}

Apuntadores a Funciones
Un área en la cual desempeñan un papel prominente los apuntadores es el lenguaje C es la transmisión de
parámetros a funciones. Por lo común, los parámetros se transmiten por valor a una función en C, es decir,
se copian los valores transmitidos en los parámetros de la función llamada en el momento en que se invoca.
Si cambia el valor de un parámetro dentro de la función, no cambia su valor en el programa que la llama.
Por ejemplo considérese el siguiente fragmento y función de programa (el número de línea es solo una guía
en el ejemplo):

68
La línea 2 imprime el número 5 y después llama a funct. El valor de "x", que es 5, se copia en "y" y comienza
la ejecución de funct. Después, la línea 9 imprime el número 6 y regresa funct. Sin embargo, cuando la línea
8 incrementa el valor de "y", el valor de "x" permanece invariable. Así, la línea 4 imprime el número 5, "x" y
"y" refieren a 2 variables diferentes.

Si deseamos usar funct para modificar el valor de "x", debemos de transmitir la dirección de "x" de la
siguiente manera:

La línea 2 imprime nuevamente el número 5, y la línea 3 llama a funct. Ahora, sin embargo, el valor
transferido no es el valor entero de "x" sino el valor apuntador "&x". Este es la dirección de "x". El
parámetro de funct no es más y de tipo int, sino py de tipo int * . (Es conveniente nombrar a las variables
apuntadores comenzando con la letra p para recordar tanto que se trata de un apuntador) la línea 8 ahora
aumenta al entero en la localidad py; py, sin embargo, no cambia y conserva su valor inicial "&x".

Así, py apunta al entero "x" de tal manera que cuando *py, aumenta x. La línea 9 imprime 6 y cuando
regresa funct, la línea 4 imprime también 6. Los apuntadores son el mecanismo usado en el lenguaje C para
permitir a una función llamada modificar las variables de la función que llama.

ARREGLOS EN C
Arreglos. Es una serie de datos del mismo tipo, también conocidos como vectores o rangos. Una arreglo
esta constituido por varias posiciones de memoria de igual tamaño consecutivas que tienen el mismo tipo
de variable y se accesan usando el mismo nombre seguido de un subíndice entre corchetes. La cantidad
total de espacio que se usa por un arreglo depende de 2 cosas: El número de elementos en el arreglo y El
tamaño del arreglo.

¿Por qué Usar los Arreglos?


Por que son uno de los factores esenciales de los programas de computadora.
Permiten que se haga referencia a entradas individuales en una tabla de elementos de datos usando el
mismo código y variando el índice del elemento.

La Declaración de un Arreglo: es igual a como se haría con una variable, a excepción de que también se
especifica la cantidad de elementos en el arreglo encerrado entre corchetes de la siguiente manera:

69
<tipo> <nombre_del_arreglo>[<tamaño_indice>];

Cuando se declara un arreglo, sus valores se pueden inicializar de la siguiente manera:

int lista[9]= {0,4,78,5,32,9,77,1,23}; /*Tiene valores en [0…8]*/

No trate de engañar al Compilador inicializando mas valores en el arreglo de los que puede, ya que este no
lo obedecerá, sin embargo, si inicializa solo una parte del arreglo, las restantes posiciones se inicializan con
ceros.

Si desea imprimir un valor de un arreglo, la línea luce así:


printf ("%d", lista[1]); /*Imprime el número: 4*/

Una Característica importante de los arreglos en C es que no se pueden modificar los limites superior e
inferior (y por tanto el rango) durante el programa. El límite inferior se fija siempre en 0 y el superior lo fija
el programador, es decir:

Se han visto hasta ahora, arreglos llamados Unidimensionales, pero existen también los llamados
Bidimensionales(o “matrices”), y se declaran así:

<tipo> <nombre_arreglo_bidimensional>[<tamaño_fila>][<tamaño_columna>];

Estos también son comúnmente conocidos como Matrices, El tamaño en la declaración de la matriz estipula
Renglón-Columna y se representarían así:

Estos también se pueden inicializar:

int lista_nueva[2][2]={{14, 34},{23, 67}};

Y se acceden de la misma manera que un unidimensional.

Ejemplo:
printf ("%d", lista_nueva[1][0]);

Y de esta forma se pueden construir arreglos Multidimensionales por ejemplo:

int grande[3][4][7]; ó incluso int mas_grande [5][7][8][2][3][2];

70
pero se debe de tener cuidado al manejar estos arreglos por que son complejos de manipular a la larga. A
pesar de ser Bidimensionales, en la memoria siguen siendo representados como espacios secuenciales
lineales.

“Toda la memoria del computador, no es mas que un arreglo “megagigante” donde se guardan todos los
datos, por muy complejo que parezca su almacenamiento.”
Breve resumen sobre los arreglos: La Definición de arrays

Los arrays se inicializan con este formato:


int a[3] = {5, 10, 15};
char cad[5] = {‘a’, ‘b’ , c’, ‘d’, ‘e’};
int tabla [2][3] = {{l,2,3} {3,4,5}};

Las tres siguientes definiciones son equivalentes:

char saludo [5] = “hola”;


char saludo [] = “hola”;
char saludo [5] = {‘h’, ‘o’, ‘l’, ‘a‘}

1. Los arrays se pueden pasar como argumentos a funciones.


2. Las funciones no pueden devolver arrays.
3. No está permitida la asignación entre arrays. Para asignar un array a otro, se debe escribir el código
para realizar las asignaciones elemento a elemento.

Arreglos y Apuntadores.
A los arrays se accede a través de los índices:

int lista [5];


lista [3] = 5;

Las versiones con apuntadores en los arreglos son más rápidas que la indexación común. La declaración:

int arr[10]; /*Se declara un arrays en C con diez elementos sin inicializar*/
int *pa,*ptr; /*se crean dos punteros a enteros*/
ptr = arr; /*fija puntero al primer elemento del array*/
ptr =+3; /* ptr = ptr + 3, suma 3 a ptr; ptr apunta al 4to elemento de arr*/
*ptr = 5; /*completa el 4to elemento de arr con valor 5.*/

/*El apuntador pa toma la dirección de memoria donde esta almacenado a[0]*/

pa=&a[0];

/*y así establecemos que:*/

/*pa apunta al valor almacenado en a[0]*/


*pa=a[0];

/*y*/

71
/*pa+1 apunta al valor almacenado en a[1]*/
*(pa+1)=a[1];

/*y así sucesivamente…*/

De esta manera se pude manejar mas eficientemente los valores y direcciones de un arreglo Bi o
Unidimensional.

El nombre de un array se puede utilizar también como si fuera un puntero al primer elemento del array.

double a [10];
double *p = a; // p y a se refieren al mismo array.

M O Y

0 1 2 3 4 5 6 7 8 9

A este elemento se puede acceder por:

a[0] accede a M , *p ;o bien p[0]


a[5] accede a O, *(p + 5); o bien p[5]
a[9] accede a Y, *(p + 9); o bien p[9]

Si el nombre apunta al primer elemento del array, entonces nombre + 1 apunta al segundo elemento. El
contenido de lo que se almacena en esa posición se obtiene por la expresión: *(<nombre_del_apuntador> +
1).

Aunque las funciones no pueden modificar sus argumentos, si un array se utiliza como un argumento de una
función, la función puede modificar el contenido del array.

Punteros a estructuras
Los punteros a estructuras son similares y funcionan de igual forma que los punteros a cualquier otro tipo
de dato.

struct familia
{
char marido[100];
char esposa[100];
char hijo[100];
}
familia Mackoy; //Mackoy estructura de tipo familia
struct familia *p; //p, un puntero a familia
p = &Mackoy; //p, contiene dirección de mackoy
strcpy (p->marido,“Luis Mackoy”); //inicialización
strcpy (p ->esposa, “Vilma Gonzalez”); //inicialización
strcpy (p ->hijo, “Luisito Mackoy”); //inicialización

Punteros a objetos constantes

72
Cuando se pasa un puntero a un objeto grande, pero se trata de que la función no modifique el objeto (por
ejemplo, el caso de que sólo se desea visualizar el contenido de un array), se declara el argumento
correspondiente de la función como puntero a un objeto constante.

declaración: const <NombreTDD> *v establece v como un puntero a un


objeto que no puede ser modificado. Un ejemplo puede ser:
void <nombre_función>(const <ObjetoGrande> *v);
Punteros a void
El tipo de dato void representa un valor nulo. En C++, sin embargo, el tipo de puntero void se suele
considerar como un puntero a cualquier tipo de dato. La idea fundamental que subyace en el puntero void
en C++ es la de un tipo que se puede utilizar adecuadamente para acceder a cualquier tipo de objeto, ya que
es más o menos independiente del tipo. Un ejemplo ilustrativo de la diferencia de comportamiento en C y
C++ es el siguiente segmento de programa:

int main()
{
void *vptr;
int *iptr;
vptr = iptr;
iptr = vptr; //Incorrecto en C++, correcto en C
iptr = (int *) vprr; //Correcto en C++
}

Punteros y cadenas
Las cadenas en C++ se implementan como arrays de caracteres, como constantes de cadena y como
punteros a caracteres.

Constantes de cadena

Su declaración es similar a:
char *cadena = “Mi profesor”;

o bien su sentencia equivalente:


char varcadena[ ] = “Mi profesor”;

Si desea evitar que la cadena se modifique, añada const a la declaración:

const char *varcadena = “Mí profesor”;

Los puntos a cadena se declaran:


char s[1]; o bien: char *s;

Punteros a cadenas
Los punteros a cadenas no son cadenas. Los punteros que localizan el primer elemento de una cadena
almacenada.

char *varCadena;
const char *cadenafija;

73
Consideraciones prácticas
Todos los arrays en C++ se implementan mediante punteros:

char cadenal[l6] = “Concepto Objeto”;


char *cadena2 = cadenal;

Las declaraciones siguientes son equivalentes y se refieren al carácter


C’:
cadenal[0] = ‘a’;
char car = ‘a’;
cadena1[0] = car;

LOS OPERADORES NEW Y DELETE

C++ define un método para realizar asignación dinámica de memoria, diferente del utilizado en mediante
los operadores new y delete.

El operador new sustituye a la función malloc tradicional en C, y el operador delete sustituye a la función
free tradicional, también en C. new, asigna memoria, y devuelve un puntero al new Nombre Tipo y un
ejemplo de su aplicación es:

int *ptrl;
double *ptr2;

ptrl = new int; // memoria asignada para el objeto ptrl


ptr2 = new double; // memoria ampliada para el objeto ptr2

*ptrl = 5;
*ptr2 = 6.55;

Dado que new devuelve un puntero, se puede utilizar ese puntero para inicializar el puntero de una sola
definición, tal como:

int *p = new int;

Si new no puede ocupar la cantidad de memoria solicitada devuelve un valor NULL. El operador delete
libera la memoria asignada mediante new.

delete prt1;

Un pequeño programa que muestra el uso combinado de new y delete es:

include <iostream.h>
void main (void)
{
char *c;
c = new char[512];

74
cin >> c;
cout << c <<endl;
delete [] c;
}

Los operadores new y delete se pueden utilizar para asignar memoria a arrays, clases y otro tipo de datos.

int *i;
i = new int[2][35]; //crear el array de 2 x 35 dimensiones

… //asignar el array

delete i; //destruir el array


Sintaxis de new y delete

new <nombre-tipo> new int new char[l00];


new <nombre-tipo> <ínicializador> new int (99) new char(’C’);
new <nombre-tipo> new (char*);
delete <expresión> delete p;
delete[] <expresión> delete []p;

ENUMERACIONES
En C++, un nombre de una enumeración, estructura o unión es un nombre de un tipo. Por consiguiente, la
palabra reservada struct, union, o enum no son necesarias cuando se declara una variable.

El tipo de dato enumerado designa un grupo de constantes enteros con nombres. La palabra reservada
enum se utiliza para declarar un tipo de dato enumerado o enumeración. La sintaxis es:

enum <nombre>{<valor_1>,<valor_2>,<valor_3>,…,<valor_n>};

Donde <nombre> es el nombre de la variable declarada enumerada, <valor> es una lista de “tipos n-
numerados”, a los que se asigna valores cuando se declara la variable enumerada y puede tener un valor de
inicialización. Se puede utilizar el nombre de una enumeración para declarar una variable de ese tipo
(variable de enumeración). Ej:

<nombre> <ver>;
Considérese la siguiente sentencia:

enum color [Rojo, Azul, Verde, Amarillo];

Una variable de tipo enumeración color es:


color pantalla = Rojo; //Estilo de C++

ESTRUCTURAS
Una estructura (o registro) es un tipo de dato compuesto que contiene una colección de elementos de tipos
de datos diferentes combinados en una única construcción del lenguaje. Cada elemento de la colección se

75
llama miembro y puede ser una variable de un tipo de dato diferente. Una estructura representa un nuevo
tipo de dato en C++.

La sintaxis de una estructura es:

struct <nombre>{<miembro_1>;<miembro_2>;…;<miembro_n>};

La sintaxis para la composición de un miembro especifico, podría ser:


<miembro_n> = <TDD> <campo_n>

Un ejemplo y una variable tipo estructura se muestran en las siguientes sentencias:

struct cuadro
{
int i;
float f;
};

struct cuadro nombre; /*Estilo C cuadro es una variable del TDD cuadro*/
cuadro nombre; //Estilo C++

UNIONES
Una unión es una variable que puede almacenar objetos de tipos y tamaños diferentes. Una unión puede
almacenar tipos de datos diferentes, sólo puede almacenar uno cada vez, en oposición a una estructura que
almacena simultáneamente una colección de tipos de datos. La sintaxis de una unión es:

union <nombre>{<miembro_1>,<miembro_2>,…,<miembro_n>};
Un ejemplo de una unión es:

union alfa
{
int x;
char o;
};

Una declaración de una variable estructura es:

alfa w;

El modo de acceder a los miembros de la estructura es mediante el operador punto ●.

Ejemplo:

u.x = 145;
u.c = ‘z’;

C++ admite un tipo especial de unión llamada unión anónima, que declara un conjunto de miembros que
comparten la misma dirección de memoria. La unión anónima no tiene asignado un nombre y, en
consecuencia, se accede a los elementos de la unión directamente. La sintaxis de una unión anónima es:

76
union
{
int nuevolD;
int contador;
}

Las variables de la unión anónima comparten la misma posición de memoria y espacio de datos.
int main()
{
union
{
int x;
float y;
double z;
}
x = 25;
y = 245.245; //el valor en y sobrescribe el valor de x
z = 9.41415; //el valor en z sobrescribe el valor de z
}

CADENAS EN C++
Una cadena es una serie de caracteres almacenados en bytes consecutivos de memoria. Una cadena se
puede almacenar en un array de caracteres (char) que tenían en un carácter nulo (cero):

char perro[5] = { `m`,`o`,`r`,`g`,`a`,`n`}; // no es una cadena


char gato[5] = { ´f´,´e´,´l´,´i´,´n´,´o´,´\0´,}; // es una cadena

Lectura de una cadena del teclado

#include <iostream.h>
main ()
{
char cad [80];
cout << “introduzca una cadena:”; // lectura del teclado cm » cad;
cin >> cad;
cout << “Su cadena es:”;
cout << cad;
return 0;
}

Esta lectura del teclado lee una cadena hasta que se encuentra el primercarácter blanco. Así, cuando se lee
“Hola que tal” la primera cadena, en cad sólo se almacena Hola. Para resolver el problema, utilizará la
función gets () que lee una cadena completa leída del teclado. El programa anterior quedaría así:

#include <iostream.h>
#include <stdio.h>
main()

77
{
char cad[80];
cout « “Introduzca una cadena:”;
gets (cad);
cout « Su cadena es:”; cout << cad;
return 0;
}
Definición de funciones

La definición de una función es el cuerpo de la función que se ha declarado con anterioridad.

double Media (double x, double y) // Devuelve la media de x e y


{
return (x + y)/2.0
}
char LeerCaracter() //Devuelve un carácter de la entrada estándar
{
char c;
cin >> c;
return c;
}

Argumentos por omisión


Los parámetros formales de una función pueden tomar valores por omisión, o argumentos cuyos valores se
inicializan en la lista de argumentos formales de la función.

int Potencia (int n, int k = 2);


Potencia(256); //256 elevado al cuadrado

Los parámetros por omisión no necesitan especificarse cuando se llama a la función. Los parámetros con
valores por omisión deben estar al final de la lista de parámetros:

void fund (int i =3, int j); // no correcto


void func2 (int i, int j=0, int k = 0); // correcto
void func3 (int i = 2, int j, int k = 0); // no correcto
void ImprimirValores (int cuenta,double cantidad= 0.0);

Llamadas a la función ImprimirValores:

ImprimirValores (n,a);
ImprimirValores (n); //equivalente a ImprimirValores (n, 0.0)

Otras declaraciones y llamadas a funciones son:

double fl(int n, int m, int p=0); // correcto


double f2(int n, int m= 1, int p = 0); // correcto
double f3(int n = 2, int m= 1, int p =0); // correcto
double f4(int n, int m = 1, int p); // no correcto
double f5(int n = 2, int m, int p = 0); // no correcto

78
Funciones en línea (inline)
Si una declaración de función está precedida por la palabra reservada inline, el compilador sustituye cada
llamada a la función con el código que implementa la función. Ejemplo:

inline int max(int a, int b)


{
if (a > b) return a;
return b;
}
main ()
{
int x = 5, y = 4;
int z = Max(x,y);
}
Los parámetros con valores por omisión deben entrar al final de la lista de parámetros: Las funciones f1, f2 y
f3 son válidas, mientras que las funciones f4 y f5 no son válidas. Las funciones en línea (inline) evitan los
tiempos suplementarios de las llamadas múltiples a funciones. Las funciones declaradas en línea deben ser
simples con sólo unas pocas sentencias de programa; sólo se pueden llamar un número limitado de veces y
no son recursivas.

Ejemplo:

inline int abs(int i);


inline int min(int v1, int v2);
int mcd(int vl, int v2);

“Las funciones en línea deben ser definidas antes de ser llamadas.”

#include <iostream.h>
int incrementar (int I);
inline incrementar (int i)
{
i ++;
return i;
}
main (void)
{
int i = 0;
while (i < 5)
{
i = incrementar (i);
cout « “i es:”« i «endl;
}
}

Sobrecarga de funciones

79
En C++, dos o más funciones distintas pueden tener el mismo nombre. Esta propiedad se denomina
sobrecarga. Un ejemplo es el siguiente:

int max (int, int);


double max (double, double);
o bien este otro:
void sumar (char i);
void sumar (float j);

Las funciones sobrecargadas se diferencian en el número y tipo de argumentos, o en el tipo que devuelven
las funciones, y sus cuerpos son diferentes en cada una de ellas.

Ejemplo:
#include <iostream.h>
void suma (char);
void suma (float);
main (void)
{
int i = 65;
float j = 6.5;
char c = ‘a’;
suma ( i );
suma ( j ) ;
suma ( c ) ;
}
void suma (char i)
{
cout << “Suma interior(char) “ <<endl;
}
void suma ( float j )
{
cout << “Suma interior (float) “<< endl;
}

El modificador <const>

Constantes:
En C++, los identificadores de variables/constantes se pueden declarar constantes, significando que su valor
no se puede modificar.

Esta declaración se realiza con la palabra reservada const.

const double PI= 3.11416;


const char BLANCO = ´ ´;
const double PI_EG = -I;
const double DOBLE_I = 2 * PI ;

80
El modificador de tipos const se utiliza en C++ para proporcionar protección de sólo lectura para variables y
parámetros de funciones. Cuando se hace preceder un tipo de argumento con el modificador const para
indicar que este argumento no se puede cambiar, el argumento al que se aplica no se puede asignar un
valor ni cambiar.

void copia (const char *fuente, char *destino);


void funciondemo (const int I);
Paso de parámetros a funciones
En C++ existen tres formas de pasar parámetros a funciones:

1. Por valor La función llamada recibe una copia del parámetro y este parámetro no se puede modificar
dentro de la función

void intercambio (int x, int y)


{
int aux = y;
y = x;
x = aux;
}
//.....intercambio (i , j ); // las variables i, j, no se intercambian

2. Por dirección. Se pasa un puntero al parámetro. Este método permite simular en C/C++ la llamada por
referencia, utilizando tipos punteros en los parámetros formales en la declaración de prototipos. Este
método permite modificar los argumentos de una función.

void intercambio (int *x,int *y)


{
int aux = *y;
*y = *x
*x = aux;
}
// ...intercambio (&i, &j); // i , j se intercambian sus valores

3. Por referencia. Se pueden pasar tipos referencia como argumentos defunciones, lo que permite
modificar los argumentos de una función.

void intercambio(int &x, int &y)


{
int aux = y;
y = x;
x = aux;
}
//.....intercambio (i,j); // i , j intercambian sus valores

81
Si se necesita modificar un argumento de una función en su interior, el argumento debe ser un tipo de
referencia en la declaración de la función.

Paso de arrays
Los arrays se pasan por referencia. La dirección del primer elemento del array se pasa a la función; los
elementos individuales se pasan por valor. Los arrays se pueden pasar indirectamente por su valor si el
array se define como un miembro de una estructura.

Ejemplo 1:

Paso del array completo.

#include <iostream.h>
void func1 (int x[]); //prototipo de función
void main( )
{
int a[3] = {l,2,3};
func1 (a) ; // sentencias
func1 (&a[0]) ; // equivalentes
}
void func(int x[])
{
int i;
for (i = 0; 1 < 3; i + 1)
cout << 1 << x[i]<< ‘\n’;
}

Ejemplo 2:

Pasa de a un elemento de un array.

#include <iostream.h>
const int n + 3;
void func2(int x);
void main( )
{
int a[n] = {l,2,3};
func2 (a[2]);
}
void func2(int x)
{
cout << x << ‘\n’;
}

Tamaño de un TDD (El operador: sizeof)

82
El operador (funsión) sizeof proporciona el tamaño en bytes de un tipo de dato o variable sizeof toma el
argumento correspondiente (tipo escalar, array, record, etc.).

La sintaxis del operador es:

sizeof (<nombre variable>); /*si se quiere saber el tamaño en byte de una variable*/
sizeof (<tipo de dato>); /*si se quiere saber el tamaño de un tipo de dato elemental*/

Ejemplos:
int m , n[12];
sizeof(m); // retorna 4, en maquinas de 32 bits
sizeof(n); // retorna 48, 4 byte x 12 posiciones
sizeof (15); // retorna 4 un entero 15
sizeof (int); // retorna el tamaño en memoria de un int (generalmente 32 bits). */

La sentencia nula:
La sentencia nula se representa por un punto y coma, y no hace ninguna acción:

char cad[80]=”Cazorla”;
int i;
for (i = 0; cad[i] != ‘\0’; i++);
(100)

Prioridad y asociatividad de operadores en C (versión completa)

Cuando se realizan expresiones en las que se mezclan operadores diferentes es preciso establecer una
precedencia (prioridad) de los operadores y la dirección (o secuencia) de evaluación (orden de evaluación:
izquierda-derecha, derecha-izquierda), denominada asociatividad. La Tabla B.11 muestra la precedencia y
asociatividad de operadores.

Ejemplo:
a * b/c+d equivale a: (a * b ) / ( c + d )

Sobrecarga de operadores
La mayoría de los operadores de C++ pueden ser sobrecargados o redefinidos para trabajar con nuevos
tipos de datos.

83
Precedencia y asociatividad de operadores

:: ( ) [ ] . -> Izquierda-Derecha
++ -- Derecha-Izquierda
& (dirección) ~ (tipo) ¡ - +
sizeof (tipo) new delete * (indireccion)
.* ->* Izquierda-Derecha
*/% Izquierda-Derecha
+— Izquierda-Derecha
<< >> Izquierda-Derecha
< <= > >= Izquierda-Derecha
= = ¡= Izquierda-Derecha
& Izquierda-Derecha
^ Izquierda-Derecha
| Izquierda-Derecha
&& Izquierda-Derecha
|| Izquierda-Derecha
= += - = *= /= %= >>= <<= &= |= ^= Derecha-Izquierda
, (operador coma) Izquierda-Derecha
Operadores que se pueden sobrecargar
+ - * / % ^ & | ~ ! = < > >> << >>= <<= != == ( ) -> , []
ENTRADAS Y SALIDAS BÁSICAS
Al contrario que muchos lenguajes, C++ no tiene facilidades incorporadas para manejar entrada o salida. En
su lugar, se manejan por rutinas de bibliotecas. Las clases que C++ utiliza para entrada y salida se conocen
como flujos.

Un flujo (stream) es una secuencia de caracteres junto con una colección de rutinas para insertar
caracteres en flujos (a pantalla) y extraer caracteres de un flujo (de teclado).

Salida
El flujo cout es el flujo de salida estándar que corresponde a stdout en C. Este flujo se deriva de la clase
ostream construida en iostream.

CPU cout PANTALLA

Entrada binaria Salida de caracteres

Uso de flujos para salida de caracteres.

Si se desea visualizar el valor del objeto int llamado i, se escribe la sentencia:

cout << i;

Las salidas en C++ se pueden conectar en cascada, con una facilidad de escritura mayor que en C.

#include <isostrean.h>.
int main()
{
int i;

84
i = 1099;
cout << ”El valor de i es” << i << “\n”;
}

Otro programa que muestra la conexión en cascada es:

#include <iostream.h>
int main()
{
int x = 45;
double y = 496.125;
char *c = "y es multiplicada por x = ";
cout << c << y * x << "\n”;
}

Entrada
La entrada se maneja por la clase istream. Existe un objeto predefinido istream, llamado cin, que se refiere
al dispositivo de entrada estándar (el teclado). El operador que se utiliza para obtener un valor del teclado
es el operador de extracción >>. Por ejemplo, si i era un objeto int, se escribirá:
cin >> i;
que obtiene un número del teclado y lo almacena en la variable i.
Un programa simple que lee un dato entero y lo visualiza en pantalla es:

#include <iostream.h>
int main()
{
int i;
cin >> i;
cout << i;
}

Al igual que en el caso de cout, se pueden introducir datos en cascada:

#include <iostream.h>
int main()
{
char c[60];
int x,y;
cin >> c >> x >> y;
cout << c << “ “ << x << “ “ << y << “\n”;
}

Manipuladores
Un método fácil de cambiar la anchura del flujo y otras variables de formato es utilizar un operador especial
denominado manipulador. Un manipulador acepta una referencia de flujo como un argumento y devuelve
una referencia al mismo flujo.

El siguiente programa muestra el uso de manipuladores específicamente para conversiones de número


(dec, oct, y hex):

85
#include <iostream.h>
int main ()
{
int i = 36;
cout << dec << i << oct << i << “ ” << hex << i << “\n”;
}

La salida de este programa es: 36 44 24

Otro manipulador típico es endl, que representa al carácter de nueva línea (salto de línea), es equivalente a
\n . El programa anterior se puede escribir también así:

#include <iostream.h>
int main ()
{
int i = 36;
cout << dec << i << oct << i << hex << i << endl;
}

PALABRAS RESERVADAS DE C++

Las palabras reservadas o claves no se deben utilizar como identificadores, debido a su significado estricto
en C++; tampoco se deben redefinir. La Tabla enumera las palabras reservadas de C++ según el ARM(Siglas
del libro de BJARNE STROUSTRUP en el que se definen las reglas de sintaxis del lenguaje C++ estándar).

Palabras reservadas de C++

asm* continue float new* signed try


Auto Default for operator* sizeof typedef
break delete* friend* private* static union
Case Do goto protected* struct unsigned
catch* Double if public* switch virtual*
Char Else inline* register template* void
class* Enurn int return this* volatile
Const Extern long short throw* while
*Estas palabras no existen en ANSI C.

Los diferentes compiladores comerciales de C++ pueden incluir, además, nuevas palabras reservadas. Estos
son los casos de Borland, Microsoft y Sysmantec.

Tabla. Palabras reservadas de Turbo/Borland C++

asm _ds interrupt short


auto else _loadds signed
break enum long sizeof
case _es _near _ss

86
catch export near static
_cdecl extern new struct
_cdecl far operator switch
char far pascal template
class float pascal this
const for private typedef
continue friend protected union
_cs goto public unsigned
default huge register virtual
delete if return void
do inline _saveregs volatile
_double int _seg while

Tabla. Palabras reservadas de Microsoft Visual C/C++ 1.5/2.0

asm else int signed


Auto enum _interrupt sizeof
Based _except _leave static
break _export _loadds _stcall
case extern long struct
_cdec l _far maked switch
char _fastcall _near thread
const _finally _pascal _try
continue float register typedef
_declspec for return union
default _fortran _saveregs unsigned
dllexport goto _self void
dllimport _huge _segment Volatile
Do if _segname While
doublé _inline short

El comité ANSI ha añadido nuevas palabras reservadas


Tabla. Nuevas palabras reservadas de ANSI C++

Bool false reinterpretcast typeid


Cons _cast mutable static_cast using
dynamic_cas namespace true wchart
t

87
EJERCICIOS

COMO HACER CICLOS EN C:

Estructura DO...WHILE

Su sintaxis es:
Do
{
<sentencia1>;
<sentencia2>;
} while (<condición>);

Ejemplo:

/* Uso de la sentencia DO...WHILE para hacer un menú*/

#include <stdio.h>
main()
{
char seleccion;
do
{
printf("1.- Comenzar\n");

88
printf("2.- Abrir\n");
printf("3.- Grabar\n");
printf("4.- Salir\n");
printf("Escoge una opción: ");
seleccion = getchar(); /*función que lee un caracter*/
switch(seleccion)
{
case '1':printf("Opción 1");
break;
case '2':printf("Opción 2");
break;
case '3':printf("Opción 3");
}
}
while(seleccion!='4');
}

Estructura FOR
Su sintaxis es:
for (<inicialización>;<condición>;<incremento>)
{
<sentencia1>;
<sentencia2>;
}

El flujo del bucle FOR transcurre de la siguiente forma:

Estructura WHILE

Su sintaxis es:
while(<condicion>)
{
<Sentencia1>;
<Sentencia2>;
}

Ejemplo:

89
inline float suma (float a, float b)
{
Return a+b;
}
inline int max( int a, int b)
{
return (a > b) ? a : b;
}

Ejemplo:

#include <iostream.h>
#include <conio.h>
main()
{
int i=0;
clrscr();
int j=10;
cout<<j<<'\n';

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


{

int j=10;
cout<<i<<" j: "<<j<<'\n';

cout<<"\ni al salir del ciclo: "<<i;


getch();

/* while((int h=1) <5){ Incorrecto


cout<<"hola"
h++;
}
switch(int i=10){ Incorrecto
case 1: cout<<"exito";
}

Ejemplo:

#include <iostream.h>
#include <conio.h>
int punto(int=5, int=4);
main () {
cout<<"valor 1: "<<punto()<<'\n';
cout<<"valor 2: "<<punto(1)<<'\n';
cout<<"valor 3: "<<punto(1,3)<<'\n';

90
getch();
}
int punto( int x, int y){
if(y!=4)
return y;
if(x!=5)
return x;
return x+y;
}

EJERCICIO

#include <iostream.h>
#include <conio.h>
int b=1;
int f(int);
int h(int x=f(b)); // argumento default f(::b)
void main ()
{
clrscr();
b=5;
cout<<b<<endl;
{
int b=3;
cout<<b<<endl;
cout<<h(); //h(f(::b))
}
getch();
}
int h(int z){
cout<<"Valor recibido: "<<z<<endl;
return z*z;
}
int f(int y)
{
return y;
}

Ejercicio # 1 cout << ''Introduce la longitud


del lado de tu cubo: '';
#include <iostream.h> float lado;
#include <stdlib.h> cin >> lado;
inline float cubo( const float s ) cout << ''El volumen del cubo
{ return s * s * s; } de lado '';
main( ) cout<< lado << ''es'' <<
{ cubo( lado ) << '\n';
system (“pause”);

91
return 0; {
} int v = 7;
::v = 10.5; // Utilizar la variable
global v
cout<< "variable local v = " << v <<
Ejercicio # 2 '\n';
cout << "variable global v = "
#include <iostream.h>
<< ::v << '\n';
#include <stdlib.h> system ("pause");
float v;
return 0;
int main( ) }
} hor ;
public:
void entrada_emp( void ) ;
void salida_emp( void ) ;
};
void empleado::entrada_emp( void ) {
char cr ;
cout << "Nombre del empleado:" ;
cin >> nom.nombre ;
cin.get( cr ) ; //Pasar el cambio de línea
cout << "Primer apellido del empleado:" ;
cin >> nom.primer_apel ;
cin.get( cr ) ;
cout << "Segundo apellido del empleado:" ;
cin >> nom.segundo_apel ;
cin.get( cr ) ;
cout << "Total de horas trabajadas: " ;
cin >> hor.horas ;
cout << "Sueldo base por hora: " ;
cin >> hor.salario_base ;
cout << "Pago por horas extra: " ;
cin >> hor.salario_extra ;
cout << '\n\n' ;
}
void empleado::salida_emp( void ) {
Ejercicio # 3
cout << nom.nombre << " "
#include <iostream.h> << nom.primer_apel << " "
#include <stdlib.h> << nom.segundo_apel << endl ;
class empleado { if ( hor.horas <= 40 )
struct nombre_emp { cout << "Pago básico: $"
char nombre[20] ; <<hor.horas * hor.salario_base
char primer_apel[20] ; << endl ;
char segundo_apel[20] ; else {
} nom ; cout << "Pago básico: $" << 40 *
struct horas_emp { hor.salario_base, endl ;
double horas ; cout << "Pago extra: $"
double salario_base ; << (hor.horas - 40) * hor.salario_extra
double salario_extra ; << endl ;

92
} emp_corte_ingles.entrada_emp( ) ;
} emp_corte_ingles.salida_emp( ) ;
void main( void ) { system("pause");
empleado emp_corte_ingles; }
// empleado.

cout<<"Escribe El Primer Numero"<<endl;


cin>>x;
cout<<"Escribe El Segundo Numero"<<endl;
cin>>y;
if(x>y) cout<<"El mayor es "<<x<<endl;
else if(y>x) cout<<"El mayor es "<<y<<endl;
else cout<<"Son iguales"<<endl;
getch();
}

Ejercicio 5
# include "stdio.h"
# include "conio.h"
# include "stdlib.h"

int main ()
{
int v[10];
int i=0, x=0;

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


{
Ejecicio 4
system ("cls");
#include <iostream> printf("\n digite los numeros");
#include <conio.h> printf ("\n Posn del vector [%d]", i);
using namespace std; scanf ("%d",&v[i]);
}
int main(){ for (i=0; i<10; i++)
double x,y; {

93
if (v[i]%3==0) printf("\n En el vector hay [%d] multiplos de 3",
{ x);
(x = x + 1); getch();
} }
}

APÉNDICE
En este capítulo y para finalizar veremos los archivos de cabecera, donde están declaradas las funciones que
utilizaremos más a menudo.

Librería stdio.h (Librería estandar de C/C++)

printf
Función: Escribe en la salida estándar con formato. Sintaxis: printf(<formato>,<arg1>, ...);

Scanf
Función: Lee de la salida estándar con formato. Sintaxis: scanf(<formato>,<arg1>, ...);

puts
Función: Escribe una cadena y salto de línea. Sintaxis: puts(<cadena>);

gets
Función: Lee y guarda una cadena introducida por teclado. Sintaxis: gets(<cadena>);

fopen
Función: Abre un fichero en el modo indicado. Sintaxis: pf=fopen(<fichero> , <modo de apertura>);

fclose
Función: Cierra un fichero cuyo puntero le indicamos. Sintaxis: fclose(<nombre_puntero>);

94
fprintf
Función: Escribe con formato en un fichero. Sintaxis: fprintf(<nombre_puntero>,<formato> , <arg1>,..);

fgets
Función: Lee una cadena de un fichero. Sintaxis: fgets(<cadena> , <longitud> , <nombre_puntero>);

Librería stdlib.h (Librería estandar de C/C++)


atof
Función: Convierte una cadena de texto en un valor de tipo float.
Sintaxis: float <nomb_variable> = atof(<cadena>);

atoi
Función: Convierte una cadena de texto en un valor de tipo entero.
Sintaxis: int <nomb_variable>= atoi(<cadena>);

itoa
Función: Convierte un valor numérico entero en una cadena de texto. La base generalmente será 10,
aunque se puede indicar otra distinta.
Sintaxis: itoa(<número> , <cadena> , <base>);

exit
Función: Termina la ejecución y abandona el programa.
Sintaxis: exit(<variable_estado>); /* Normalmente el estado será 0 */

Librería conio.h (Librería estandar de C/C++)

clrscr
Función: Borra la pantalla. Sintaxis: clrscr( ); es lo mismo que system(“cls”) ;

clreol
Función: Borra desde la posición del cursor hasta el final de la línea. Sintaxis: clreol( );

gotoxy
Función: Cambia la posición del cursor a las coordenadas indicadas. Sintaxis: gotoxy(<columna> , <fila>);

textcolor
Función: Selecciona el color de texto (0 - 15). Sintaxis: textcolor(<variable_color>);

textbackground
Función: Selecciona el color de fondo (0 - 7). Sintaxis: textbackground(<variable_color>);

wherex
Función: Retorna la columna en la que se encuentra el cursor. sintaxis: <variable_columna>=wherex( );

wherey
Función: Retorna la fila en la que se encuentra el cursor. Sintaxis: <variable_fila>=wherey( );

getch

95
Función: Lee y retorna un único caracter introducido mediante el teclado por el usuario. No muestra el
carácter por la pantalla.
Sintaxis: char <nomb_variable> =getch();

getche
Función: Lee y retorna un único carácter introducido mediante el teclado por el usuario. Muestra el carácter
por la pantalla.
Sintaxis: char <nomb_variable> = getche();

Librería string.h (Librería estándar de C++)


strlen
Función: Calcula la longitud de una cadena. Sintaxis: int <nomb_variable>=strlen(<cadena>);

strcpy
Función: Copia el contenido de una cadena sobre otra.
Sintaxis: strcpy(<cadena_a_sobre_escribir>,<cadena_original>);

strcat
Función: Concatena dos cadenas. Sintaxis: strcat(<cadena_1>,<cadena_2>,…,<cadena_n>);

strcmp
Función: Compara el contenido de dos cadenas. Si cadena1 < cadena2 retorna un número negativo. Si
cadena1 > cadena2, un número positivo, y si cadena1 es igual que cadena2 retorna 0 ( o NULL ).
Sintaxis: int <nomb_variable>=strcmp(<cadena1>,<cadena2>);

FUNCIONES INTERESANTES
fflush(stdin)
Función: Limpia el buffer de teclado.
Sintaxis: fflush(stdin);
Prototipo: stdio.h

sizeof
Función: Operador que retorna el tamaño en bytes de una variable.
Sintaxis: int <nomb_variable_2>= sizeof (nomb_variable_1|TDD);

cprintf
Función: Funciona como el printf pero escribe en el color que hayamos activado con la función textcolor
sobre el color activado con textbackground.
Sintaxis: cprintf(<formato> , <arg1> , ...);
Prototipo: conio.h

kbhit
Función: Espera la pulsación de una tecla para continuar la ejecución.
Sintaxis: while (!kbhit( )) /* Mientras no pulsemos una tecla... */
Prototipo: conio.h

random
Función: Retorna un valor aleatorio entre 0 y numero_limite-1.
Sintaxis: int <nomb_variable>=random(<numero_limite>);

96
/* También necesitamos la función randomize */
Prototipo: stdlib.h

randomize
Función: Inicializa el generador de números aleatorios. Deberemos llamarlo al inicio de la función en que
utilicemos el random. También deberemos utilizar el include time.h, ya que randomize hace una llamada a
la función time, incluida en este último archivo.
Sintaxis: randomize( );
Prototipo: stdio.h

system
Función: Ejecuta el comando indicado. Esto incluye tanto los comandos del sistema operativo, como
cualquier programa que nosotros le indiquemos. Al acabar la ejecución del comando, volverá a la línea de
código situada a continuación de la sentencia system.

Sintaxis: system (<comando>); /* p.ej: system("arj a programa"); */


Prototipo: stdlib.h

97

También podría gustarte