Documentos de Académico
Documentos de Profesional
Documentos de Cultura
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++.
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:
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)
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++.
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:
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.
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.
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:
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.
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:
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
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.
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.
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.
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.
printf("Número: ");
scanf("%d", &num);
printf("El valor leído es: " %d\n", num);
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:
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;
system("PAUSE");
return EXIT_SUCCESS;
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++ 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:
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 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).
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]);
}
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.
#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);
}
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.
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:
#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)
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};
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:
struct trabajador
{
char nombre[20];
char apellidos[40];
int edad;
char puesto[10];
};
struct trabajador fijo, temporal;
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 notas
{
char nombre[30];
int notas[5];
};
struct notas alumno={"Carlos Pérez",{8,7,9,6,10}};
REGISTROS Y FUNCIONES
visualizar(fijo);
Ejemplo:
#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);
}
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:
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:
21
Su empleo con registros está especialmente indicado. Se puede hacer de varias formas:
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>);
FILE *pf;
pf = fopen("AGENDA.DAT","rb");
if ( pf == NULL )
printf ("Error al abrir el archivo");
else
fclose(pf);
Un carácter:
fputc(<variable_caracter>,<puntero_archivo>);
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);
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>);
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>);
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>);
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
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.
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.
remove(<nombre>): Como la función del DOS del, podremos eliminar el archivo indicado en nombre.
Ejemplo:
int potencia (int, int);
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.
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:
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);
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:
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::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:
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
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.
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.
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.
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);
}
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.
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++
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.
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";
#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.
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.
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:
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();
}
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.
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();
}
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;
}
#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;
}
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) _
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";
}
Sintaxis
<valor_devuelto><nombre_función> (<parámetros>){<cuerpo>;}
#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 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:
SINTAXIS:
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();
}
#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:
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”.
#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();
}
class <nombre_de_clase_hija>:<MDA>
<nombre_de_clase_madre>{
<cuerpo>;
};
#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*/
}
#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.
60
class <nom_derivada>:<MDA> <nomb_base1>,<nomb_base2>,<nomb_baseN<{
<cuerpo>; /*<nomb_base_n> = <nombre_clase_base_n>*/
};
#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;}
};
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.
#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();
}
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.
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.
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.
65
*(&h)=42;
/*Una vez declarado un puntero, se puede fijar la dirección o posición de memoria del tipo al que apunta.*/
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 *ip;
double *dp;
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.
<tipo> *<nombre_de_apuntador>(<variable>);
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.
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.
#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.
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>];
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.
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í:
Ejemplo:
printf ("%d", lista_nueva[1][0]);
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
Arreglos y Apuntadores.
A los arrays se accede a través de los índices:
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.*/
pa=&a[0];
/*y*/
71
/*pa+1 apunta al valor almacenado en a[1]*/
*(pa+1)=a[1];
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
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
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.
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”;
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:
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 = 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:
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;
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
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:
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++.
struct <nombre>{<miembro_1>;<miembro_2>;…;<miembro_n>};
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;
};
alfa w;
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):
#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
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:
ImprimirValores (n,a);
ImprimirValores (n); //equivalente a ImprimirValores (n, 0.0)
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:
Ejemplo:
#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:
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.
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.
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
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.
3. Por referencia. Se pueden pasar tipos referencia como argumentos defunciones, lo que permite
modificar los argumentos de una función.
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:
#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:
#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’;
}
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.).
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)
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.
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”;
}
#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;
}
#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.
85
#include <iostream.h>
int main ()
{
int i = 36;
cout << dec << i << oct << i << “ ” << hex << i << “\n”;
}
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;
}
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).
Los diferentes compiladores comerciales de C++ pueden incluir, además, nuevas palabras reservadas. Estos
son los casos de Borland, Microsoft y Sysmantec.
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
87
EJERCICIOS
Estructura DO...WHILE
Su sintaxis es:
Do
{
<sentencia1>;
<sentencia2>;
} while (<condición>);
Ejemplo:
#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>;
}
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';
int j=10;
cout<<i<<" j: "<<j<<'\n';
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;
}
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.
Ejercicio 5
# include "stdio.h"
# include "conio.h"
# include "stdlib.h"
int main ()
{
int v[10];
int i=0, x=0;
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.
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>);
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 */
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();
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.
97