Está en la página 1de 129

Programación C y C++ 1

Programación
Lenguaje C y C++

M. C. Gastón Dehesa Valencia


2 Programación C y C++

Derechos Reservados © 2002 por Gastón Dehesa Valencia

Todos los Derechos Reservados. Se autoriza para que este material pueda ser leida
y reproducida por los estudiantes que la requieran.

En este libro se han incluido programas y descripciones gráficas por su valor


educacional. Han sido debidamente probados pero no se garantizan para ningún
propósito en particular. El editor no proporciona ninguna garantía o representación,
ni acepta ninguna responsabilidad con respecto a los programas y descripciones
gráficas.

Muchos de los nombres utilizados por los fabricantes o vendedores de software para
distinguir sus productos son marcas registradas. El editor tiene toda la intención de
proporcionar la información de la marca registrada acerca de los fabricantes y los
productos mencionados en este libro. Una lista de las designaciones de marcas
registradas se muestra más adelante en esta página.

Marcas Registradas
C++ Builder es una marca registrada por Inprise Corporation.
Turbo C++ es una marca registrada por Borland International, Inc.
MS-Windows™ es una marca registrada por Microsoft Corporation
MS-DOS es una marca registrada por Microsoft Corporation
Visual C++ es una marca registrada por Microsoft Corporation

Dedico esta obra a mi familia


Por que representan una fuente de
Energía inagotable, que me impulsa
a emprender nuevos proyectos.
Programación C y C++ 3

Contenido

1.- Introducción al Lenguaje C


Historia del lenguaje
Estructura de un programa
Compilación y Ligado
Tipos de Datos y Modificadores
Operadores
Entrada y Salida de Datos
Especificadores de clase de almacenamiento
Ejercicios

2.- Sentencias de control de programa


Sentencias de Selección
Sentencias de Iteración
Sentencias de Salto
Ejercicios

3.- Arreglos y Apuntadores


Que son los arreglos
Arreglos Unidimensionales
Cadenas
Arreglos Bidimensionales
Apuntadores
Punteros y Arreglos
Inicialización de Apuntadores
Funciones de Asignación Dinámica
Ejercicios

4.- Funciones y Estructuras


Funciones
Pase de parámetros
Estructuras
Arreglos de Estructuras
Ejercicios

5.- Programación Orientada a Objetos


Que es la POO
Definición de una clase
Tipos de Accesos a una clase
Tipos de Usuarios
Relación entre Usuarios y Tipos de Accesos
Clases Amigas
Datos Estáticos
Constructores y Destructores
4 Programación C y C++

Ejercicios

6.- Herencia y Polimorfismo


Clases Derivadas
Herencia Simple y Múltiples
Problema y solución de la Herencia Múltiples
Constructores y Destructores en clases Derivadas
Polimorfismo
Sobrecarga de funciones
Funciones virtuales
Clases Abstractas
Ejercicios
Programación C y C++ 5

Prologo

Este libro puede ser utilizado como guía para el examen de admisión de la
Maestría en Informática que el Instituto en Computación y Electrónica
Dehesa ofrece en lo que se refiere a Programación.

Este libro esta organizado de la siguiente forma: En los cuatro primeros


capítulos se aborda la programación estándar y en los capítulos 5 y 6 los
conceptos básicos de la programación orientada a objetos.

Cada tema expuesto viene acompañado de uno o más ejercicios, el nombre del
archivo aparece como comentario al inicio de cada listado de programa. Los
programas que contiene el libro fueron probados usando compiladores de
Borland.

En archivo empaquetado que acompaña al libro encontrará un directorio por


cada unidad con los ejercicios desarrollados.

Espero que al terminar de leer este libro se cubran las expectativas planteadas,
cualquier comentario o sugerencia acerca del contenido del material lo puede
hacer a la siguiente dirección de correo electrónico: gastondv@hotmail.com
6 Programación C y C++

Capitulo 1

Introducción al Lenguaje C
• Historia del lenguaje
• Estructura de un programa
• Compilación y Ligado
• Tipos de Datos y Modificadores
• Operadores
• Entrada y Salida de Datos
• Especificadores de clase de almacenamiento
• Ejercicios
Programación C y C++ 7

HISTORIA DEL LENGUAJE

El Lenguaje C nació en los Laboratorios Bell de AT&T y ha sido


estrechamente asociado con el sistema operativo UNIX, ya que su desarrollo
se realizo en este sistema y debido a que tanto UNIX como el propio
compilador de C y la casi totalidad de los programas y herramientas de
UNIX, fueron escritos en C. Su eficiencia y claridad han hecho que el
lenguaje ensamblador apenas haya sido utilizado en UNIX.

Este leguaje está inspirado en el lenguaje B escrito por Ken


Thompson en 1970 con intención de recodificar el UNIX, que en la fase de
arranque estaba escrito en ensamblador, en vista a su transportabilidad a otras
maquinas. B era un lenguaje evolucionado e independiente de la máquina,
inspirado en el lenguaje BCPL concebido por Martín Richard en 1967.

En 1972 , Dennis Ritchie, toma el relevo y modifica el lenguaje B,


creando el lenguaje C y rescribiendo el UNIX en dicho lenguaje. La novedad
que proporciono él lenguaje C sobre el B fue el diseño de tipos y estructuras
de datos.

En 1980 Bjarne Stroustrup de los laboratorios Bell de Murray Hill,


New Jersey, inspirado en el lenguaje Simula67 adiciono las características de
la programación orientada a objetos (incluyendo la ventaja de una biblioteca
de funciones orientada a objetos) y lo denomino C con clases. Para 1983 dicha
denominación cambio a la de C++. Con este nuevo enfoque surge la nueva
metodología que aumenta las posibilidades de la programación bajo nuevos
conceptos.

Con la popularidad de las microcomputadoras se crearon muchas


implementaciones de C. En lo que se podría decir que era un milagro, los
códigos fuentes aceptados por la mayoría de esas implementaciones eran
altamente compatibles. Sin embargo, como no existía ningún estándar,
aparecieron discrepancias. Para remediar la situación, el Instituto de
Estándares Americano (ANSI) estableció un comité a mediados de 1983 para
crear un estándar que definiera al lenguaje C. Este comité ANSI termino el
proceso de formalización en 1990.

Actualmente muchas empresas se dedican a vender el Compilador del


lenguaje C, dos de estos imperios son:

Microsoft Visual C++ Ver 6.0


Borland C++ Builder 5.0

La mayoría de los compiladores actuales soportan la programación en


C Estándar, C Orientado a Objetos y la Programación Visual.
8 Programación C y C++

ESTRUCTURA DE UN PROGRAMA

Un programa escrito en lenguaje C tiene la siguiente estructura, aunque no


necesariamente debe contener todos los puntos:
1.- Definición de archivos de cabeceras
2.- Definición de constantes y variables globales
3.- Definición de Funciones del usuario
4.- Definición e implementación del programa principal ( main() )
5.- Implementación de funciones del usuario.

A manera de ejemplo considérese el siguiente programa:


//*Est_Prog.cpp

//Definición de archivos de cabecera


#include <conio.h>
#include <stdio.h>

//Definición de Constantes y Variables Globales


#define PI 3.1416
float Area;

//Definición de funciones del usuario


void Area_Cir(int R);

//Definición e implementación del Programa Principal


void main()
{int Radio;

clrscr();
printf("BIEN BENIDO AL LENGUAJE C");
printf("\n\n CALCULO DEL AREA DE UN CIRCULO");
printf("\n Radio: ");
scanf("%i",&Radio);
Area_Cir(Radio);
printf("\n El Area es; %8.2f",Area);
getch();

//Implementación de funciones del Usuario


void Area_Cir(int R)
{
Area= 2*PI*R*R;
}

La idea de presentar un programa no muy pequeño de entrada a


diferencia de como lo hacen otros autores es para que el estudiante en su
primer día conozca gran cantidad de información sobre el Lenguaje C.
El programa Est_Pro.cpp contiene las 5 partes del que se compone un
programa escrito en el lenguaje C. en la sección de Definición de archivos de
cabecera se presentan los archivos conio.h y el stdio.h ambos son archivos de
Programación C y C++ 9

cabecera definidos por el lenguaje, existen un gran numero de ellos que


acompaña al compilador según la versión y fabricante por lo regular hallados
en el directorio \Include, los archivos de cabecera definidos en esta sección
que pertenezcen al compilador deberán estar entre los signos < > y aquellos
que el propio programador cree para sus aplicaciones entre comillas “ “ lo
cual le indicara al compilador que busque el archivo en el directorio de
trabajo y no en el directorio por defauld de archivos de cabecera (include).
La siguiente lista muestra algunos de los archivos de cabeceras que
más se usan así como una breve descripción de su contenido.
<stdio.h> para uso de funciones de entrada/salida standard.
<iostream.h> para uso de funciones de entrada/salida por flujos.
<conio.h> para uso de funciones de entrada/salida por teclado o
consola
<alloc.h> para la asignación dinámica de memoria
<graphics> para uso de funciones relacionadas con gráficas
<io.h> para uso de funciones de entrada/salida de bajo nivel
<math.h> para uso de funciones matemáticas.
<mem.h> para uso de funciones de manipulación de memoria.
<string.h> para uso de funciones relacionadas con cadenas
<time.h> para uso de funciones de hora y fecha.

Para definir un archivo de cabecera se usa una directiva de


preprocesamiento llamado #include seguido del archivo de cabecera
encerrado entre signos < > ó “ ” según el lugar donde este se encuentre, de
esta forma el compilador incluirá durante la compilación las variables,
constantes y funciones que se encuentren en el archivo y que sean usados por
el programa en desarrollo.

En la sección de definición de constantes y variables globales se encuentra


otra directiva de preprocesamiento conocido como #define que permite
definir la constante, observe que después del identificador PI no existe un
signo =, aunque el significado es que el identificador PI toma el valor de
3.1416, para la definición de las variables globales basta con indicar su tipo de
dato y el identificador seguido de punto y coma, aquí se define Area de tipo
real (float), recuerde que una variable global perdura a lo largo de la ejecución
de todo el programa y puede ser vista, modificable desde cualquier parte del
programa o función.

En la sección de definición de funciones del usuario se requiere


definir todas las funciones que se implementen por el programador, por que
solo de esta forma el compilador puede conocer de forma adelantada la
existencia de los mismos, es decir antes de llamar a una función primero el
compilador necesita saber de su existencia en caso contrario no podría
resolver esta referencia provocando un error en tiempo de compilación, el
lenguaje C es un lenguaje estructurado lo cual significa que un programa
según su tamaño se dividirá en varios módulos conocidos como funciones, de
esta forma se evita que la función principal este saturada de código. En este
10 Programación C y C++

ejemplo se define solamente una función, Area_Cir() sin tipo de retorno


(void) y recibe como parámetro un entero( El radio del circulo).

La función principal (main()) es el primero que se ejecuta cuando


inicia un programa, por lo que es indispensable que éste exista, en él se
establece la lógica o secuencia que seguirá el programa durante su ejecución.
Una descripción de lo que ocurre en el ejemplo es el siguiente:
Se define una variable local Radio de tipo entero
Borra la pantalla
Se Imprimen mensajes en pantalla
Lee un dato de tipo entero almacenándolo en la variable Radio
Llama a la función Area_Cir() pasándole como argumento el Radio leído
Imprime la variable Area calculada en la función
Espera la entrada de una tecla para terminar

Como puede observarse el orden de los comandos es el orden o


secuencia en que estos se ejecutan.

Finalmente en la sección de Implementación de funciones se tendrá


la implementación de todas las funciones que el programador halla definido
en la sección correspondiente, en este caso la función Area_Cir() solamente
tiene una línea de código que se encarga de asignar a la variable global Area ,
el resultado de multiplicar 2*3.1416*R*R , si el usuario introdujo un Radio =5
entonces sería: 2*3.1416*5*5.

Si una función se define y se llama desde alguna parte del programa y


no esta implementado, se detectara un error en tiempo de enlace y no en
tiempo de compilación tema que se tratará en la siguiente sección.
Programación C y C++ 11

COMPILACIÓN Y LIGADO

Para crear un archivo ejecutable (Código maquina relocalizable) se realiza


mediante dos etapas o tiempos que son: Compilación y ligado.
Compilación.- Durante este proceso se examina el o los programas
fuente de la aplicación, los archivos de cabecera para hallar errores de
sintaxis, de no existir se genera un archivo objeto (.OBJ) por cada programa
fuente.
Ligado.- Durante este proceso se unen todos los programas objetos
del que se conforme la aplicación, así como los archivos de librería (.LIB)
para formar un solo programa independiente, o programa ejecutable (.EXE).
Para aclarar lo anterior observe la siguiente figura:

Princip.cpp Archivo2.cpp

stdio.h conio.h

COMPILACION

Princip.obj Archivo2.obj
Cl.lib Mathl.lib
LIGADO

Extern.exe

Figura 1. 1 Proceso de Compilación y Ligado de un programa en C

La implementación de las funciones halladas en los archivos de


cabecera predefinidos por el lenguaje se encuentran en librerías (Mathl.lib,
Cl.lib, etc.) incluidos también en el software del compilador, por lo que se
integran al programa en tiempo de enlace, es por esta razón que si una función
definido por el usuario no esta implementada en el programa ni tampoco en
una librería se detectará en tiempo de enlace.

De lo anterior se deduce entonces que un archivo de cabeceras (*.h) es


diferente de un archivo de librerías (*.Lib), lo cual es muy cierto y veamos
algunas diferencias:

Característica Archivo de Cabecera Archivo de Librería


Tipo Texto Binario
Cuando se usa Tiempo de Compilación Tiempo de ligado
Contenido Definiciones Implementaciones
compiladas

Tabla 1. 1 Diferencias entre un archivo de Cabecera y uno de Librería


12 Programación C y C++

Es frecuente que se confundan los términos archivo de librería con


archivo de cabecera, la tabla y dibujo anterior precisamente tiene la finalidad
de hacer notar que son diferentes. Durante la escritura de un programa es
frecuente probar una y otra vez si se esta haciendo lo correcto, para ello se usa
la ejecución paso a paso de un programa (Depuración), en ocasiones algunos
archivos de cabecera se cargan en el ambiente de programación y se debe
tener la precaución de no modificarlos para no producir incongruencias entre
el archivo de cabecera y el archivo de librería, al final de cuentas están
directamente relacionados entre si .

TIPOS DE DATOS Y MODIFICADORES

Todo lenguaje de programación suministra al diseñador de software un


conjunto de tipos de datos y un conjunto de instrucciones, el diseñador a partir
de estas puede crear nuevos tipos y nuevas instrucciones.

Palabras reservadas

Las palabras reservadas del compilador son todas aquellas palabras que se
usan para especificar tipos de variables, estructuras cíclicas, estructuras de
decisión, etc., y que no pueden ser usadas por el usuario como nombre de
variables, de funciones o de nuevos tipos .Estas palabras son las siguientes:
asm delete goto public this
auto do huge register union
break double if return unsigned
case else int Short virtual
carch enum interrupt signet void
char extern long sizeof volatile
class far near static while
const float new struct
continue for private switch
defauld friend protected template

Tabla 1. 2 Palabras Reservadas del Lenguaje

Las palabras reservadas deben escribirse siempre en minúsculas.

Tipos de datos

Un tipo de dato en C es la manera como el compilador almacena la


información en la memoria, por ejemplo el sistema operativo MS-DOS es un
sistema de 16 bits, mientras que Windows 98, Windows 2000, Windows NT
son de 32 bits, así que el compilador para cada uno de estos sistemas
operativos almacenara en algunos casos un mismo tipo de dato con longitud
diferente, más adelante se presenta una tabla de los tipos de datos para un
sistema de 16 bits.
Programación C y C++ 13

C se distingue por ser un lenguaje con demasiados tipos, existen otros


lenguajes como PASCAL que cuentan con unos pocos tipos, esta variedad de
tipos nos obliga a ser demasiado cuidadosos a la hora de usar algunos
operadores de asignación.
Antes de empezar a describir los tipos disponibles en el lenguaje
estudiemos un poco lo que es una variable.
Las variables siempre pertenecen a un tipo de datos, este tipo de datos
puede ser alguno de los tipos predefinidos por el lenguaje o definido por el
usuario, algunos tipos predefinidos para un sistema de 16 bits son.

Tipos de datos # de Bits Intervalo


char 8 -127 a 127
int 16 -32768 a 32767
long 32 0 a 4294967295
float 32 Aprox. 6 dígitos de precisión
double 64 Aprox. 12 dígitos de precisión
void 0 Sin valor

Tabla 1. 3 Tipos de Datos Básicos de un sistema de 16 bits

Midificadores

Los modificadores son palabras reservadas que se le adicionan a los tipos para
modificarles su tamaño y/o su rango, estos son:
Signed
unsigned
long
short
Estas son algunas de las combinaciones que se pueden presentar:

Tipo # de Bits Intervalo


unsigned char 8 0 a 255
Char 8 -127 a 127
unsigned int 16 0 a 65535
short int 16 Lo mismo que int
Int 16 -32,768 a 32,767
unsigned long int 32 0 a 4,294,967,295
long int 32 -2,147,483,648 a 2,147,483,647
Float 32 3.4E-38 a 3.4E38
double float 64 1.7E-308 a 1.7E308

Tabla 1. 4 Tipos de Datos con modificadores en un Sistema de 16 Bits

Como se puede observar cada tipo tiene un tamaño en bits, este tamaño
define un rango de operación que limita los números que se puede almacenar
en una variable. imagínese que se desea diseñar un programa de nómina que
14 Programación C y C++

va ha manejar los sueldos de los empleados, piense en el tipo de datos que


usaría para la variable sueldo; seguramente pensó en un tipo unsigned int, si
observamos la tabla anterior el tipo unsigned int almacena valores entre 0 y
65535, ¿será el tipo unsigned int el más apropiado? Bueno tal vez contestará
que no, ya que los sueldos pueden ser superiores a 65535, tal vez el tipo más
apropiado será unsigned long que almacena valores entre 0 y 4294967295.
Bueno, pero que pasaría si escogiéramos el tipo unsigned int para la variable
sueldo, en el momento que almacenemos un valor superior a 65535 el
contenido quedará truncado.
Para el caso de un Sistema Operativo de 32 bits, el compilador
almacenaría los datos con tamaños diferentes, este sería el caso:

Tipo # de Bits Intervalo


unsigned char 8 0 a 255
char 8 -128 a 126
unsigned int 32 0 a 4,294,967,295
short int 16 -32,768 a 32,767
int 32 -2,147,483,648 a 2,147,483,648
unsigned long int 32 Lo mismo que unsigned int
long int 32 Lo mismo que int
float 32 1.2E-38 a 3.4E381
double float 64 2.2E-308 a 1.8E3082

Tabla 1. 5 Tipos de Datos con moficadores en un sistema de 32 bits

Al examinar la tabla anterior se puede observar que int es lo mismo


que long int mientras que para un sistema de 16 bits estos tienen diferente
longitudes y por lo tanto diferentes intervalos.
Como se deduce en consecuencia que la longitud en bytes de un tipo dato
depende del sistema operativo y del compilador que se use, para conocer con
precisión la longitud en bytes de una variable de un tipo de dato se puede
utilizar el operador sizeof. Por ejemplo:
int x;
x=sizeof(int));
En un compilador operado en el sistema operativo MS-DOS x tomaría
el valor de 2, mientras que en Windows 98 el valor de 4.

En este libro con la finalidad de evitar las complicaciones del ambiente


del compilador se abordarán los aspectos básicos de la programación estándar
y orientado a objetos usando un compilador que se ejecuta bajo el MS-DOS
(Hasta el capitulo 6), la parte restante del libro estará basado en el ambiente
Windows para conocer la programación Visual con C++.

Comentarios
Programación C y C++ 15

Un comentario es una secuencia de caracteres utilizada para explicar el código


fuente y no se toma en cuenta durante la compilación (No se considera como
código). Existen dos formas para definir un comentario:
• Secuencia de caracteres encerrados entre los símbolos /* y */
• Comienza con los caracteres // y termina al final de la línea.

Variables

Es una posición de memoria de cierta longitud según el tipo de dato cuyo


valor puede ser modificado a lo largo de la ejecución de un programa .
Sintaxis:
[clase] tipo identificador[,identificador];
Donde:
clase representa un especificador de clase de almacenamiento (se
explica posteriormente).
tipo determina el tipo de variable (char, int, float, etc.)
identificador Indica el nombre de la variable.
Una variable que se declara al inicio del programa es considerada
como variable global y es accesible desde cualquier parte del programa. Por
lo contrario una variable declarada dentro de una función se considera como
variable local y es accesible solamente dentro de este.

Cada variable de un programa, debe declararse antes de ser utilizada.


La declaración consiste en enunciar el nombre de la variable y asociarle un
tipo. El tipo determina los valores que puede tomar así como las operaciones
que con ella pueden realizarse.
Ejemplos:
int Suma, Promedio;
char Car, Nombre[40];

Una variable puede ser inicializada cuando se declara:


char car = ’\0’ ;
int Incremento = 10;
char Archivo[] = ”Datos.dbf”;

Conversión de Tipos de datos

Cuando se trabaja con datos de diferentes tipos es frecuente mezclarlos


durante una expresión o asignación, a diferencia de otros lenguajes, C realiza
la conversión de tipos compatibles de manera automática.
Por ejemplo:
int x=92; //Se declara la variable x de tipo entero inicializándola con 92
char car=x; //car es de tipo carácter y se le asigna el valor de un entero x
En este ejemplo se da la conversión automática de tipos, es decir a la
variable car se le asigna el carácter ’\’ , que es el equivalente del código
ASCII.
16 Programación C y C++

Cuando se presenten casos de conversión de tipos se debe tener en


cuenta que es posible la perdida de datos. Supóngase que x=256 en lugar de
92, en este caso car tomaría el valor de ‘\0’, es decir el carácter nulo. Esto se
debe a que un entero es de 16 bits (en MS-DOS) mientras que un char es 8
bits, por lo tanto los 8 bits más significativos del entero se perderán.
No todos los compiladores soportan la conversión de tipos durante las
asignaciones, algunos solamente lo soportan durante una expresión este es el
caso del C++ Builder, por ejemplo:
int x;
x= Edit1->Text; //Error No se puede convertir una cadena a entero

Como Edit1->Text es una propiedad de tipo cadena (Arreglo de


caracteres) no se puede llevar a cabo la conversión de manera directa, para
solucionar este problema la asignación se debe realizar de manera indirecta:

x = Edit1->Text*1; //OK La conversión se da en la expresión.

OPERADORES

Los operadores se utilizan para manipular datos: ejecutan cálculos, buscan


igualdades, realizan asignaciones, trabajan con variables y se encargan de
muchas otras tareas que los programadores nunca llevan a cabo. En la
siguiente tabla se presentan los de uso más común .

Operador Descripción Ejemplo Operador Descripción Ejemplo


OPERADORES ARITMÉTICOS OPERADORES DE ASIGNACIÓN
+ Adición x=x+z; = Asignación x=10;
- Sustracción x=x-z; += Asignar y sumar x+=10;
* Multiplicación x=x*z -= Asignar y restar x-=10;
/ División x=x/z; *= Asignar y multiplicar x*=10;
% Modulo o resto x=x%z; /= Asignar y dividir x/=10;
OPERADORES LÓGICOS &= Asig. operador AND x&=0x02;
&& AND Lógico if (x&&0xFF) |= Asignar operador OR x|=0x02;
|| OR Lógico if (x||0xFF) OPERADORES A NIVEL DE BITS
! NOT Lógico If (!Soltero) & AND a nivel de bits C=A&B;
OPERADORES RELACIONALES | OR a nivel de bits
== Igual que if (x==y) ∧ XOR a nivel de bits C=A∧B;
!= Diferente que << Desp. a la izquierda B=A<<3;
< Menor que >> Desp. a la derecha C=A>>2;
> Mayor que ~ NOT a nivel de bits A=~D;
<= Menor o igual que OPERADORES DE APUNTADORES
>= Mayor o igual que * Indirección Int *Ap;
OPERADORES DE CLASE Y ESTRUCT. & Dirección x=&Ap;
:: Resol. de alcance Punto::Dibujar() OTROS
-> Miembro indirecto P->Dibujar(); ++ Incremento x++;
. Miembro directo P.Borar(); -- Decremento x--;

Tabla 1. 6 Operadores de C++ más usados


Programación C y C++ 17

Prioridad y orden de Procedencia

La prioridad de los operadores define el orden en que se evalúa una expresión


cuando tiene distintos operadores. C y C++ tienen reglas específicas para
determinar el orden de evaluación. Lo más sencillo de recordar es que la
multiplicación y división tienen prioridad sobre la suma y la resta. A partir de
esto, si una expresión no le resulta clara, tampoco lo será para alguien que lea
el código, por lo que deberá utilizar paréntesis para hacer más explicito el
orden de evaluación. Por ejemplo:
A= x + y – 2 / 2 + z;
Tendrá un significado distinto dependiendo de cómo se agrupen los
paréntesis:
A= x + (y - 2) / (2 + z);

En la siguiente tabla lista los operadores de C y C++ de mayor a menor


prioridad, y describe como se asocia cada operador (de izquierda a derecha o
de derecha a izquierda). Todos los operadores situados entre líneas tienen el
mismo nivel de prioridad.

Nivel Operadores Asociatividad


1 () [] -> :: . Izquierda – Derecha
2 ! ~ ++ -- & * Derecha . Izquierda
3 * / % Izquierda – Derecha
4 + - Izquierda – Derecha
5 << >> Izquierda – Derecha
6 < <= > >= Izquierda – Derecha
7 = != Izquierda – Derecha
8 & Izquierda – Derecha
9 ∧ Izquierda – Derecha
10 | Izquierda – Derecha
11 && Izquierda – Derecha
12 || Izquierda – Derecha
13 = *= -= *= %= < <= > >= &= ∧= |= Derecha – Izquierda
14 , Izquierda – Derecha

Tabla 1. 7 Prioridad y Asociatividad de operadores de C++

Ejemplos:
a).- 8/4*6 8*6/4 28/(3*4)
2*6 48/4 28/12
12 12 2
b).- 3/4*6 3*6/4
0*6 18/4
0 4
c).- (float) 2/4
2.0/4
0.5
d).- -3+4% 5/2
-3+4/2
-3+2
-1
18 Programación C y C++

ENTRADA Y SALIDA DE DATOS (ESTÁNDAR Y FLUJOS)

Prácticamente todos los programas requieren procesar información


proveniente de fuentes externas para obtener la solución de un problema
específico. Esta información constituye los datos de entrada del algoritmo que
definen nuestro programa y es mediante ellos que se obtendrá la salida o
solución correspondiente:
De ahí la importancia de proporcionar adecuadamente los valores o
datos de aquellas variables del programa que deba leer la computadora;
además de la importancia de incluir mensajes en las ordenes de salida que nos
indiquen la naturaleza o nombre de los resultados obtenidos.

El sistema operativo MS-DOS soporta dos métodos de entrada y salida de


datos que son:
• Entrada y Salida Estándar
• Entrada y salida por flujos
El sistema operativo Windows la entrada y salida por pantalla lo hace a
través de un conjunto de controles visuales, los cuales se analizaran
posteriormente. En esta sección se analizan los dos primeros métodos.

Entrada y Salida Estándar

Para la entrada y Salida de datos con formato se utilizan dos funciones


hallados en el archivo de cabecera stdio.h : printf() y scanf().

Salida por pantalla


Printf(char *cadena_de_control, Lista_argumentos);

La cadena de control está formada por dos tipos de elementos . El


primer elemento es el carácter que se mostrará en la pantalla. El segundo
elemento contiene especificadores de formato que definen la forma en que se
muestran los argumentos posteriores. El espicificador de formato empieza
con un % y va seguido por el código del formato. Debe haber exactamente el
mismo número de argumentos que especificadores de formato y ambos deben
coincidir en su orden de aparición de izquierda a derecha.
En la siguiente tabla se muestra una gran variedad de especificadores
de formato.
Código Formato
%c Carácter
%d Entero en decimales con signo
%i Entero decimales con signo
%e Notación científica
Programación C y C++ 19

%f Punto flotante)
%h Entero corto
%o Octal sin signo
%s Cadena de caracteres
%x Hexadecimal sin signo

Tabla 1. 8 Especificadores de Formato

Ejemplo:

printf(“El Lado es: %i El Area del Cuadrado es: %f”,Lado, Area);

Si la variable Lado=5 por lo tanto la variable Area=25, así el mensaje sería:


El Lado es: 5 El Area del Cuadrado es: 25
También en la cadena de control pueden existir una serie de caracteres
que permitan formatear la salida, conocidos como secuencia de escape

Secuencia de escape Acción realizada


\a Alarma
\b Retroceso
\f Avance de pagina
\n Nueva Linea
\r Retorno de carro
\t Tabulación (horizontal)
\v Tabulación (vertival)
\\ Barra inclinada

Tabla 1. 9 Secuencias de Escape

Entrada por teclado


Scanf(char *cadena_de_control, Lista_argumentos);

Esta función direcciona automáticamente la lectura de valores por el


teclado de la microcomputadora. Como se aprecia el formato es idéntico al
printf().

El lenguaje C emplea apuntadores en lectura de toda cantidad


numérica debido a que asigna dichos valores a través de las direcciones de las
variables, por tal motivo los nombres de ellas van precedidas del símbolo &
(con lo que se indica que se usan apuntadores). En el caso de cadenas, los
nombres de las variables son en si mismos apuntadores y no requieren dicho
símbolo.

Ejemplo:
20 Programación C y C++

/*ES-Estan.cpp
Muestra el uso de las funciones printf() y scanf()
*/
#include <conio.h>
#include <stdio.h>

void main()
{char Nom[40];
int Edad;
float Peso;

clrscr();
printf("IDENTIFIQUESE POR FAVOR");
printf("\n\n NOMBRE: ");
scanf("%s",Nom);
printf("\n EDAD: ");
scanf("%i",&Edad);
printf("\n PESO: ");
scanf("%f",&Peso);

printf("\n\n SUS DATOS SON: ");


printf("%s %i %4.2f",Nom,Edad,Peso);
getch();
}

La Salida del programa después de ejecutarla sería:


IDENTIFIQUESE POR FAVOR
NOMBRE: Juvenal
EDAD: 36
PESO: 50.4

SUS DATOS SON: Juvenal 36 50.40

Observe que al leer las variables Edad y Peso de tipo entero y real
respectivamente se utiliza el operador & que indica dirección, mientras que la
variable Nom que es un arreglo de caracteres (cadena) no lo requiere, la
función printf() hace uso frecuentemente de la secuencia de escape \n para
cambiar de línea, por ejemplo el penúltimo printf() hace que se deje una línea
en blanco.

Existen en el archivo de cabecera stdio.h una serie de funciones que


permiten específicamente leer y escribir datos de tipo carácter y cadena, no es
la finalidad de este libro profundizar en este tema más sin embargo se
considera necesario conocer de su existencia. En la siguiente tabla se
sintetizan:
Función Operación
getchar() Lee un carácter del teclado; espera un salto de carro.
getche() Lee un carácter con eco; no espera un salto de carro.
getch() Lee un carácter sin eco; no espera un salto de carro.
putchar() Escribe un carácter en pantalla.
gets() Lee una cadena del teclado.
puts() Escribe una cadena en pantalla.
Tabla 1. 10 Funciones complementarias de E/S
Programación C y C++ 21

Entrada y Salida por flujos

C++ proporciona el archivo de cabecera iostream.h que contiene funciones


que realizan operaciones de Entrada/Salida, este archivo tiene la ventaja sobre
el stdio.h por ser una implementación orientada a objetos, dos de estos
objetos son: cin y cout. Donde:
• cin Usado para entrada de datos
• cout Usado para salida de datos
Para el manejo de de estos dos objetos se utilizan dos operadores:
• El operador de inserción (<<) transmite sus argumentos situados a la
derecha hacia el flujo cout.
• El operador de extracción (>>) lee informaciones del flujo cin a la
izquierda del operador y las almacena en las variables indicadas a la
derecha.

A manera de ejemplo rescribiremos el programa ES-Estan.cpp pero en


lugar de usar printf() y scanf() se usara cout y cin.

/*ES-Flujo.cpp*/

#include <conio.h>
#include <iostream.h> //En lugar de stdio.h

void main()
{char Nom[40];
int Edad;
float Peso;

clrscr();
cout<<"IDENTIFIQUESE POR FAVOR";
cout<<"\n\n NOMBRE: ";
cin>>Nom;
cout<<"\n EDAD: ";
cin>>Edad;
cout<<"\n PESO: ";
cin>>Peso;

cout<<"\n\n SUS DATOS SON: ";


cout<<Nom<<" "<<Edad<<" "<<Peso;
getch();
}

La Salida del programa después de ejecutarla sería identica al


a salida del programa ES-Estan.cpp

Cuando se lee un arreglo de caracteres como la variable Nom puede


ocurrir que el usuario escriba más de una palabra (Una línea), en cuyo caso no
sería conveniente utilizar cin sola, ya que este solamente tomaría la primera
22 Programación C y C++

palabra. Para leer más de una palabra se puede utilizar la función getline() de
la siguiente forma:
cin.getline(Cadena, num);
Donde:
Cadena es cualquier arreglo de caracteres que se haya definido con
anterioridad y que contiene la cadena de entrada.
Num es el número máximo de caracteres que desea leer con getline()
Ejemplo:
cin.getline(Nom, 40); //Se puede almacenar hasta 40 caracteres en Nom

En la penúltima línea se observa que se pueden concatenar varios operadores


<< en una sola línea. Otro aspecto importante de los flujos, es que los
operadores << y >> efectúan automáticamente las conversiones necesarias.

Los ejemplos que se mostraran a partir de esta parte del libro en adelante,
usarán el cout y el cin para la entrada y salida de datos en pantalla. Sin
embargo al abordar el tema de la programación visual se dejará de usar.

ESPECIFICADORES DE CLASE DE ALMACENAMIENTO

Aunque este tema esta ligado más con el tema de Variables se dejo para esta
sección porque se requiere de los conocimientos anteriores para comprenderlo
con más claridad.

Existen cuatro especificadores de clase de almacenamiento sopórtados por C:


• extern
• static
• register
• auto

Los especificadores le indican al compilador cómo debe almacenar la


variable. La clase de almacenamiento precede a la declaración de la variable.
Su forma general es:
[clase] tipo identificador[,identificador];

• extern

Dado que C permite enlazar juntos módulos de un programa, compilados por


separado con el fin de acelerar la compilación y ayudar a la gestión de grandes
proyectos, debe haber alguna forma de hacer conocer a todos los archivos las
variables globales requeridas por el programa, las variables globales se deben
de declarar sólo una vez. Si se declaran dos variables globales con el mismo
nombre en el mismo archivo, el compilador imprimirá un mensaje de error
indicando que se ha duplicado un nombre de variable, lo que significa que el
compilador no sabe que variable se va ha usar cada vez. Lo mismo ocurre si
declaran todas las variables globales necesitadas por el programa en cada
archivo. Aunque el compilador no daría ningun mensaje de error en tiempo de
Programación C y C++ 23

compilación , realmente se intenta crear dos o más de cada variable. El


problema aparecería al intentar enlazar los modulos. El Enlazador mostraría el
mensaje de error “Identificador duplicado” por que no sabría que variable
usar. La solución está en declarar todas las variables globales en un archivo y
usar declaraciones extern en los otros.
Ejemplo:
/*Princip.cpp proyecto:exten.prj /*Archivo2.cpp proyecto:exten.prj
*/ */
#include <conio.h> #include <conio.h>
#include <iostream.h> #include <iostream.h>

float Prom; extern float Prom; //Ojo

void Promedio(); void Promedio()


{int C1,C2,C3;
void main() cout<<"\nESCRIBA LAS 3
{ CALIFICACIONES DEL ALUMNO";
clrscr(); cout<<"\n\nI UNIDAD: ";
cout<<"\nCALCULO DEL PROMEDIO DE cin>>C1;
CALIFCACIONES"; cout<<"\nII UNIDAD: ";
Promedio(); cin>>C2;
cout<<"\n\nEl Promedio es: cout<<"\nIII UNIDAD: ";
"<<Prom; cin>>C3;
getch(); Prom=(C1+C2+C3)/3;
} }
La Salida del programa después de ejecutarla sería:
CALCULO DEL PROMEDIO DE CALIFCACIONES
ESCRIBA LAS 3 CALIFICACIONES DEL ALUMNO

I UNIDAD: 80
II UNIDAD: 90
III UNIDAD: 100

El Promedio es: 90

El programa anterior esta compuesto por los archivos Princip.cpp y


Archivo2.cpp la variable global Prom se encuentra dafinida en el archivo
Princip.cpp sin embargo se utiliza tambien en el Archivo2.cpp, para indicar
al compilador que se trata de la misma variable se usa el expecificador extern
en su declaración. Observe que la función Promedio() se define unicamente
en Princip.cpp y se implementa en Archivo2.cpp, esto no trae ningún
problema durante la compilación porque es durante el enlace que se resuelve
la referencia hacia esta función.
Tal vez se preguntará ¿pero como se puede lograr que dos archivos
fuentes conformen un solo programa?. Se puede llevar a cabo con cualquiera
de los dos métodos: El primero consiste en realizar la compilación y enlazado
mediante la línea de comandos del MS-DOS el segundo creando un proyecto
de esta aplicación para que los detalles los efectúe el propio ambiente del
software.
El primer método es un tanto rudimentario ya que consume una gran
cantidad de tiempo para llevarlo a cabo, sin embargo el segundo es mucho
más eficiente y es el que se explicará a continuación.
24 Programación C y C++

Creación de Proyectos

En la mayoría de los ambientes de programación modernos se considera este


método, los pasos a seguir diferirán unos de otros dependiendo del fabricante
y versión, a manera de ejemplo se explicará como se lleva a cabo la creación
de proyectos usando el Software de Borland ver. 1.0 para MS-DOS, para
poder compilar y enlazar de manera directa y transparente para el
programador el programa Extern.prj. El nombre que se le da a un proyecto
será el mismo que tendrá el programa ejecutable despues de su compilación y
ligado. Pasos:

1.- Crear un directorio de trabajo estando en el directorio del compilador


C:\tc>md ejerc-I

2.- Ejecutar el programa del compilador


C:\tc>tc

3.- En el ambiente definir como directorio de trabajo el directorio recien


creado
Seleccionar File | Change dir... y escribir en la caja de dialogos c:\tc\ejerc-I y OK

4.- Crear el proyecto, por defauld se guarda en el directorio de trabajo


Seleccionar Project | Open Project… escribir en la caja de dialogos extern.prj y
seleccionar el botón OK

5.- Agregar los archivos fuentes al proyecto


Estando en la ventana project oprimir la tecla <Insert> en la caja de dialogos escribir
el nombre del primer archivo fuente: Princip.cpp y seleccionar el boton “Add” se
observa como el nombre del archivo aparece ahora en la ventana projects, escribir el
nombre del segundo archivo: Archivo2.cpp y seleccionar “Add”, este proceso puede
seguir para cada archivo fuente que se desee agregar, para el programa anterior con
esto basta.

6.- Escribir el código en cada programa fuente


Para abrir un archivo fuente que pertenesca al proyecto basta con posicionarse
sobre él en la ventana Project y obrimir <Enter> se abre la venta de edición donde se
podrá escribir todo el código necesario como muestra la siguiente figura, no olvide
guardar los cambios que se realizan
Programación C y C++ 25

Figura 1. 2.- Ventana de BorlandC 1.0 para manejo de proyectos

7.- Ejecutar el programa


Con las teclas <Ctrl>+<F9> se compila, enlaza y ejecuta el programa si no
aparecen errores.

• static

Las varibles static son variables permanentes dentro de una función o archivo
según si se declaran como locales o globales respectivamente.

Variables static locales

Cuando se aplica el modificador static a una variable local, el compilador crea


un almacenamiento permanente para ella de forma muy parecida que cuando
crea almacenamiento para una variable global. La diferencia clave entre una
variable local static y una variable global es que la variable local static es
una variable local que retiene su valor entre llamadas de funciones.
Ejemplo:
/*Estatic1.cpp */
#include <conio.h>
#include <iostream.h>

void serie();

void main()
{int x;
clrscr();
for (x=0;x<5;x++)
serie();
getch();
}
26 Programación C y C++

void serie()
{static int cont=0; //Variable Local static
cont=cont+10;
cout<<"\t"<<cont;
}
La salida de este programa será:
10 20 30 40 50

Aunque aun no se ha abordado el tema de la instrucción for se utiliza


en el programa para ilustrar el efecto del especificador static. Cuando se llama
a la función serie() por primera vez se crea el espacio de almacenamiento para
la variable cont y se inicializa con 0, la siguienta línea la incrementa a 10 y es
el primer valor que se visualiza despues de un tabulador, en la siguiente
llamada a la función serie() como no se destruye la variable cont este retiene
su valor, al incrementarse durante la segunda llamada a la función serie()
ahora tendrá 20 y así sucesivamente.
De no definir la variable cont como static (int cont=0; ) cada vez que se llame a
la función serie() se creara un espacio para el y al finalizar se destruirá por lo
que el resultado sería:
10 10 10 10 10

Las variables static son muy importantes en la creación de funciones


independientes, ya que existen varios tipos de rutinas que deben preservar su
valor entre llamadas. Si no se permitiera usar static entonces habría que usar
variables globales y las variables globales dejan la puerta abierta a posibles
efectos secundarios.

Variables static globales

Cuando se aplica el modificador static a una variable global, se indica al


compilador que cree una variable global conocida únicamente en el archivo en
el que se declara. Esto significa que aunque la variable es global, las rutinas de
otros archivos no la reconocerán ni alterarán su contenido directamente,
estando así libre de efectos secundarios.
Ejemplo:
/*Staticg1.cpp, Static.prj */ /*Staticg2.cpp, Static.prj */

#include <conio.h> static int cont; //ojo


#include <iostream.h>
void Inicia_Semilla(int i)
void Inicia_Semilla(int i); {
int serie(); cont=i;
}
void main()
{int x; int serie()
clrscr(); {
Inicia_Semilla(0); cont=cont+10;
for (x=0;x<5;x++) return(cont);
cout<<"\t"<<serie(); }
getch();
}
Programación C y C++ 27

En el archivo Staticg2.cpp se define la variable cont como variable global


static, lo cual impide que se pueda acceder desde otro archivo, este será vista
y modificada solamente en el propio archivo, por eso se requiere de una
función adicional para poder inicializarla desde otro archivo. La salida del
programa será exactamente igual que el archivo Estatic1.cpp aunque usando
una técnica diferente.

• register
El especificador de almacenamiento register originalmente pedía al
compilador que mantuviera el valor de una variable en un registro de la CPU
en lugar de en memoria, que es donde se almacena normalmente las variables.
Esto significaba que las operaciones debían de realizarse con mayor rapidez.
Actualmente el estándar ANSI simplemente establece “que el acceso al objeto
sea lo más rápido posible”, lo anterior es aplicado a cualquier tipo de dato.
El especificador registers solo puede aplicarse a variables locales y a
los parámetros de una función. Ejemplo:

void imprime_Caracter()
{
register int x;
for (x=0; to N; x++)
cout <<leer_puerto();
}

En este ejemplo se imprimen N caracteres en pantalla retornados por


una función Leer_puerto(), al definir x con el especificador register el ciclo se
lleva a cabo lo más rápido que se pueda.

• auto
Cuando se define una variable sin indicar su especificador, por defauld su
especificador es auto. El especificador de almacenamiento auto declara una
variable con existencia local. Esta es visible solamente en el bloque en el que
es declarada.
28 Programación C y C++

EJERCICIOS RESUELTOS

1.- Usando la entrada salida estándar escribir un programa que calculé el área
de un triangulo a partir de su base y altura usando la formula:
Area=(Base*Altura)/2.

/* Area.cpp */

#include <stdio.h>
#include <conio.h>

void main ()
{
float Altura,Base,Area;
clrscr();
printf ("CALCULA EL AREA DE UN TRIANGULO.");
printf("\n\nTECLEE LA BASE: ");
scanf("%f",&Base);
printf("\nTECLEE LA ALTURA: ");
scanf("%f",&Altura);
Area=(Base*Altura)/2;
printf("\nEl EL AREA ES: %8.2f", Area);
printf("\nPRESIONE <ENTER> PARA TERMINAR");
getch();
}

2.- Usando la entrada salida estándar escribir un programa que calcule el


sueldo de un empleado, solicitando su nombre, Número de horas trabajadas y
el costo por hora.
/* Sueldo.cpp

# include <stdio.h>
# include <conio.h>

void main ()
{
char nombre[40];
float chr, hrst, Sueldo;
clrscr();
printf ("\n CALCULA EL SUELDO DE UN EMPLEADO");
printf ("\n\n NOMBRE DEL EMPLEADO: ");
gets(nombre);
printf ("\n HORAS TRABAJADAS: ");
scanf ("%f", &hrst);
printf ("\n COSTO POR HORA: ");
scanf ("%f",&chr);
Sueldo=hrst*chr;
printf("\n EL SUELDO DE %s es de: $ %8.2f",nombre,Sueldo);
getch();

}
Programación C y C++ 29

3.- Programa que calcula el precio de venta de un artículo . El precio de venta


se calcula añadiéndole al costo el 120% como utilidad y el 15% de impuesto.

/*Preventa.cpp

# include <stdio.h>
# include <conio.h>

void main ()
{
char descripcion[40];
float CosP, Temp, CVenta;
clrscr();
printf ("\n CALCULA EL PRECIO DE VENTA DE UN ARTICULO.");
printf ("\n\n DESCRIPCION DEL ARTICULO: ");
gets (descripcion);
printf ("\n COSTO DE PRODUCCION: ");
scanf("%f", &CosP);
Temp= (CosP+(CosP*1.2));
CVenta=Temp + Temp*.15;
printf("\n EL COSTO DE VENTA DE %s ES: $ %8.2f", descripcion,
CVenta);
getch();
}

4. – Usando la entrada salida por flujos escribir un programa que lee la


longitud de los lados A y B de un triangulo y calcule la hipotenusa (C),
aplicando el teorema de Pitágoras, C 2 = A2 + B2. o sea:

C2 = a 2 + b 2.

/* Hipotenu.cpp */
# include <iostream.h>
# include <conio.h>
# include <math.h> //Por las funciones matemáticas

void main ()
{
float A, B, C;
clrscr();
cout<<"CALCULO DE LA HIPOTENUSA";
cout<<"\n CATETO A:" ;
cin>>A;
cout<<"\n CATETO B:";
cin>>B;

C= sqrt (pow(A,2)+pow(B,2));

cout<<"\n LA HIPOTENUSA ES: "<<C;


getch();
}
30 Programación C y C++

5.- Programa que lee una temperatura en grados Fahrenheit e imprime su


equivalente en grados Celsius, kelvin, y Rankine. Donde:
Celsius=(Fahrenheit-32)x5/9, Kelvin=Celsius+273, Rankine=Fahrenheit+460.

/*Fahrenhe.cpp */

# include <iostream.h>
# include <conio.h>
void main ()
{
float Fahr, Celsius, Kelvin, Rankine;
clrscr();
cout<<"\n CONVIERTE GRADOS FAHRENHEIT A CELSIUS, KELVIN, Y
RANKINE.";
cout<<"\n GRADOS FAHRENHEIT:";
cin>>Fahr;
Celsius= (Fahr-32)*5/9;
Kelvin=Celsius+273;
Rankine=Fahr+460;
cout<<"\n EQUIVALENCIA A GRADOS ";
cout<<"\n CELSIUS : "<<Celsius;
cout<<"\n KELVIN : "<<Kelvin;
cout<<"\n RANKINE : "<<Rankine;
getch();
}

6.- Utilizando el especificador extern, escriba un programa compuesto por 2


archivos que calcule el costo total de venta de 3 productos.
En el archivo Venta2.cpp deberá leer el precio de los 3 productos
vendidos por un comerciante y calculará el total de ventas que tuvo
En el archivo Venta1.cpp deberá imprimir el resultado.

/*Venta1.cpp /*Venta2.cpp
Proyecto: Venta.prj */ proyecto: Venta.prj */

#include <conio.h> #include <conio.h>


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

float Vta; extern float Vta; //Variable


externa
void func1();
void func1()
void main() {
{ float a,b,c;
clrscr(); cout<<"INTRODUSCA LOS PRECIOS DE
func1(); LOS 3 PRODUCTOS:";
cout<<"\n EL TOTAL DE cout<<"\n\nPRIMER PRODUCTO: ";
VENTAS ES:$ "<<Vta; cin>>a;
getch(); cout<<"\nSEGUNDO PRODUCTO:";
} cin>>b;
cout<<"\nTERCERO PRODUCTO:";
cin>>c;
Vta=a+b+c;
}
Programación C y C++ 31

7.- Utilizando el especificador extern escriba un programa compuesto por 3


archivos que calcule el interés simple y la deuda de un Préstamo a una taza de
interés en N años:
En el archivo Banco2.cpp Leerá la Inversión, La taza de interés y el
número de años, calculando el Interés simple a partir de estos datos
En el archivo Banco3.cpp calculará la deuda acumulada
En el archivo Banco1.cpp mostrará en pantalla tanto el Interés como la
deuda calculados.
/*Banco1.cpp /* Banco2.cpp
proyecto: Banco.prj */ Proyecto: Banco.prj */

# include <conio.h> # include <iostream.h>


# include <iostream.h>
extern float Inversion, Interes;
float Inversion,Interes,Deuda;
void Calcula()
void Calcula(); {
void Calcula2(); float Taza;
int N;
void main() cout<<"\nINVERSION :";
{ cin>>Inversion;
clrscr(); cout<<"\nTAZA DE INTERES : ";
cout<<"CALCULO DE INTERESES\n\n cin>>Taza;
"; cout<<"\nCUANTOS AÑOS: ";
Calcula(); cin>>N;
Calcula2(); Interes=Inversion*Taza*N;
cout<<"\nLOS INTERESES SON : }
"<<Interes;
cout<<"\n\nLA DEUDA SERIA :
"<<Deuda;
getch();
}
// Banco3.cpp
Proyecto: Banco.prj

extern float Inversion,Interes,Deuda;

void Calcula2()
{
Deuda=Interes+Inversion;
}
32 Programación C y C++

EJERCICIOS PROPUESTOS.

1. Elaborar un programa que calcule e imprima el costo de un terreno


cuadrado o rectangular, teniendo como datos la anchura, la longitud en
metros, y el costo del metro cuadrado.

2. Una temperatura en grados centígrados (C) se puede convertir a su


equivalente Fahrenheit (F) con la fórmula:
F = 9/5 C + 32 .
Elaborar un programa que lea una temperatura en grados centígrados
y obtenga e imprima la temperatura Fahrenheit equivalente.

3. Elabore un programa que lea un número de pies y calcule e imprima su


equivalente en yardas, pulgadas, centímetros y metros, de acuerdo con
las siguientes equivalencias:
1 pie = 12 pulgadas, 1 yarda = 3 pies, 1 pulgada =2.54 cms. , 1 metro =
100 cm.
4. Elaborar un programa que lea el artículo y su costo; la utilidad es el
200% y el impuesto es el 15%; calcular e imprimir el artículo, utilidad,
impuesto y precio de venta.

5. Elaborar un programa que lea dos números enteros y positivos e


imprima la media aritmética, es decir, el punto medio entre los dos
números.

6. Elaborar un programa que lea el radio (r) de una esfera, calcule e


imprima el volumen y el área.
Volumen = 4/3 Π r3 Area= 4 Π r2

7. Elaborar un programa que lea una cantidad de horas e imprima su


equivalente en minutos, segundos y días.

8.- Elaborar un programa que lea el valor de w e imprima el valor de z.

Z= 1 / 2Π e –w2/2

9.- Elaborar un programa que lea la cantidad de dólares a comprar y el tipo de


cambio en pesos (costo de un dólar en pesos); calcular e imprimir la cantidad a
pagar en pesos por la cantidad de dólares indicada.

10.- Utilizando el especificador extern, escriba un programa compuesto por 2


archivos que calcule la distancia que recorre la luz en un tiempo dado.
En el archivo Luz2.cpp deberá leer el tiempo en segundos y calculará
la distancia recorrida (Distancia = Velocidad* Tiempo)
En el archivo Luz.cpp deberá imprimir el resultado.
Programación C y C++ 33

Capitulo 2

Sentencias de control de programa


• Sentencias de Selección
• Sentencias de Iteración
• Sentencias de Salto
• Ejercicios

En este capitulo se discute el conjunto de sentencias de control de programa


que el lenguaje C soporta .
34 Programación C y C++

SENTENCIAS DE SELECCIÓN
A las sentencia de selección tambien se les conoce como sentencia
condicional, entre las que se incluyen if y switch

Muchas sentencias de C se basan en una prueba condicional que


determina la acción que se ha de llaver acabo. Una expresión condicional
tiene como resultado un valor cierto o falso. En C cualquier valor distinto de
cero es cierto, incluyendo los números negativos. El 0 es el único valor falso.

• if, if-else

La sintaxis de esta sentencia es:


if (expresion es verdadera)
{
sentencia_1;
sentencias_2;
}
Si al evaluar la expresión, ésta es verdadera se ejecutan las sentencias
del bloque (un bloque comienza con { y termina en }), en caso contrario se
ejecutan las sentencias a continuación del bloque. La cláusula else se puede
añadir a la sentencia if cuando la expresión es falsa y se desean ejecutar
sentencias distintas.
if (expresion es verdadera)
{
sentencia_1;
sentencias_2;
}
else
{
sentencia_11;
sentencias_22;
}
Ejemplo:
/*IF.cpp
Determina si una persona es mayor o menor de edad
usando la Instrucción if*/

#include <conio.h>
#include <iostream.h>

void main()
{int Edad;
clrscr();
cout<<"PROPORCIONE SU EDAD: ";
cin>>Edad;

if (Edad<18)
cout<<"\n Aun es menor de edad";
else
cout<<"\n Es mayor de edad";

getch();
}
Programación C y C++ 35

Si el usuario escribe un número menor que 18 entonces se muestra el


mensaje “Aun es menor de edad” y si escribe un número igual o mayor que 18
entonces el mensaje será “Es mayor de edad”.
Se pueden anidar if evaluándose de arriba hacia abajo tan pronto como
se encuentre una condición cierta, se ejecuta la sentencia asociada con ella y
se pasa por alto el resto de la escala.
Ejemplo:

/*IF2.cp
Encuentra el mayor de 3 números*/

#include <conio.h>
#include <iostream.h>

void main()
{clrscr();
int A=3,B=10,C=15; //Probar con otros valores
int Mayor;

if ((A>B) &&(A>C))
Mayor=A;
else
if ((B>A) && (B>C))
Mayor=B;
else
Mayor=C;

cout<<"El Mayor es: "<<Mayor;


getch();
}

• switch

La sentencia switch permite evaluar una expresión y tomar diversas acciones


en función del resultado de la expresión.
Switch (expresión entera)
{
case constante_1:
sentencia_11;
sentencia_12;
...
break;
case constante_2:
sentencia_21;
sentencia_22;
...
break;
...
default:
sentencia_11;
sentencia_12;
...
}
36 Programación C y C++

La siguientes reglas se aplican en el uso de la sentencia switch

• Expresión entera, puede ser una constante, una variable, una llamada a
función o una expresión. La sentencia switch no funciona con datos de
tipo coma flotante
• El valor después de cada etiqueta case debe ser una constante entera o
carácter, como 3 o ‘b’, o bien una expresión que se evalúe a una
constante como ‘a’ +’32’.
• Necesita utilizar una sentencia break después de cada conjunto de
sentencias ejecutables. La sentencia break hace que la ejecución del
programa se reanude después del final de la sentencia switch actual. Si
no se utiliza la sentencia break, la ejecución del programa se reanudará
en las siguientes etiquetas case.
• El conjunto de sentencias case no necesita ser encerradas entre llaves.
• Si ninguno de la valores de constante_1, constante_2, etc. Coincide con
el valor de expresión_entera, se ejecutarán las sentencias que vienen a
continuación de la sentencia opcional default.

Ejemplo:
/*Switch.cpp
Calcula el área de figuras Geométricas
usando la Instrucción switch*/

#include <conio.h>
#include <iostream.h>

void main()
{char op;
int L,B,H,R;
float Area;

clrscr();
cout<<" CALCULO DE AREAS";
cout<<"\n 1.- RECTANGULO";
cout<<"\n 2.- TRINGULO";
cout<<"\n 3.- CIRCULO";
cout<<"\n Opcion:";
op=getche();

switch (op)
{
case '1': cout<<"\n\n Lado:";
cin>>L;
Area=L*L;
break;

case '2': cout<<"\n\n Base:";


cin>>B;
cout<<"\n Altura:";
cin>>H;
Area=(B*H)/2;
break;
Programación C y C++ 37

case '3': cout<<"\n\n Radio:";


cin>>R;
Area=2*3.1416*R*R;
break;
default:cout<<"\n\n OPCION NO VALIDA";
}
if ((op>='1')&&(op<='3'))
cout<<"\n El Area es: "<<Area;

getch();
}
La salida de una ejecución sería:
CALCULO DE AREAS
1.- RECTANGULO
2.- TRINGULO
3.- CIRCULO
Opcion:2

Base:10

Altura:20

El Area es: 100

Dependiendo de la opción seleccionada por el usuario será el conjunto


de sentencias que se ejecuten, si el usuario selecciona la opción 2 entonces
solicita la Base y la Altura del triangulo almacenando los datos en las
variables B y H respectivamente, calcula el Area con la formula (B*H)/2, en
este punto se interrumpe la secuencia de ejecución de sentencias por la
presencia de la instrucción break, reanudándose este al final del switch donde
se encuentra la instrucción if que evalúa si la opción seleccionada esta en el
intervalo de 1 a 3 para desplegar el contenido del Area calculada. El programa
al no limitar los valores posibles que el usuario puede seleccionar
(Validación), puede introducir cualquier otro valor diferente de 1, 2 ó 3, en
este caso se ejecuta la sentencia hallada después del default y al final del
programa no se imprimiría el Area.

SENTENCIAS DE ITERACIÓN
Entre las sentencias de iteración se incluyen for, while y do-while. Cualquier
sentencia de iteración tiene tres partes importantes que son: Inicialización,
condición e incremento, aunque cada sentencia de iteración debe usarse
preferente según la situación en la mayoría de los casos se puede adaptar
cualquiera de las tres a cualquier situación.

• for

Cuando se desea ejecutar una sentencia simple o compuesta, repetitivamente


un número de veces conocido, la construcción adecuada es la sentencia for.

for(exp_inicialización; condición; expresión_incremento)


38 Programación C y C++

instrucción;

Cuando se encuentra la instrucción for se ejecuta primero la expresión de


inicialización, no volviendo a ejecutarse más. Generalmente esta instrucción
realiza la inicialización de la variable de control de ciclo. Tras esto se prueba
la condición. Siempre que condición se evalúa como verdadero, la instrucción
o instrucciones dentro del ciclo son ejecutados. Después de entrar en el ciclo
y ejecutar todas las instrucciones dentro de éste se ejecuta
expresión_incremento. Sin embargo si condición toma el valor Falso, las
instrucciones dentro del ciclo son ignoradas y la ejecución continúa con la
instrucción al final del ciclo.
Cuando se necesitan ejecutar varias instrucciones dentro del ciclo se
hacen necesarias definir el bloque con las llaves {}
Ejemplo:
/*for.cpp
Efectúa ciclos según Ini, Fin, Inc */

#include <conio.h>
#include <iostream.h>

void main()
{clrscr();

int x, Ini=0, Inc=5, Fin=20; //Probar con otros valores


for (x=Ini;x<Fin;x=x+Inc)
cout<<"\t"<<x;

getch();
}
La salida sería:
0 5 10 15

El programa efectúa un total de 5 ciclos, aunque en este último la condición se


hace falsa por lo que finaliza, transfiriendo el control a la línea de código que
lee una tecla. La secuencia de eventos es el siguiente:
No. Ciclo x condicion Acción
1 0 0<20 Imprime 0
2 5 5<20 Imprime 5
3 10 10<20 Imprime 10
4 15 15<20 Imprime 15
5 20 20<20 Fin ciclo for..
Programación C y C++ 39

for anidados

Un ciclo for puede colocarse dentro de otro, en este caso el ciclo interno se
ejecutará totalmente cada vez que se ejecute el ciclo que lo contiene.
Ejemplo:
/*for2.cpp
Imprime tabla de multiplicación */

#include <conio.h>
#include <iostream.h>

void main()
{clrscr();
int x,y;
int fin1=6, fin2=5; //Probar con otros valores
for (x=1;x<=fin1;x++)
{
cout<<"\n";
for (y=1;y<=fin2;y++)
cout<<x*y<<"\t";
}
getch();
}
La salida sería:
1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20
5 10 15 20 25
6 12 18 24 30

Por cada valor que toma x en el ciclo for externo se efectúa totalmente el ciclo
for interno es decir y inicialmente toma el valor de 1 y se imprime 1*1=1,
luego y=2 y se imprime 2, y así hasta que y=5 imprime 5, en el siguiente
ciclo la condición se hace falsa dándose por terminado el ciclo interno, pero el
ciclo externo incrementa su valor y ejecuta nuevamente el ciclo interno, este
proceso se sigue hasta que la condición del ciclo externo se hace falsa.

• while

La sentencia while es un ciclo de verificación preliminar, esto significa que la


condición es evaluada antes de entrar a ejecutar las instrucciones dentro del
cuerpo del ciclo. Debido a esto se pueden ejecutar de cero a muchas veces.

while (condicion)
{
instrucción_1;
instrucción_2;
}
40 Programación C y C++

La inicialización de un ciclo while por lo regular se realiza antes de ella y el


incremento dentro del bloque.
/*While.cpp
Visualiza números en orden descendente */

#include <conio.h>
#include <iostream.h>

void main()
{int ini=50,fin=0,inc=10;
int x=ini; //Inicialización

while (x>fin)
{
cout<<x<<"\t";
x=x-inc; //Decremento
}
}
La salida sería:
50 40 30 20 10

• do-while

Difiere tanto de for como de while en que es un ciclo de verificación


posterior, es decir al ciclo se entra al menos una vez , y la condición del ciclo
se prueba al cabo de la primera iteración. Como los ciclos do-while se
ejecutan como mínimo una vez, es mejor utilizarlos en aquellas aplicaciones
en las que se quiere entrar al ciclo.
do
{
Instrucción_1;
Instrucción_2;
}
while(condición);
Programación C y C++ 41

Ejemplo:
/*Do-While.cpp
Menu de Opciones */

#include <conio.h>
#include <iostream.h>

void main()
{clrscr();
char op;
do
{ cout<<" MENU";
cout<<"\n 1.-Altas";
cout<<"\n 2.-Bajas";
cout<<"\n 3.-Consultas";
cout<<"\n 4.-Salir";
cout<<"\nOpcion:";
do
{
op=getch();
}
while(op!='1'&&op!='2'&&op!='3'&&op!='4');
//Código adicional
}
while (op!='4');
}
La salida del programa sería:
MENU
1.-Altas
2.-Bajas
3.-Consultas
4.-Salir
Opcion:

En este ejemplo se utilizan dos ciclos do-while anidados. El ciclo exterior


controla la terminación del programa y el ciclo interior valida la opción
seleccionada por el usuario, de tal forma que si el usuario selecciona una
opción distinta a las permitidas (1,2,3,4) se vuelve a leer sin salir del ciclo
interior. Solamente si el usuario selecciona la opción ‘4’ la condición del ciclo
exterior será falsa y finalizará, en caso contrario desplegará nuevamente el
menú de opciones.

SENTENCIAS DE SALTO
C tiene cuatro sentencias que llevan a cabo un salto incondicional: return,
goto, break y continue. De ellas, se puede usar return y goto en cualquier
parte del programa . Las sentencias break y continue se deben usar junto con
una sentencia de ciclo.

• return
La sentencias return se usa para volver de una función. Se trata de una
sentencia de salto porque hace que la ejecución vuelva (salte atrás) al punto en
que se hizo la llamada a la función.
42 Programación C y C++

Return (expresión)

Donde: el valor de expresión se devuelve como valor a la función.


Se puede usar tantas sentencias return como se quiera en una función.
Sin embargo, la función termina tan pronto como encuentra el primer return.
Una función declarada como void no debe contener una sentencia return. La
función exit() definido en el archivo de cabecera stdio.h es parecida a return
aunque este en lugar de afectar a una función afecta a todo el programa, más
adelante en el tema de funciones se podrán apreciar ejemplos.

• goto
La sentencia goto se puede usar para realizar ciclos usando una etiqueta, o
para saltar a otra parte de un programa, actualmente no es recomendable su
uso por que hace ilegible el código.
Etiqueta:
Setencia_1;
Sentencia_2;
...
goto Etiqueta;
Donde: Etiqueta es cualquier etiqueta valida anterior o posterior al goto.
Ejemplo:
/*goto.cp */

#include <conio.h>
#include <iostream.h>

void main()
{clrscr();
int x=0;
Inicio:
x++;
cout<<"\n"<<x;
if (x<10)
goto Inicio;
}

• break

La sentencia break tiene dos usos. Se puede usar para finalizar un case en una
sentencia switch. También se puede usar para forzar la terminación inmediata
de una bucle, saltando la evaluación condicional normal del ciclo.
Cuando se encuentra la sentencia break dentro de un ciclo, el ciclo
finaliza inmediatamente.
Programación C y C++ 43

Ejemplo:
/*break.cpp
Lee caracteres hasta que se pulse la tecla <esc> */

#include <conio.h>
#include <iostream.h>

void main()
{ clrscr();
char car;
cout<<"PULSE <Esc> PARA TERMINAR\n\n";
for(;;) //Ciclo infinito
{
car=getche();
if (car==27)
break;
}
}

• continue

La sentencia continue funciona de una forma algo similar a break. Sin


embargo, en vez de forzar la terminación, continue, forza una nueva iteración
del ciclo y salta cualquier código que exista abajo en el bloque.
Ejemplo:

/*continue.cpp
Imprime los números del 0 al 50 que sean múltiplos de 4 */

#include <conio.h>
#include <iostream.h>

void main()
{ clrscr();
int x;
for (x=0;x<50;x++)
{
if (x % 4) //Cualquier valor diferente de cero es verdadero
continue;
cout<<x<<" ";
}
}
La salida del programa es:
0 4 8 12 16 20 24 28 32 36 40 44 48
44 Programación C y C++

EJERCICIOS RESUELTOS

1.- Programa para calcula la calificación final de un alumno, teniendo como


datos N calificaciones parciales (3). Imprime el nombre, la calificación final y
un comentario de “Aprobado” si obtiene 70 o más y “Reprobado” en caso
contrario.

/*Alumno.cpp */

#include <stdio.h>
#include <conio.h>
#include <iostream.h>
#define N 3

void main()
{ clrscr();
float Prom,SumCal=0,Cal;
int x;
char Nom[40];
cout<<"DETERMINA SI UN ALUMNO APRUEBA O NO";
cout<<"\n\nNOMBRE DEL ALUMNO: ";
gets(Nom);
for (x=1;x<=N;x++)
{
cout<<"\n CALIFICACION No "<<x<<": ";
cin>>Cal;
SumCal=SumCal+Cal;
}

Prom= SumCal/N;
cout<<"\nEL ALUMNO "<<Nom<<" TIENE EL PROMEDIO DE: "
<<Prom<<" Y ESTA:";
if(Prom>=70)
cout<<"\n\nAPROBADO";
else
cout<<"\n\nREPROBADO";

getch();
}
Programación C y C++ 45

2.- En un hotel se hace un descuento del 10% si el cliente se hospeda más de


5 días, el 15% si se hospeda más de 10 días y del 20% si se hospeda más de 15
días. Programa que lee el número de días y el precio diario de la habitación e
imprima el subtotal a pagar, el descuento y el total a pagar.

/*Hotel.cpp */

#include <conio.h>
#include <iostream.h>

void main()
{ clrscr();
float CosHabi,Dias,Desc,SubTotal,Total;
cout<<"CALCULA EL COSTO DE UNA HABITACION DE UN HOTEL";
cout<<"\n\nCOSTO DE LA HABITACION: ";
cin>>CosHabi;
cout<<"No. DE DIAS: ";
cin>>Dias;
SubTotal=CosHabi*Dias;
if (Dias<=5)
Desc=0;
else
if (Dias>5 && Dias<=10)
Desc=SubTotal*.10;
else
if (Dias>10 && Dias<=15)
Desc=SubTotal*.15;
else
Desc=SubTotal*.20;
Total=SubTotal-Desc;
cout<<"\nSUB-TOTAL: "<<SubTotal;
cout<<"\nDESCUENTO: "<<Desc;
cout<<"\n _______ ";
cout<<"\nTOTAL: "<<Total;
}
46 Programación C y C++

3.- Programa que calcula el salario de un empleado, Según el número de horas


trabajadas por semana y el salario por hora. Si el número de horas trabajadas
es menor o igual a 40 se paga la cuota normal por hora. Si el número de horas
rebasa las 40 horas reglamentarias, el excedente se paga como tiempo extra:
1 a 10 hrs. Extras se paga al doble de la cuota por hora
11 a 20 hrs. Extras se paga al triple de la cuota por hora
21 ó más hrs. Extras se paga al cuádruple de la cuota por hora
En ningún caso el número de horas trabajadas por semana podrá ser superior a
80.
/*Salario.cpp */
#include <stdio.h>
#include <conio.h>
#include <iostream.h>

void main()
{ clrscr();
float SalSem, SalHra, SalTE=0,SalBase;
int Hrs;
char Nom[40];
cout<<"CALCULA EL SALARIO SEMANAL DE UN TRABAJADOR";
cout<<"\n\nNOMBRE DEL TRABAJADOR: ";
gets(Nom);
cout<<"\nNo. DE HORAS TRABAJADAS POR SEMANA: ";
do{ //Valida el intervalo de Hrs permitidas
cin>>Hrs;
}
while(Hrs<0 || Hrs>80);
cout<<"\nSALARIO POR HORA:";
cin>>SalHra;
if (Hrs<=40)
{
SalBase=Hrs*SalHra;
SalSem=SalBase;
}
else
{
SalBase=40*SalHra;
if (Hrs>40 && Hrs <=50)
SalTE=(Hrs-40)*SalHra*2;
else
if (Hrs>50 && Hrs <=60)
SalTE=(Hrs-40)*SalHra*3;
else
SalTE=(Hrs-40)*SalHra*4;
SalSem=SalBase+SalTE;
}
cout<<"\nTRABAJADOR: "<<Nom;
cout<<"\nSALARIO BASE: "<<SalBase;
cout<<"\nSALARIO POR TIEMPO EXTRA: "<<SalTE;
cout<<"\n _______ ";
cout<<"\nSALARIO TOTAL SEMANAL: "<<SalSem;
}
Programación C y C++ 47

4.- Escribir un programa para dibujar un rectángulo en modo texto en la


pantalla, solicitar las coordenadas x1, y1, x2 y y2, según la siguiente figura:
x1,y1

x2,y2
/*Rectang.cpp */

#include <conio.h>
#include <iostream.h>

void main()
{ clrscr();
int x,y,x1,y1,x2,y2;
cout<<"DIBUJA UN RECTANGULO EN PANTALLA SEGUN COORDENADAS";
cout<<"\nESCRIBA LAS COORDENADAS X1,Y1,X2,Y2 MODO TEXTO ";
cout<<"SEPARADOS POR UN ESPACIO: \n";
cin>>x1>>y1>>x2>>y2;

for (x=x1;x<x2;x++) //Dibuja Línea horizontal


{
gotoxy(x,y1);cout<<"−";
gotoxy(x,y2);cout<<"−";
}
for (y=y1+1;y<y2;y++) //Dibuja línea Vertical
{
gotoxy(x1,y);cout<<"⏐";
gotoxy(x2,y);cout<<"⏐";
}
gotoxy(x1,y1);cout<<"⌐"; //Para las Esquinas
gotoxy(x2,y1);cout<<"¬";
gotoxy(x1,y2);cout<<"└";
gotoxy(x2,y2);cout<<"┘";
}
48 Programación C y C++

EJERCICIOS PROPUESTOS.

1.- Elabore un programa para calcular e imprimir el precio de un terreno del


cual se tienen los siguientes datos: largo, ancho y precio por metro cuadrado.
Si el terreno tienen más de 400 metros cuadrados se hace un descuento de
10%, si el terreno tiene más de 500 metros cuadrados el descuento es de 17%
y si tiene más de 1000 el descuento es de 25%.

2.- Una librería vende libros con las condiciones siguientes: Si el cliente es
tipo 1 se le descuenta el 30%, si el cliente es tipo 2 se le descuenta el 20%, si
el cliente es tipo 3 se le descuenta 10%. Cuando el cliente realiza una compra
se generan los datos siguientes: Nombre del cliente, Tipo de cliente (1,2,3),
cantidad de libros, costo por libro. Elabore un programa que lea estos datos e
imprima: Nombre del cliente, Total a pagar, Descuento y el Neto a pagar.

3.- Igual que el ejercicio anterior, pero además:


Si la cantidad de libros solicitada es mayor que 50, se hace un descuento
adicional de 5%; en caso de ser mayor que 100 el descuento adicional es de
10%.

4.- Elaborar un programa que lea los datos de un estudiante: nombre y tres
calificaciones parciales e imprimir el nombre y la calificación final de
acuerdo a lo siguiente: Para aprobar el curso, debe tener 70 o más en cada
una de las tres calificaciones, la calificación final será el promedio. En caso
de haber reprobado uno o más exámenes ordinarios, la calificación final será
NA (NO ACREDITADO).

5.- En un almacén de ventas de trajes si se compra uno se hace el 50% de


descuento, si compra 2 el 55%, si compra 3 el 60% y si compra más de 3 el
65%. Elaborar un programa que lea la cantidad de trajes y el precio unitario
(todos tienen el mismo precio) e imprima el subtotal a pagar, el descuento y
el total a pagar.

6.- Reescriba el programa del ejercicio resulto 1 pero sin usar ciclos

7.- Programa que dibuje una secuencia de N rectangulos (5) a partir de un


rectangulo cuyas coordenadas sean proporcionados por el usuario, los
rectangulos siguientes se irán haciendo más pequeños en forma proporcional
cada vez.
Programación C y C++ 49

Capitulo 3

Arreglos y Apuntadores
• Que son los arreglos
• Arreglos unidimensionales
• Cadenas
• Arreglos bidimensionales
• Apuntadores
• Punteros y arreglos
• Inicialización de apuntadores
• Funciones de asignación dinámica
• Ejercicios
Los arreglos, punteros y cadenas de caracteres son conceptos relacionados en
el lenguaje C, por esta razón se integran en este capitulo.
50 Programación C y C++

QUE SON LOS ARREGLOS

Un arreglo es una colección de variables del mismo tipo que se referencia por
un nombre común. A un elemento específico de un arreglo se accede
mediante un índice. En C todos los arreglos constan de posiciones de
memoria contiguas. La dirección más baja corresponde al primer elemento y
la dirección más alta al último elemento. Los arreglos pueden tener una o más
dimensiones.

ARREGLOS UNIDIMENSIONALES

A los arreglos unidimensionales también se les conoce como listas.

Tipo nombre[Tamaño];
Tipo nombre[];
Donde: tipo Indica el tipo de datos de los elementos del arreglo.
nombre Es el identificador del arreglo
tamaño Especifica el número de elementos del arreglo. El tamaño
puede omitirse cuando se inicializa el arreglo, cuando se declara como un
parámetro en una función o cuando se hace referencia a un arreglo declarado
en otra parte del programa, es recomendable que el tamaño sea definido como
una constante para garantizar no rebasar el límite del arreglo.
Ejemplo:
#define N 30
int Calif[N];
La declaración de la variable anterior hace que el compilador reserve
espacio de memoria para almacenar 30 datos de tipo entero. En C todos los
arreglos tienen el 0 como índice de su primer elemento, por tanto el primer
elemento de la variable anterior sería Calif[0] y el último elemento Calif[29].
El lenguaje C no checa los límites de un arreglo. Es responsabilidad
del programador realizar este tipo de operaciones para no escribir o modificar
porciones de memoria que no le pertenecen al arreglo.
La cantidad de memoria requerida para guardar un arreglo esta
directamente relacionada con su tipo y su tamaño. Para un arreglo
unidimensional, el tamaño total en bytes se calcula:
Tatal en bytes =sizeof(tipo)*tamaño = sizeof(nombre_ arreglo).
Ejemplo:
float Salario[10];
int Tam;
Tam=sizeof (Salario); //Tam=40

• Inicialización de un arreglo

Cuando se crea un arreglo sólo se puede utilizar constantes para inicializarlo


e aquí algunos ejemplos:
int Calif[4]={80,90,75,100};
float real[5]={23.45,90.34,70.1,75,10};
Programación C y C++ 51

char Caracter[]={‘a’,’b’,’c’,’d’,’e’,’f’};
char Cadena[11]=”HOLA AMIGOS”;
char Cadena2[]=”PROGRAMACIÓN EN LENGUAJE C”;

Para comprender mejor el manejo de los arreglos se presentan a continuación


algunos ejemplos:

/*Arreglo2.cpp.cpp
Visualiza el contenido de un arreglo */

#include <conio.h>
#include <iostream.h>
#define N 4

void main()
{ clrscr();
int Cal[N],x;
Cal[0]=90; Cal[1]=80;Cal[2]=100; Cal[3]=70;
for (x=0;x<N;x++)
cout<<"\t"<<Cal[x];
getch();
}
La salida es
90 80 100 70

La asignación anterior contempla los cuatro elementos del arreglo desde el


índice 0 hasta el índice 3, durante la impresión se utiliza la constante N para
limitar el número de iteraciones que efectúa el ciclo for

/*Arreglo3.cpp.cpp
Determina el número mayor que existe en un arreglo
unidimensional generados aleatoriamente.*/

#include <conio.h>
#include <iostream.h>
#include <stdlib.h> //por random() y randomize()
#define N 10

void main()
{ clrscr();
int Aleat[N],x;
int Mayor=0;
randomize(); //inicializa la semilla
for (x=0;x<N;x++)
{
Aleat[x]=random(100); //genera números aleat. entre 0 y 100
cout<<" "<<Aleat[x];
}
//Encuentra el número mayor en el arreglo
for (x=0;x<N;x++)
if (Aleat[x]>Mayor)
Mayor=Aleat[x];
cout<<"\n\nEl mayor es: "<<Mayor;
}
52 Programación C y C++

En cada ejecución del programa la secuencia de números aleatorios cambia, por lo


consiguiente también la salida, aquí se presenta una de ellas:
79 24 59 18 41 37 64 3 23 29

El mayor es: 79

/*Arreglo4.cpp
Ordena un arreglo unidimensional en forma ascendente
aplicando el método de selección */

#include <conio.h>
#include <iostream.h>
#define N 10
void main()
{ clrscr();
int A[N]={79,24,59,18,41,37,64,3,23,29}; //Arr. inicializado
int menor,temp,ind,x,y;
for (x=0;x<N-1;x++)
{
menor=A[x];
ind=x;
for (y=x+1;y<N;y++) //Recorre la lista y determina
if (A[y]<menor) //el menor
{
menor=A[y];
ind=y;
}
if (ind!=x) //Intercambia el elemento menor a la posición x
{
temp=A[x];
A[x]=A[ind];
A[ind]=temp;
}
}
for (x=0;x<N;x++)
cout<<" "<<A[x];
getch();
}
La salida del programa es:
3 18 23 24 29 37 41 59 64 79

El método de ordenación por selección separa el elemento con menor


valor y lo intercambia con el primer elemento. Después, de los N-1 elementos
se busca el siguiente elemento con menor valor y se intercambia con el
segundo elemento y así sucesivamente. El intercambio continúa hasta llegar a
los dos últimos elementos.
Programación C y C++ 53

CADENAS
C no tiene un tipo de dato para cadenas de caracteres. Una cadena de
caracteres es un arreglo unidimensional, en el cual todos sus elementos son de
tipo char, al final del arreglo contiene el carácter nulo ‘/0’. C soporta una gran
variedad de funciones para el manejo de cadenas definidos en el archivo de
cabecera string.h e aquí algunos:

Nombre Función
strcpy(c1,c2) Copia c2 en c1
strcat(c1,2) Concatena c2 al final de c1
strlen(c1,c2) Devuelve la longitud de la cadena
strcmp(c1,c2) Devuelve 0 si c1 y c2 son iguales; menor que 0 si c1<c2;
mayor que 0 si c1>c2
strchr(c1,car) Devuelve un puntero a la primera ocurrencia de car en c1
strstr(c1,c2) Devuelve un puntero a la primera ocurrencia de c2 en c1
Tabla 3. 1.- Funciones para el manejo de cadena
Ejemplos:

/*Cadena.cpp */

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

void main()
{ clrscr();
int L,x;
char Cad[]="HOLA AMIGO"; //Esta es la cadena
L=strlen(Cad); //Longitud de la cadena
for (x=L-1;x>=0;x--)
cout<<Cad[x];

getch();
}
La salida del programa es:
OGIMA ALOH

Con la función strlen() se calcula la longitud de la cadena, para que en el ciclo


for se inicie a partir del último índice del arreglo de caracteres,
decrementandolo de uno en uno hasta llegar a cero, en cada ciclo se imprime
un carácter a la vez.
54 Programación C y C++

/*Cadena2.cpp
Operaciones con cadena */

#include <string.h>
#include <stdio.h>
#include <conio.h>
#include <iostream.h>
#define N 40

void main()
{ clrscr();
int L,x;
char C1[]="INSTITUTO ";
char C2[]="TECNOLOGICO DEL ISTMO";
char C[N];
char Est[N];

strcpy(C,C1); //Copia C1 en C
strcat(C,C2); //Concatena C2 en C
cout<<"\nDonde Estudio el autor de este libro: ";
gets(Est); //En lugar de cin.getline();

if (strcmp(C,Est)==0)
cout<<"\nAcertaste";
else
cout<<"\nNo Acertaste, él estudio en el: "<<C;
getch();
}
La salida es:

Donde Estudio el autor de este libro: INSTITUTO POLITECNICO NACIONAL

No Acertaste, él estudio en el: INSTITUTO TECNOLOGICO DEL ISTMO

La lectura de una cadena con la función cin.getline() agrega el carácter


cambio de línea (‘\n’) al final de la cadena por lo que jamás abría una
coincidencia con la cadena Concatenada C, en su lugar se usa mejor la función
gets() de stdio.h

ARREGLOS BIDIMENSIONALES
El termino dimensión representa la cantidad de índices utilizados para
referenciar un elemento particular en un arreglo. Los arreglos de más de una
dimensión se conocen como arreglos multidimensionales. La forma más
simple de un arreglo multidimensional es el arreglo bidimensional. Para
definir un arreglo Tabla de enteros bidimensional de tamaño 5,4 se escribiría:
Int Tabla[5][4];

Un arreglo bidimensional puede verse como una tabla, donde el primer


índice denota el número de filas y el segundo el número de columnas, así la
declaración anterior denotaría una tabla de 5 filas por 4 columnas.
Programación C y C++ 55

0 1 2 3
0 0 5 10 15
1 20 25 30 35
2 40 45 50 55
3 60 65 70 75
4 80 85 90 95

Tabla[1][2]
Un programa que asigne valores a los elementos del arreglo bidimensional
como en la tabla anterior sería:

/*A_Bidim.cpp
Asigna valores múltiplos de 5 a un arreglo bidimensional */

#define N 5
#define K 4

void main()
{ int Tabla[N][K];
int x,y,cont=0;
for (x=0;x<N;x++)
for(y=0;y<K;y++)
{
Tabla[x][y]=5*cont;
cont++;
}
}
El programa utiliza dos ciclos for para asignar valores a cada uno de
los elementos del arreglo, el for interno controla el 2º. índice (columnas) y el
for externo el primer índice (filas).

La cantidad de memoria en bytes para almacenar un arreglo


bidimensional, se puede calcular aplicando la siguiente formula.

Tamaño en bytes=sizeof(tipo)*tamaño del 1er. índice * tamaño del 2º. índice

Así la definición int Tabla[5][4]; requeriría: 2*5*4 = 40 Bytes de


memoria.

Ejemplo:

/*A_Bidim2.cpp
Lee las K calificaciones de N alumnos, calcula sus promedios,
el aprovechamiento del grupo y visualiza los resultados */

#include <conio.h>
#include <iostream.h>
#define N 5
#define K 4

void main()
56 Programación C y C++

{ clrscr();
int Calif[N][K];
float Prom[N], Aprov;
int x,y, Suma1,Suma2=0;

cout<<"ALUMNO I II III IV\n";


for (y=0;y<N;y++)
{
cout<<y+1;
Suma1=0;
for(x=0;x<K;x++)
{
gotoxy(x*5+10,y+2);
cin>>Calif[y][x];
Suma1=Suma1+Calif[y][x];
}
Prom[y]=Suma1/K; //Promedio por alumno
Suma2=Suma2+Prom[y];
}
Aprov=Suma2/N; //Aprovechamiento de grupo

//Visualización
cout<<"\nALUMNO I II III IV Prom";
for (y=0;y<N;y++)
{
cout<<"\n"<<y+1;
for(x=0;x<K;x++)
{
gotoxy(x*5+10,y+N+4);
cout<<Calif[y][x];
}
gotoxy(x*5+10,y+N+4);
cout<<Prom[y];
}
cout<<"\nAprovechamiento : "<<Aprov;
getch();
}
La salida del programa es:
ALUMNO I II III IV
1 70 85 90 80
2 100 90 70 95
3 75 80 85 90
4 70 85 75 100
5 100 100 95 95

ALUMNO I II III IV Prom


1 70 85 90 80 81
2 100 90 70 95 88
3 75 80 85 90 82
4 70 85 75 100 82
5 100 100 95 95 97
Aprovechamiento : 86

El programa esta diseñado para adaptarse ante el cambio de los valores


de las constantes K y N, si en lugar de 4 unidades se requiere de 5 basta con
modificar este valor y todo funciona a la perfección, de igual forma se puede
Programación C y C++ 57

modificar el valor de N, en lugar de 5 alumnos puede modificarse a 10, en el


programa se incluye la instrucción gotoxy() hallado en el archivo de cabecera
conio.h para el control del cursor en la pantalla.

Un programa bien escrito debe reunir la característica anterior para adaptarse ante un
cambio sin ninguna o casi ninguna modificación en el código.

El lenguaje C permite arreglos de más de dos dimensiones . El límite, si lo hay


depende del compilador. La forma general de declaración de un arreglo
multidimensional es:

Tipo nombre[T1][T2][T3]...[Tn]

Es raro que en algún programa se utilicen los arreglos de tres o más


dimensiones por la cantidad de memoria que se requiere para almacenarlos.
Ejemplo:
Int Tri[20][10][5];
Requeriría de 2*20*10*5 = 2,000 bytes de memoria para almacenarla.

APUNTADORES

Un puntero es una variable que contiene una dirección de memoria.


Normalmente, esa dirección es la posición de otra variable de memoria. Si una
variable contiene la dirección de otra variable, entonces se dice que la primera
variable apunta a la segunda.

Dirección de Variable en
memoria memoria
*P 2C00 2C03
2C01
2C02
Car 2C03 ‘A’
Figura 3. 1.- Una variable apuntando a otra

P es una variable de tipo puntero, que apunta a la dirección de memoria


2C03H, es decir apunta a la variable Car, este último es una variable estática
que contiene el carácter ‘A’.

• Declaración de un Apuntador

Una declaración de un puntero consiste en un tipo base, un * y el nombre de la


variable. La forma general para declarar una variable puntero es:

Tipo * nombre;
Ejemplo:
int *P;
58 Programación C y C++

Existen dos operadores especiales de punteros: & y *. El & es un operador


monario (sólo necesita un operando) que devuelve la dirección de memoria de
su operando; El *, es el complemento de &, devuelve el valor de la variable
localizada en la dirección que sigue.

Ejemplo:
int x=10,y;
int *P;
P=&x; //Asigna la dirección de x a P
y=*P; //Asigna el cont. de lo apuntado por P a y, o sea 10

Las variables puntero deben apuntar siempre a otra variable de su


mismo tipo, aunque C permite apuntar a una variable de un tipo distinto sin
mensajes de error durante la compilación en algunos casos, hacerlo sería
incongruente. Ejemplo:

float x=10;
int *P;
P=&x; //Las variables no son del mismo tipo

• Aritmética de punteros

Existen sólo dos operaciones aritméticas que se pueden realizar con los
punteros: la suma(++) y la resta(--). Cada vez que se incrementa un puntero,
apunta a la posición de memoria del siguiente elemento de su tipo base. Cada
vez que se decrementa, apunta a la posición del elemento anterior. Con
punteros a caracteres , parece una aritmética normal. Sin embargo, el resto de
los punteros aumentan o decrementan en la longitud del tipo de datos a los que
apuntan. Suponiendo los enteros de 2 bytes de longitud, cuando se incrementa
un puntero a entero, su valor aumenta en 2. El siguiente programa ilustra este
ejemplo:

/*Apunta2.cpp
Aritmética de punteros/

#include <conio.h>
#include <iostream.h>

void main()
{ clrscr();
int x;
int *P;
for (x=0;x<5;x++)
{
cout<<P<<" ";
P++; //Incremento
}
}
La salida sería:
0x3dc10000 0x3dc10002 0x3dc10004 0x3dc10006 0x3dc10008
Programación C y C++ 59

Al principio el apuntador P apunta a la dirección 0x3dc10000, en el


primer ciclo se incrementa un elemento de tipo entero es decir un incremento
de 2 por lo que ahora apunta a la dirección 0x3dc10002, y así sucesivamente
por cada ciclo.
La dirección inicial de una apuntador no siempre va a ser el mismo en
cada ejecución del programa, la dirección inicial dependerá del estado de la
memoria en el momento de la ejecución, es decir dependerá del sistema
operativo y los programas de aplicación cargados. Si al probar el programa
anterior las direcciones no coinciden con los aquí mostrados se debe
precisamente a esta situación.
El siguiente ejemplo muestra un sencillo programa para manejar una
pila de enteros.

/*Apunta3.cpp
Pila de Enteros */

#include <conio.h>
#include <iostream.h>
#define N 5

void main()
{ clrscr();
int Pila[N],Val;
int *P,*Tope;
P=Pila; //Apunta al inicio de la pila
Tope=Pila+N; //Apunta al final de la pila
cout<<"Agregue elementos en la pila, 0 para terminar:\n";
do
{
cin>>Val;
if (Val!=0) //Agrega elementos a la pila
if(P<Tope)
{
*P=Val; //Agrega el elemento
P++; //Incrementa el apuntador
}
else
cout<<"Pila Llena\n";
else //Visualiza el contenido de la pila
{
cout<<"Contenido de la pila\n";
do{
P--;
cout<<*P<<"\t";
}
while (P>Tope-N);
}
}
while (Val!=0);
getch();
}
La salida Sería:
Agregue elementos en la pila, 0 para terminar:
80
60 Programación C y C++

30
95
50
10
40
Pila Llena
0
Contenido de la pila
10 50 95 30 80

PUNTEROS Y ARREGLOS
Existe una estrecha relación entre los punteros y los arreglos, considérese el
siguiente fragmento:
int Arr[10], *Ap;
Ap=Arr;
Aquí Ap ha sido asignado a la dirección del primer elemento del arreglo Arr,
por que el nombre de un arreglo sin índice devuelve la dirección de inicio del
arreglo. Para acceder al tercer elemento del arreglo Arr, se escribe:
Arr[2] ó *(Ap+2);
Ambas sentencias devuelven el tercer valor. Para acceder a los elementos de
un arreglo se puede efectuar por cualquiera de los dos métodos: La indexación
del arreglo ó la aritmética de punteros, la ventaja del segundo método es que
mejora la velocidad. Ejemplo:

/*Apunta4.cpp */
#include <conio.h>
#include <iostream.h>

void main()
{ clrscr();
int Arr[10]={10,20,30,40,50,60,70,80,90,100};
int *Ap,x;
Ap=Arr; //Apunta al inicio del arreglo
*(Arr+2)=999; //Cambia el valor del tercer elemento
for (x=0;x<10;x++)
cout<<" "<<*(Ap+x);
}
La Salida sería:
10 20 999 40 50 60 70 80 90 100

INICIALIZACIÓN DE APUNTADORES

Después de declarar un puntero y antes de asignarle un valor, contiene un


valor desconocido. Si se intenta utilizar el puntero antes de darle el valor ,
probablemente se estrellara no sólo el programa sino también el sistema
operativo de la computadora.
Un puntero debe ser correctamente inicializado para evitar cualquier
problema, se pueden usar dos métodos:
• Que apunte a una variable estática
• Asignarle memoria dinámica.
Programación C y C++ 61

El primer método consiste en asignarle la dirección de otra variable


previamente definida, ya sea una variable local o global, simple ó a un arreglo
como en el programa anterior. El segundo método es el que se aborda a
continuación.

FUNCIONES DE ASIGNACIÓN DINÁMICA


La asignación dinámica es la forma en que un programa puede obtener
memoria mientras se esta ejecutando. A las variables globales se le asigna
memoria en tiempo de compilación. Las variables locales usan la pila. Sin
embargo, durante la ejecución de un programa no se pueden añadir variables
globales o locales. Pero hay ocasiones en que un programa necesita usar
cantidades de memorias variables. Por ejemplo: un procesador de textos, una
hoja de cálculos, etc.
La memoria dispuesta mediante las funciones de asignación dinámica
de C se obtienen del montón (La región de memoria libre que queda entre el
programa y la pila), generalmente contiene una gran cantidad de memoria
libre.
En los programas tradicionales de C, toda asignación dinámica de
memoria se manipula mediante funciones tales como malloc() y free()
hallados en el archivo de cabecera stdlib.h. La función malloc() estable
bloques de memoria, mientras que free() se utiliza para liberar estos bloques
asignados. En C++ se define un método para signar memoria dinámica
utilizando los operadores new y delete.

En el siguiente ejemplo se ilustra un programa para la asignación dinámica de


memoria en C.
/*As_Dinam.cpp*/

#include <conio.h>
#include <iostream.h>
#include <stdlib.h> //Por las funciones de asignación dinámica

void main()
{ clrscr();
int *P;
P=(int *) malloc(2*sizeof(int)); //Asigna espacio para 2 Ent.
*P=1;
P++;
*P=2;
cout<<*P<<" "<<*(P-1); //Visualiza: 2 1
free(P); //Libera la memoria
}

El mismo ejemplo pero ahora con new y delete


/*As_Dina2.cpp */

#include <conio.h>
#include <iostream.h>

void main()
62 Programación C y C++

{ int *P;
P=new int[2]; //Asigna espacio para 2 Enteros (4 bytes)
*P=1;
P++;
*P=2;
cout<<*P<<" "<<*(P-1); //Visualiza: 2 1
delete(P); //libera la memoria
getch();
}

• new

El operador new está disponible inmediatamente en C++, de modo que no se


necesita utilizar ningún archivo de cabecera; new se puede utilizar con dos
formatos:
new tipo Asigna un único elemento
new tipo[Número_elementos] Asigna un Arreglo.

Siempre que se asigne memoria, es necesario comprobar antes de usar


el puntero, el valor devuelto por new, si no existe memoria disponible, el
operador new proporciona el valor 0 ó NULL. Ejemplo:

/*As_Dina3.cpp
Verifica la cantidad de memoria dinámica libre */

#include <conio.h>
#include <iostream.h>

void main()
{ clrscr();
char *Cad;
int x;
for (x=1;;x++) //Ciclo infinito
{
Cad=new char[1024]; //Asigna 1 Kbyte de memoria
if (Cad==0)
break;
}
cout<<"Se detecto: "<<x<<" Kbytes de memoria libre";
delete(Cad);
}
La salida es:
Se detecto: 60 Kbytes de memoria libre

El programa realiza un ciclo for infinito, en cada ciclo asigna 1 Kbyte de


memoria dinámica a la variable Cad , cuando la memoria se agota el operador
new retorna un 0 asignándosele a Cad, y es cuando el ciclo deja de ejecutarse
por la presencia de la sentencia break. El valor de x se muestra en pantalla,
indicando la cantidad de memoria asignada.
Programación C y C++ 63

• delete

El operador delete libera la memoria asignada con new.


delete dirección

El operador delete toma como argumento un puntero. El bloque de memoria


apartado por este puntero se libera, es decir, se devuelve al sistema operativo
para que pueda ser reutilizada.

EJERCICIOS RESUELTOS

1.- Un vendedor de equipo de computo desea saber la ganancia total por la


venta de N partes de computadoras. Escribir un programa que lea el costo de
compra de cada uno de los N artículos, calcule el costo de venta agregándole
el 35% de ganancia más el 15% de IVA sobre el precio de compra y visualice
los resultados en forma de tabla.

/*Ganacia.cpp */

#include <conio.h>
#include <iostream.h>
#define N 5

void main()
{ clrscr();
int x,GanTotal=0;
float CosArt[N], Gan[N],Iva[N], Venta[N];
cout<<"CALCULA LA GANANCIA EN LA VENTA DE "<<N<<" ARTICULOS";
cout<<"\nPROPORCIONE EL COSTO DE LOS ARTICULOS\n";
for (x=0;x<N;x++)
{
cout<<"\n"<<x+1<<" : ";
cin>>CosArt[x];
Gan[x]=CosArt[x]*.30; //Ganancia por artículo
Iva[x]=CosArt[x]*.15; //IVA por artículo
Venta[x]=CosArt[x]+Gan[x]+Iva[x]; //Prec.de Venta por art.
GanTotal=GanTotal+Gan[x]; //Ganancia total
}
clrscr();
cout<<"ARTIC. COSTO IVA GANACIA VENTA";
for (x=0;x<N;x++)
cout<<"\n"<<x+1<<"\t"<<CosArt[x]<<"\t"<<Iva[x]<<"\t"
<<Gan[x] <<"\t"<<Venta[x];
cout<<"\n\t\t\t_____\n\tGanancia Total= "<<GanTotal;
getch();
}
64 Programación C y C++

2.- Programa que lea los nombres de N alumnos y los ordene en forma
ascendente aplicando el método de burbuja.

/*Burbuja.cpp */

#include <string.h>
#include <stdio.h>
#include <conio.h>
#include <iostream.h>
#define N 5

void main()
{ clrscr();
char Nom[N][40],Temp[40];
int x,y;
cout<<"ORDENA EN FORMA ASCENDENTE "<<N<<" NOMBRES ";
cout<<"\nPROPORCIONE LOS NOMBRES\n";
for (x=0;x<N;x++)
{
cout<<x+1<<" : ";
gets(Nom[x]);
}
//Método de burbuja
for (x=1;x<N;x++)
for(y=N-1;y>=x;y--)
if(strcmp(Nom[y-1],Nom[y])>0)
{
strcpy(Temp,Nom[y-1]); //Intercambio de elementos
strcpy(Nom[y-1],Nom[y]);
strcpy(Nom[y],Temp);
}

cout<<"\nLOS NOMBRES ORDENADOS SON: ";


for (x=0;x<N;x++)
cout<<"\n"<<x+1<<" : "<<Nom[x];

getch();
}

3.- Un grupo de 5 asesores desean conocer los promedios finales obtenidos


por N alumnos de cierta especialidad que desean titularse por la opción VI,
este consiste en un examen escrito y un examen oral. En el examen escrito
cada uno de los asesores registra la calificación obtenido por el alumno
durante la asesoría de su materia, en el examen oral solamente intervienen 3
asesores que dictaminan mediante un conjunto de criterios previamente
establecido la calificación del alumno. Escribir un programa que muestre en
forma de tabla, cada uno de los alumnos, las calificaciones en la fase escrita,
el promedio en la fase escrita, las calificaciones en la fase oral, el promedio en
la fase oral y el promedio general, e indicar con “SI” a los alumnos que
aprueben y “NO” a los alumnos que reprueben. Para poder aprobar el examen
se debe tener un promedio general superior o igual a 80%.
Programación C y C++ 65

/*OpcionVI.cpp */

#include <conio.h>
#include <iostream.h>
#define N 2 //No. de Alumnos
#define M 5 //No. de Materias
#define S 3 //No. de Sinodales

void main()
{ clrscr();
int CalEs[N][M];
int CalOr[N][S];
float PrEs[N], PrOr[N],PrGrl[N];
int x,y, Sum;
cout<<"PROMEDIO GENERAL DE LOS ALUMNOS PARA LA TITULACION";
for (x=0;x<N;x++)
{ Sum=0;
cout<<"\nCAL. DE LA FASE ESCRITA, ALUMNO No. "<<x+1<<"\n";
for(y=0;y<M;y++)
{
cout<<"MATERIA "<<y+1<<" : ";
cin>>CalEs[x][y]; //Lee Calificaciones fase Escrita
Sum=Sum+CalEs[x][y];
}
PrEs[x]=Sum/M; //Promedio por alumno en la fase Escrita
clrscr();
}
for (x=0;x<N;x++)
{ Sum=0;
cout<<"CALIFICACIONES DE LA FASE ORAL, ALUMNO "<<x+1<<"\n";
for(y=0;y<S;y++)
{
cout<<"SINODAL "<<y+1<<" : ";
cin>>CalOr[x][y]; //Lee Calificaciones fase Oral
Sum=Sum+CalOr[x][y];
}
PrOr[x]=Sum/S; //Promedio por alumno en la fase Oral
PrGrl[x]=(PrEs[x]+PrOr[x])/2;
clrscr();
}
cout<<"\n RESULTADOS";
cout<<"\n MATERIAS SINODALES";
cout<<"\nNo. 1 2 3 4 5 PrEsc. 1 2 3 PrOr PrGrl.";
for (x=0;x<N;x++)
{ cout<<"\n"<<x<<" "<<CalEs[x][0]<<" "<<CalEs[x][1]<<" "<<CalEs[x][2];
cout<<" "<<CalEs[x][3]<<" "<<CalEs[x][4]<<" "<<PrEs[x];
cout<<"\t"<<CalOr[x][0]<<" "<<CalOr[x][1]<<" "<<CalOr[x][2];
cout<<" "<<PrOr[x]<<"\t\t"<<PrGrl[x];
if (PrGrl[x]>=80)
cout<<"\tSI";
else
cout<<"\tNO";
}
}
66 Programación C y C++

4.- Utilizando apuntadores y asignación dinámica de memoria construya un


programa para dar de alta una serie de N números, comprobar la existencia de
memoria al hacer la asignación dinámica, al finalizar el programa mostrar los
números ordenados en forma ascendente utilizando el método de burbuja.

/*Ordenar.cpp */

#include <stdlib.h>
#include <conio.h>
#include <iostream.h>
#define N 5 //Numero de elementos

void main()
{ clrscr();
int *Num,x,y,Temp;
Num=new int [N];
if (Num==0) //Verifica si no hay memoria dinámica
{
cout<<"\nNo existe memoria disponible";
exit(0); //Finaliza el programa
}
cout<<"DA DE ALTA UNA SERIE DE "<<N
<<" NUMEROS Y LOS ORDENA\n";

for (x=0;x<N;x++) //Alta de la serie de números


{
cout<<"No. "<<x+1<<": ";
cin>>*Num; //Asigna el numero
Num++; //Incrementa el apuntador
}
Num=Num-N; //Apunta al primer elemento

for (x=1;x<N;x++) //Método de burbuja


for(y=N-1;y>=x;y--)
if(*(Num+y-1)>*(Num+y))
{
Temp=*(Num+y-1); //Intercambio de elementos
*(Num+y-1)=*(Num+y);
*(Num+y)=Temp;
}

for (x=0;x<N;x++) //Imprime el contenido del apuntador


{
cout<<"\n "<<*Num;
Num++;
}
delete(Num);
getch();
}
Programación C y C++ 67

5.- El Maestro de la materia de Programación III desea conocer el Índice de


Aprobación y Reprobación de su grupo de N alumnos, la calificación mínima
que se considera como aprobatoria es de 70%. Escribir un programa que lea
las calificaciones del grupo de alumnos visualice el Número de alumnos
aprobados, el índice de aprobación, el número de alumnos reprobados y el
índice de reprobación.

/*Indice.cpp */

#include <conio.h>
#include <iostream.h>
#define N 5 //Número de alumnos

void main()
{ clrscr();
int x, Cal[N];
float NApr=0,NRep=0,IApr,IRep;
cout<<"CALCULA EL INDICE DE APROBACION Y REPROBACION";
cout<<"\nDE "<<N<< " ALUMNOS, PROPORCIONE LAS CALIFIC.\n";
for (x=0;x<N;x++)
{
cout<<x+1<<": ";

cin>>Cal[x];
if (Cal[x]>=70)
NApr++;
else
NRep++;
}
IApr=(NApr/N)*100;
IRep=(NRep/N)*100;
cout<<"\nNo. de Aprobados "<<NApr;
cout<<"\nIndice de aprobacion "<<IApr<<" %";
cout<<"\nNo. de Reprobados "<<NRep;
cout<<"\nIndice de Reprobacion "<<IRep<<" %";
getch();
}
68 Programación C y C++

EJERCICIOS PROPUESTOS

1.- Tomando como base el programa del ejercicio resuelto 1, calcular además
el Costo total , El IVA total y el Precio de Venta Total de los N artículos.
2.- Ordenar en forma descendente una lista de N Nombres aplicando el
método de Inserción
3.- Igual que el ejercicio 2 pero aplicando el método Shell
4.- Considérese la siguiente secuencia de nombres proporcionados al
programa del ejercicio resuelto 2: Ana, Carlos, Zenaido, Raúl, Víctor.
Explique el proceso de ordenamiento.
5.- AGREGAR MAS EJERCICIOS
Programación C y C++ 69

Capitulo 4

Funciones y Estructuras
• Funciones
• Pase de parámetros
• Estructuras
• Arreglos de Estructuras
• Ejercicios
70 Programación C y C++

FUNCIONES
Las funciones son la piedra angular en C y C++. Todo lo que se programa en
C y C++ esta dentro de una función. Esto se debe a que todos los programas
deben incluir la función main(), que es en si misma una función. Aunque C++
esta orientada a objetos, las funciones siguen siendo parte de este estilo de
programación ya que dentro de las clases se hayan un conjunto de funciones,
este tema se tratará más adelante, en este capitulo se va ha destacar la
importancia de la programación modular.
La ejecución de un programa comienza por la función main(), cuando
se llama a una función, el control se pasa a la misma para su ejecución; y
cuando finaliza, el control es devuelto de nuevo al módulo que llamo, para
continuar con la ejecución del mismo a partir de la sentencia que efectuó la
llamada.

Sintaxis:
Tipo_de_retorno Nombre_funcion (Lista de parámetros)
{
cuerpo de la función
}

Donde:
Tipo_de_retorno especifica el tipo de valor que devuelve la sentencia
return de la función. El valor puede ser cualquier tipo válido. Si no se
especifica ningún tipo, el compilador asume que la función devuelve como
resultado un entero, las funciones que no devuelven ningún valor deben tener
el tipo void. La Lista de parámetros es la lista de nombres de variables
separados por comas con sus tipos asociados que reciben los valores de los
argumentos cuando se llama a la función. Una función puede no tener
parámetros en cuyo caso la lista de parámetros esta vacía. Sin embargo,
incluso cuando no hay parámetros se requieren los paréntesis y la lista de
parámetros puede estar sustituida por void.

Ejemplo:
float Area_Triang(int Base, int Altura)
{
float Area;
Area=(Base*Altura)/2;
return(Area);
}

La función Area_Tring retorna un valor real (observe la sentencia


return), y recibe como parámetro dos enteros. Las variables que se definan en
el interior de la función son considerados como variables locales, estos
incluyen a los parámetros, por lo que se crean cuando se llama a la función y
perduran solamente durante la ejecución de la misma, destruyéndose en el
momento de finalizar, excepto si tienen el especificador static.
Programación C y C++ 71

• Llamada a una función


La llamada a una función tiene la forma:

[Variable=]Nombre_Funcion([Lista de argumentos])

Donde:
Variable.- Especifica la variable donde va a ser almacenado el valor devuelto
por la función. La llamada puede prescindir del valor devuelto por la función.
Lista de argumentos.- Es la lista de expresiones separados por comas, Los
valores resultantes son pasados a la función y asignados a sus
correspondientes parámetros, el número de argumentos debe ser igual al
número de parámetros definidos en la función.
Ejemplo:
A=Area_Triang(10,15);

La llamada a la función Area_Triang envía como argumentos los


valores 10 y 15, y se almacenan en las variables Base y Altura
respectivamente que están definidos como parámetros en la implementación
de la función, dentro de la función se realiza el calculo y el resultado se
retorna, asignándosele a la variable A.

PASE DE PARÁMETROS

Se pueden pasar argumentos a las funciones de dos formas:


• Llamada por valor
• Llamada por referencia

El primer método copia el valor de un argumento en el parámetro de la función. De esta

forma , los cambios en los parámetros en la función no afectan a las variables que se usan

en la llamada.

La llamada por referencia copia la dirección del argumento en el


parámetro. Dentro de la función se usa la dirección para acceder al argumento
usado en la llamada. Esto significa que los cambios hechos a los parámetros
afectan a la variable usada en la llamada a la función.

Ejemplo:
/*Referen.cpp */

#include <conio.h>
#include <iostream.h>
void Intercambio(int *A, int *B);

void main()
{ int x=10,y=50;
Intercambio(&x, &y); //Llamada por referencia
cout<<x<<" "<<y; //Imprime 50 10
72 Programación C y C++

void Intercambio(int *A, int *B)


{
int temp;
temp=*A;
*A=*B;
*B=temp;
}

La función Intercambio() recibe como parámetros dos punteros A y B


e intercambia sus contenidos. Desde la función main() se encuentran
definidos las variables x, y con los valores 10 y 50 respectivamente, al llamar a
la función intercambio se pasan como argumentos sus direcciones,
anteponiendo a cada variable el operador &, al retorno de la función los
valores de x, y se encuentran intercambiados.

Cuando se desee modificar el argumento se recomienda efectuar


una llamada por referencia y si no se desea modificar el argumento
entonces se hará una llamada por valor.

• Pase de arreglos como parámetros

Cuando se usa un arreglo como argumento de una función, sólo se pasa la


dirección del arreglo, no una copia del arreglo entero. Cuando se llama a una
función con un nombre de un arreglo, se pasa a la función un puntero al
primer elemento del arreglo, esto significa que la declaración del parámetro
debe ser un tipo puntero compatible. Existen tres formas de declarar un
parámetro que va a recibir un puntero a un arreglo:
• Como un arreglo determinado
• Como un arreglo indeterminado (sin tamaño)
• Como un puntero

Ejemplo:
/*Ar_para.cpp
Lee el salario por día de N trabajadores, calcula el total a
pagar por semana a cada trabajador y el monto total.*/

#include <conio.h>
#include <iostream.h>
#define N 5
//Definición de funciones
void Leer(float SD[N]);
void Calcular(float SD[], float SS[]);
void Visualizar(float *SD, float *SS);

float Monto=0; //Variable global

void main()
{
float SalDia[N], SalSem[N];
Programación C y C++ 73

Leer(SalDia);
Calcular(SalDia,SalSem);
Visualizar(SalDia,SalSem);
getch();
}

void Leer(float SD[N]) //Arreglo como parámetro determinado


{ clrscr();
int x;
cout<<"Proporcione el Salario Diario del Trabajador No. 1 al
"<<N<<"\n";
for (x=0;x<N;x++)
{
cout<<x+1<<" : ";
cin>>SD[x];
}
}

void Calcular(float SD[],float SS[]) //Como arreglo Indet.


{ int x;
for (x=0;x<N;x++)
{
SS[x]=SD[x]*7;
Monto=Monto+SS[x];
}

void Visualizar(float *SD,float *SS) //Como apuntador


{ int x;
cout<<"\nTrabajador S/Diario S/Semanal";
for (x=0;x<N;x++)
cout<<"\n\t"<<x+1<<"\t"<<SD[x]<<"\t"<<SS[x];
cout<<" Monto: "<<Monto;
}
La salida del programa sería:
Proporcione el Salario Diario del Trabajador No. 1 al 5
1 : 80
2 : 90
3 : 70
4 : 90
5 : 80

Trabajador S/Diario S/Semanal


1 80 560
2 90 630
3 70 490
4 90 630
5 80 560 Monto: 2870

El ejemplo Ar_param.cpp contiene tres funciones, que ilustran las diferentes


formas de definir una arreglo como parámetro. En cada caso el compilador de
C convierte la definición apropiadamente para recibir un puntero a entero.
Una explicación general del programa es el siguiente:
74 Programación C y C++

En la función principal se definen los arreglos SalDia y SalSem de tipo


entero y tamaño N (5), estas variables son locales a la función principal por lo
que no pueden ser vistas desde otra función, cuando se crean los arreglos por
defauld se inicializan con el valor de 0 para cada uno de sus elementos, La
primera función que se llama es la función Leer(), enviando como argumento
el arreglo SalDia (la dirección de su primer elemento), dentro de la función se
le asigna por el usuario desde teclado valores a cada uno de sus elementos, al
retornar de la función el arreglo SalDia, ahora ya tiene asignado valores,
mientras que el arreglo SalSem aun no, ambos arreglos son enviados como
argumento a la función Calcular(), que utiliza los valores del arreglo SalDia
para calcular el salario semanal y almacenarlos en el arreglo SalSem que más
tarde serán usados para su visualización en pantalla.

Se puede observar en cada implementación de las funciones que el


nombre de los identificadores de los arreglos difieren del nombre que tienen
desde donde se hacen las llamadas, sin embargo por el hecho de que las
llamadas se hacen por referencia (Una dirección) es posible modificar el
contenido de los argumentos dentro de la función.

De las tres formas que se puede declarar un parámetro que va a recibir


un puntero a un arreglo, la más común es como un puntero, esto se permite
porque cualquier puntero se puede indexar usando [] como si fuese un
arreglo.

ESTRUCTURAS

Una estructura es un conjunto de datos relacionados entre sí y referenciado


bajo un único nombre. Cada elemento de una estructura recibe el nombre de
Dato miembro.
Sintaxis para la definición de una estructura:
struct etiqueta{
Tipo varible1;
Tipo variable2;
Tipo variable3;
.
.
.
};

Etiqueta.- Es un identificador de la estructura que va a representar un


nuevo tipo de datos definido por el programador, en el interior se definen cada
uno de los datos miembros (variables), al finalizar la estructura lleva ; que
índica que es el final de la sentencia.

Ejemplo:
Supóngase que se desea contar con información de los alumnos de una
Institución:
Programación C y C++ 75

struct Alumnos{
char N_Ctrl[9];
char Nom[40];
char Dir[40];
char Ciudad[40];
char Tel[15];
int Edad;
};

Hasta el momento solo se ha definido la estructura más no se ha


declarado una variable de tipo estructura, para hacerlo sería:

Alumnos A;

Cuando se declara una variable de tipo estructura es cuando el


compilador reserva espacio en memoria para almacenar la estructura, la
cantidad de memoria requerida será igual a la suma de la memoria requerida
por cada uno de sus datos miembros, en este caso se requeriría un total de 146
bytes (9+40+40+40+15+2).

La definición de la estructura representa un nuevo tipo de dato, por lo


que se pueden declarar a partir de ella el número de variables que se deseen.
Ejemplo:
Alumnos A2,A3,An;

Los elementos individuales de la estructura se referencían utilizando el


operador . (operador punto). Sintaxis:

Nombre_var_estructura.Dato_miembro;

Por ejemplo, el siguiente código asigna la edad 20 al dato miembro


Edad de la variable estructura A:
A.Edad=20;

El ejemplo completo para asignar y visualizar los datos a una estructura


Alumnos se muestra a continuación:
/*Estruct.cpp*/

#include <string.h>
#include <conio.h>
#include <iostream.h>
struct Alumnos{
char N_Ctrl[9];
char Nom[40];
char Dir[40];
char Ciudad[40];
char Tel[15];
int Edad;
76 Programación C y C++

};

void main()
{ clrscr();
Alumnos A; //Definición de una variable estructura
//Asignación de datos
strcpy(A.N_Ctrl,"95190205");
strcpy(A.Nom,"Sanchez L¢pez Juan");
strcpy(A.Dir,"Av. 5 de Mayo No. 37");
strcpy(A.Ciudad,"Juchitan, Oax.");
A.Edad=20;
//Visualización
cout<<"\Los datos del alumno son :";
cout<<"\n"<<A.N_Ctrl;
cout<<"\n"<<A.Nom;
cout<<"\n"<<A.Dir;
cout<<"\n"<<A.Ciudad;
cout<<"\n"<<A.Edad;
getch();
}

ARREGLOS DE ESTRUCTURAS
De la misma forma que se pueden crear arreglos con los tipos de datos básicos
de C (int, char, float) tambien se pueden crear arreglos de estructura. Para
declarar un arreglo de estructuras, se debe definir primero la estructura y
luego declarar una variable arreglo de dicho tipo. Ejemplo:

Alumnos Al[5];

Se declara un arreglo de estructura de 5 elementos del tipo Alumnos,


esto reserva 730 bytes (146*5) en memoria RAM para su almacenamiento.

Para acceder a una determinada estructura , se indexa el nombre de la


estructura . Ejemplo:

Al[2].Edad=19;

El código anterior asigna la edad 19 a la estructura 3 de su dato miembro


Edad (Recuerde que los índices de los arreglos inician con 0)

Parar aclarar el uso de los arreglos de estructura he aquí un ejemplo que


asigna datos a cada uno de los datos miembros de un arreglo de estructura.
/*Estruct2.cpp*/

#include <stdio.h>
#include <conio.h>
#include <iostream.h>
#define N 5

struct Alumnos{ //definición de la estructura


char N_Ctrl[9];
Programación C y C++ 77

char Nom[40];
char Dir[40];
char Ciudad[40];
char Tel[15];
int Edad;
};

void main()
{ clrscr();
Alumnos Al[N]; //Definición del arreglo de estructura
int i;
cout<<"NOMBRE No. COTROL DIRECCION CUIDAD EDAD";
for (i=0;i<N;i++)
{
gotoxy(1,i+2); gets(Al[i].Nom);
gotoxy(18,i+2); gets(Al[i].N_Ctrl);
gotoxy(31,i+2); gets(Al[i].Dir);
gotoxy(50,i+2); gets(Al[i].Ciudad);
gotoxy(62,i+2); cin>>Al[i].Edad;
}
}

Como N es igual a 5 el arreglo de estructura se crea de 5 elementos, por cada


ciclo que ejecuta la sentencia for se asignan valores a cada uno de los datos
miembros de la estructura, así se asignaran datos a los 5 elementos que
contiene el arreglo de estructura (Al[0],Al[1],Al[2],Al[3] y Al[4]).

• Pase de estructuras a funciones

Los ejemplos que se han presentados sobre estructuras y arreglos de


estructuras, se han definido como variables locales a la función main() y se
han usado solamente dentro de esta función, cuando se requiere usarlos en
otras funciones se requiere de pasar la estructura como argumento a la
función. Existen los mismos dos métodos vistos anteriormente en el tema de
pase de parámetros:
• Llamada por valor
• Llamada por referencia
Cuando se pasa una estructura como argumento a una función por valor, se
pasa una copia de la estructura integra, lo cual significa que todos los cambios
realizados en los contenidos de la estructura dentro de la función a la que se
pasa no afectan a la estructura utilizada como argumento.
Ejemplo:
/*Estruct3.cpp */

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

struct Producto{
char Prod[40];
float Pr_unid;
78 Programación C y C++

int Cant;
};

void Visualizar(Producto Pr);

void main()
{ clrscr();
Producto P;
strcpy(P.Prod,"Tarjeta Madre Pentium III");
P.Pr_unid=1080;
P.Cant=5;
Visualizar(P);

getch();
}

void Visualizar(Producto Pr) //Parámetro por valor


{
cout<<"\n"<<Pr.Prod;
cout<<"\n"<<Pr.Pr_unid;
cout<<"\n"<<Pr.Cant;
}

En la función main() se crea la variable de estructura P del tipo


Producto y se asignan valores a cada uno de sus datos miembros Prod,
Pr_unid y Cant posteriormente se llama a la función Visualizar() y se le pasa
como argumento la estructura P, esto significa que en la función Visualizar()
se recibe una copia de la estructura asignándose a la variable Pr para su
impresión en pantalla, si dentro de la función se modificara un dato miembro,
no afectaría al argumento P , es decir los valores asignados a cada uno de los
datos miembro de P seguirían iguales al retorno de la función Visualizar().
Observe la forma en que se define el parámetro de estructura en la
función Visualizar(), es la forma general de la definición de una variable, es
decir primero se escribe el tipo de dato, en este caso es Producto y
posteriormente la variable (Pr), el nombre de la variable como parámetro
puede o no ser diferente del argumento, aunque en ambos casos son
diferentes.

Llamada por referencia.- Cuando se desee modificar el contenido de la


estructura que se usa como argumento en la llamada a una función se debe
optar por una llamada por referencia. En este caso se pasa la dirección de
inicio de la estructura a la función anteponiendo a la variable el operador &,
dentro la función la estructura debe definirse como un apuntador.
Ejemplo:
/*Estruct4.cpp */

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

struct Producto{
char Prod[40];
Programación C y C++ 79

float Pr_unid;

int Cant;
};

void Leer(Producto *Pr);


void Visualizar(Producto Pr);

void main()
{ clrscr();
Producto P; //Definicón de la variable de estructura
Leer(&P);
Visualizar(P);
getch();
}

void Leer(Producto *Pr) //Parámetro por referencia


{
strcpy(Pr->Prod,"Tarjeta Madre Pentium III");
Pr->Pr_unid=1080;
Pr->Cant=5;
}

void Visualizar(Producto Pr) //Parámetro por valor


{
cout<<"\n"<<Pr.Prod;
cout<<"\n"<<Pr.Pr_unid;
cout<<"\n"<<Pr.Cant;
}

El ejemplo Estruct4.cpp hace exactamente lo mismo que el ejemplo


Estruct3.cpp con la diferencia de que la asignación de valores a los datos
miembros de la estructura P se realiza a través de la función Leer()
enviándosele como argumento la dirección de inicio de la estructura P, es
decir mediante una llamada por referencia. Observe la forma en que se define
el parámetro de estructura en la función Leer(), se define como un apuntador,
para acceder a un dato miembro de la estructura en lugar de utilizar el
operador . ahora se utiliza el operador -> .
Obviamente que si una variable de estructura en lugar de que sea
definido como variable local, se define como variable global, no abría
necesidad del pase de parámetros a la función, sin embargo esto trae la
desventaja de degradar la portabilidad del programa o la independencia de la
función así como producir posibles efectos secundarios.

Cuando se trate de una arreglo de estructura, la única forma de pasar la


variable como argumento, es mediante una llamada por referencia. Ejemplo:

Alumnos A[10]; //Arreglo de estructura


Altas(A); //Llamada por referencia

Se supone que la estructura Alumnos fue previamente definido así que A es un


arreglo de estructura, al usar únicamente el nombre del arreglo sin índice, en
80 Programación C y C++

realidad se trata de la dirección de inicio del arreglo, la segunda línea por lo


tanto es una llamada por referencia, que no debe confundirse con una llamada
por valor, en este caso se trata de un arreglo y en el último una estructura
simple.

EJERCICIOS RESUELTOS

1.- Programa que muestre un menú de opciones en pantalla con las siguientes
opciones: Altas, Bajas, Visualizar y Terminar, para manejar un arreglo de
estructuras de N alumnos con los siguientes datos: Número de control,
nombre, dirección, ciudad, teléfono y edad. En la opción de altas validar si el
registro esta vacío para proceder y si no indicarlo con un mensaje; en el
proceso de bajas caso inverso. Durante la visualización solamente se
mostrarán los registros que ya fueron dados de alta.

/*Alumnos.cpp */

#include <string.h>
#include <stdio.h>
#include <conio.h>
#include <iostream.h>
#define N 10 //10 Alumnos, se puede modificar

struct Alumnos{ //definición de estructura


char N_Ctrl[9];
char Nom[40];
char Dir[40];
char Ciudad[40];
char Tel[15];
int Edad;
}; //definición de funciones del usuario
void Formatear(Alumnos A[N]);
void Altas(Alumnos A[N]);
void Bajas(Alumnos A[N]);
void Visualizar(Alumnos A[N]);

void main()
{ char op;
Alumnos A[N]; //Arreglo de estructuras
Formatear(A);
do{
clrscr();
cout<<"MANEJO DE UN ARREGLO DE ESTRUCTURA";
cout<<"\n1.-Altas";
cout<<"\n2.-Bajas";
cout<<"\n3.-Visualizar";
cout<<"\n4.-Terminar";
do{
op=getch();
}
while (op!='1'&&op!='2'&&op!='3'&&op!='4');
Programación C y C++ 81

switch (op){
case '1': Altas(A); //Llamada por referencia
break;
case '2': Bajas(A);
break;
case '3': Visualizar(A);
break;
}
}
while (op!='4');
}

//formatea todos los registros desde 0 hasta N-1


void Formatear(Alumnos A[N])
{ int x;
for(x=0;x<N;x++)
{
strcpy(A[x].N_Ctrl,"\n");
strcpy(A[x].Nom,"\n");
strcpy(A[x].Dir,"\n");
strcpy(A[x].Ciudad,"\n");
strcpy(A[x].Tel,"\n");
A[x].Edad=0;
}
}

//Da de alta el registro si esta vacío, valida el intervalo


void Altas(Alumnos A[N])
{ int Reg;
do{
do{
clrscr();
cout<<"No. de reg. entre 1 y "<<N<<",0 Para term. :";
cin>>Reg;
}
while (Reg<0 || Reg>N);
if(Reg!=0 )
if(A[Reg-1].Edad==0)
{
cout<<"\nNombre: ";
gets(A[Reg-1].Nom);
cout<<"No. de Control: ";
gets(A[Reg-1].N_Ctrl);
cout<<"Direci¢n ";
gets(A[Reg-1].Dir);
cout<<"Ciudad ";
gets(A[Reg-1].Ciudad);
cout<<"Telefono: ";
gets(A[Reg-1].Tel);
cout<<"Edad: ";
cin>>A[Reg-1].Edad;
}
else
{
cout<<"\nRegistro Ocupado";
getch();
82 Programación C y C++

}
}
while(Reg!=0);
}

//Da de baja el registro si esta ocupado, valida el intervalo


void Bajas(Alumnos A[N])
{ int Reg;
do{
do{
clrscr();
cout<<"PROCESO DE BAJAS";
cout<<"\nNo. de registro entre 1 y "<<N<<", 0 Para
terminar :";
cin>>Reg;
}
while (Reg<0 || Reg>N);
if(Reg!=0 )
if(A[Reg-1].Edad!=0)
{
strcpy(A[Reg-1].N_Ctrl,"\n");
strcpy(A[Reg-1].Nom,"\n");
strcpy(A[Reg-1].Dir,"\n");
strcpy(A[Reg-1].Ciudad,"\n");
strcpy(A[Reg-1].Tel,"\n");
A[Reg-1].Edad=0;
cout<<"\n Registro "<<Reg<<" dado de baja ";
getch();
}
else
{
cout<<"\nRegistro Vac¡o";
getch();
}
}
while(Reg!=0);
}

//Visualiza la estructura en forma de tabla si ya fue dada de


alta
void Visualizar(Alumnos A[N])
{ int x,y=2;
clrscr();
cout<<"No. Nombre No.Ctrl Direcci¢n Ciudad
Telefono Edad";
for (x=0;x<N;x++)
if (A[x].Edad!=0)
{
gotoxy(1,y);cout<<x+1<<" "<<A[x].Nom;
gotoxy(17,y);cout<<A[x].N_Ctrl;
gotoxy(27,y);cout<<A[x].Dir;
gotoxy(44,y);cout<<A[x].Ciudad;
gotoxy(59,y);cout<<A[x].Tel;
gotoxy(69,y);cout<<A[x].Edad;
y++;
}
getch();
Programación C y C++ 83

}
84 Programación C y C++

2.- Programa que simule un cajero automático de un banco. Al iniciar el


programa presentará una pantalla con las siguientes opciones: Deposito,
Retiro, Saldo y Salir. El saldo inicial deberá ser igual a $2,500.00. Después de
realizar un deposito y un retiro se mostrará el saldo actualizado, verificando
que no se permita retirar una cantidad superior al saldo, si esto ocurre se
deberá mostrar el mensaje correspondiente.

/*Cajero.cpp */
#include <iostream.h>
#include <conio.h>
void Deposito();
void Saldo();
void Retiro();
float Sal=2500; //Saldo inicial

void main()
{ char op;
do{
clrscr();
cout<<"CAJERO AUTOMATICO";
cout<<"\n1.- Depósito";
cout<<"\n2.- Retiro";
cout<<"\n3.- Saldo";
cout<<"\n4.- Salir";
do{
cout<<"\n\nOpcion:";
op=getch();
}
while (op!='1'&&op!='2'&&op!='3'&&op!='4');
switch(op){
case '1':Deposito();
break;
case '2':Retiro();
break;
case '3':Saldo();
break;
}
}
while (op!='4');
}

void Deposito() //Proceso de Deposito


{int Cant;
clrscr();
cout<<"BIENBENIDO A LA OPCION DE DEPOSITO";
cout<<"\n Cantidad: ";
do{
cin>>Cant;
}
while(Cant<=0);
Sal=Sal+Cant;
Saldo();
}
Programación C y C++ 85

void Saldo() //Visualiza el Saldo


{
clrscr();
cout<<"EL SALDO ES: "<<Sal;
getch();
}

void Retiro() //Proceso de Retiro


{int Cant;
do{
clrscr();
cout<<"BIENBENIDO A LA OPCION DE RETIRO";
cout<<"\n Cantidad: ";
cin>>Cant;
if (Cant>Sal)
{
cout<<"\nRebasa su Saldo";
getch();
}
}
while (Cant>Sal && Sal!=0);
if (Sal!=0)
Sal=Sal-Cant;
Saldo();
}

3.- Una institución de nivel superior cuenta con 6 carreras ó especialidades,


cada especialidad puede tener un máximo de 13 grados ó semestres y desea
llevar el control de alumnos para manejar los datos tales como nombre del
alumno, edad, especialidad y grado. Escribir un programa que permita dar de
alta una población de N alumnos y consultas por especialidad y grado.

/*Escolar.cpp */

#include <string.h>
#include <iostream.h>
#include <conio.h>
#include <stdio.h>
#define N 5 //Población de alumnos
#define G 13 //Grado ó semestres permitidos
#define C 6 //Carreras ó Especialidad

struct Est{
char Nom[40];
int Edad;
int Esp;
int Grado;
};

Est E[N]; //Variable global.- Arreglo de estructura

void Altas();
void Grado();
void Especialidad();
void Inicializa();
86 Programación C y C++

void main()
{ char op;
Inicializa();
do{
clrscr();
cout<<"CONTROL ESCOLAR";
cout<<"\n1.- Altas";
cout<<"\n2.- Consulta por Grado";
cout<<"\n3.- Consulta por Especialidad";
cout<<"\n4.- Salir";
do{
cout<<"\n\nOpcion:";
op=getch();
}
while (op!='1'&&op!='2'&&op!='3'&&op!='4');
switch(op){
case '1':Altas(); //Pasa la direcci¢n de inicio
break;
case '2':Grado();
break;
case '3':Especialidad();
break;
}
}
while (op!='4');
}

void Inicializa() //Inicializa la estructura


{ int x;
for (x=0;x<N;x++)
{
strcpy(E[x].Nom,'\0');
E[x].Edad=0;
E[x].Esp=0;
E[x].Grado=0;
}
}

void Altas() //Proceso de Alta


{ int reg;
do{
clrscr();
cout<<"PROCESO DE ALTAS";
cout<<"\n Registro del 1 al "<<N<<" 0 para terminar: ";
do{
cin>>reg;
}
while (reg>N || reg<0);
if (reg!=0)
{
reg--;
if (E[reg].Edad==0)
{
cout<<"\nNombre: ";
gets(E[reg].Nom);
cout<<"Edad: ";
Programación C y C++ 87

cin>>E[reg].Edad;
cout<<"Especialidad:(1 a "<<C<<" ) ";
cin>>E[reg].Esp;
cout<<"Grado:(1 a "<<G<<" ) ";
cin>>E[reg].Grado;
}
else
{
cout<<"\nEl registro tiene datos";
getch();
}
reg++;
}
}
while (reg!=0);
}

void Grado() //Consulta por Grado


{ int gr,x;
clrscr();
cout<<"CONSULTA POR GRADO";
cout<<"\n GRADO DEL 1 al "<<G<<" : ";
do{
cin>>gr;
}
while (gr>G || gr<0);
cout<<"\nLOS ALUMNOS DEL "<<gr<<" GRADO SON:";
cout<<"\n\nNombre Edad Especialidad";
for (x=0;x<N;x++)
if (E[x].Grado==gr)
cout<<"\n"<<E[x].Nom<<"\t\t"<< E[x].Edad<<"\t\t"<<
E[x].Esp;

getch();
}

void Especialidad() //Consulta por Especialidad


{ int es,x;
clrscr();
cout<<"CONSULTA POR ESPECIALIDAD";
cout<<"\n\n ESPECIALIDAD DEL 1 al "<<C<<" : ";
do{
cin>>es;
}
while (es>C || es<0);
cout<<"\nLOS ALUMNOS DE LA ESPECIALIDAD "<<es<<" SON:";
cout<<"\nNombre Edad Grado";
for (x=0;x<N;x++)
if (E[x].Esp==es)
cout<<"\n"<<E[x].Nom<<"\t\t"<< E[x].Edad<<"\t\t"<<
E[x].Grado;

getch();
}
88 Programación C y C++

EJERCICIOS PROPUESTOS.

1.- Complementar el ejercicio resuelto Cajero.cpp para que ante un deposito


la cantidad no rebase los $3,000.00 y ante un retiro no sea superior a
$2,000.00 en cada proceso.
2.- Complementar el ejercicio resuelto Escolar.cpp para que además de las
opciones que maneja contenga la opción de Bajas.
3.- Rescribir el programa Alumnos.cpp , con un ligero cambio: En lugar de
que la variable de tipo estructura sea una variable local a la función main()
que sea una variable global.
4.- En una tienda de videos se desea llevar el control de las películas para
tener a la mano los siguientes datos: Titulo, duración, mes y año de compra.
Escribir un programa que una vez dado de alta cada una de las N películas, los
muestre ordenados por su fecha de compra.
5.- Programa para calcular el área de las siguientes figuras geométricas:
Rectángulo, Circulo, Trapecio. Por cada figura se contará con una función
que retornará el área calculada, usar un menú de opciones.
6.- Una tienda de ferretería desea contar con información de los artículos que
vende tales como: Nombre, Precio unitario y Existencia, Si la existencia esta
por debajo del punto de pedido (PP = 3), deberá generar una ficha de compra
que contenga los Nombres y cantidades (PP + 6) de artículos que desea
adquirir.
Programación C y C++ 89

Capitulo 5

Programación Orientada a Objetos


• Que es la POO
• Definición de una clase
• Tipos de Accesos a una clase
• Tipos de Usuarios
• Relación entre Usuarios y Tipos de
Accesos
• Clases Amigas
• Datos Estáticos
• Constructores y Destructores
• Ejercicios
La programación Orientada a Objetos a revolucionado la técnica del
análisis y diseño de programas, la mayoría de lenguajes de
programación actuales utiliza esta técnica, de aquí la importancia de
conocerlo. En esta unidad y el siguiente se abordan los conceptos
básicos para poder utilizarlo.
90 Programación C y C++

QUE ES LA POO
La Programación Orientada a Objetos es una técnica o estilo de programación
que utiliza objetos como bloque esencial de construcción. En la programación
convencional, los programas se dividen en funciones y datos, mientras que en
la POO, un programa se divide en componentes que contienen funciones y
datos, donde cada componente se considera un objeto.

• Objeto
Es cualquier entidad que se pueda imaginar, que contiene datos y funciones. A
los datos se les conoce como datos miembros y a las funciones como
funciones miembros ó métodos .

Un objeto tiene tres propiedades que son: Estado, Comportamiento e


Identidad, El estado es el valor que guardan los datos miembros en un
instante dado, el comportamiento esta definido por el conjunto de funciones
miembro que tenga el objeto, es decir la serie de operaciones que se pueden
realizar con él, la identidad es lo que hace un objeto diferente uno de otro.
Ejemplo:
Objeto Datos Funciones
Persona Nombre Comer()
Ciudad Trabajar()
Edad Dormir()
Peso

Si los datos miembros tienen asignados valores tales como:


Nombre = ”Juvenal”, Ciudad = ”Juchitan, Oax.”, Edad = 35 y Peso = 50
representarían en su conjunto el estado del objeto. Las tres funciones serían el
comportamiento que pudiera tener ese objeto, finalmente el objeto Persona es
muy amplio por que podríamos hablar de: Amelia, Daniel, Ranulfo, etc. Cada
una de esta personal son distintas unas de otra y por tanto representarían la
identidad. En el lenguaje C la identidad básicamente estaría definida por un
bloque de memoria, en cada bloque de memoria se almacenaría un objeto
distinto.

Los datos y las funciones se encapsulan en una única entidad, conocida


como clase (class) en el lenguaje C.

DEFINICIÓN DE UNA CLASE

Una clase es un estructura de datos definido por el programador que además


de contar con datos miembros, contiene también funciones miembros, es por
ello que se dice una clase es la evolución de una estructura.

Sintaxis:
Programación C y C++ 91

Class Nombre{
Dato_miembro1;
Dato_miembro2;
...
función_miembro_1();
función_miembro2();
...
}

Ejemplo:
class Empleado{
char Nom[40];
float Salario;
void Asignar(char *N,float S);
void Calcular();
};

El ejemplo muestra una clase llamada Empleado con los datos miembros Nom
y Salario, y funciones miembros Asignar() y Calcular(), esto es solamente la
definición de la clase, cuando se define una variable de esa clase es cuando se
crea una instancia de la clase o un objeto de la clase. Ejemplo:

Empleado E; //Definición de un Objeto de la clase Empleado.

Observe la sintaxis que es exactamente igual que la sintaxis para la definición


de una variable, en el tema de estructuras se observaron varios ejemplos
similares.

Un objeto es por lo tanto una variable de un clase en particular. Para acceder a


un dato miembro de la clase o a una función miembro de la clase se usa el
operador . (operador punto) al igual que una estructura si el objeto es estático
y si es un puntero entonces se usa el operador -> (Operador flecha).

Podría pensarse que para asignar el valor de 1500 al dato miembro Salario se
podría realizar de la siguiente manera:

void main()
{
Empleado E; //Definición de un Objeto de la clase Empleado.
E.Salario=1500; //Error
}
Sin embargo dentro de una clase cada uno de sus miembros tienen un control
de acceso, y en este caso si dentro de la clase no se especifica ninguno por
default es del tipo privado, que impide que se pueda manipular desde
cualquier otra parte que no sea la clase misma. Para entender lo anterior
primero analicemos los diferentes tipos de Accesos de una clase
92 Programación C y C++

TIPOS DE ACCCESOS A UNA CLASE


Existen tres niveles de privilegio de acceso que se asocian con una palabra
reserva que son:

Tipo de Acceso Palabra reservada


1.- Privado private
2.- Protegido protected
3.- Publico public
Tabla 5. 1 Tipos de Acceso de una clase

TIPOS DE USUARIOS

El código de un programa puede hallarse en diferentes puntos y según


el lugar donde se encuentre se le conoce como una clase de usuario, que puede
ser:
• La propia Clase
• Clases derivadas
• Usuarios genéricos

Toda función que se encuentre definido dentro de una clase se le conoce


como usuarios de la propia clase, y las funciones que se encuentren en una
clase derivada (Se trata en el tema de Herencia) se les conoce como usuarios
de la clase derivada, finalmente una función que no pertenezca ni a la propia
clase ni a una clase derivada se le conoce como usuario genérico, tal es el
caso de la función main() o cualquier otra que se utilice en el programa como
complemento del mismo.

RELACIÓN ENTRE USUARIOS Y TIPOS DE ACCESOS

Una vez que se conocen los diferentes tipos de usuarios y los tipos de
acceso, se puede establecer la relación entre los tipos de usuarios y los tipos
de acceso, como se muestra en la siguiente tabla.

Tipo de Acceso Tipos de Usuarios


La propia Clase Clases derivadas Usuarios gener.
Private *
Protected * *
Public * * *
Tabla 5. 2.- Relación entre usuarios y tipos de accesos

La tabla se puede interpretar de la siguiente forma, Los usuarios que


son considerados como la propia clase tendrán privilegios sobre todos los
datos y funciones miembros de la clase, independientemente del tipo de
acceso, los usuarios de clases derivadas solamente tendrán privilegios sobre
los datos y funciones miembros que tengan el acceso del tipo protected y
Programación C y C++ 93

public, finalmente los usuarios genéricos tendrán privilegio sobre los datos y
funciones miembros que tengan el tipo de acceso public.

Tener privilegios sobre un dato o función miembro, significa poder


usarlos. Ahora se puede entender por que el objeto que se define en la función
main() no puede directamente acceder a un dato miembro de la clase si este
dato tiene el tipo de acceso private. Para poder tener el privilegio se requiere
definir un tipo de acceso public.

En la practica por lo regular se acostumbra definir a los datos


miembros de la clase con el tipo de acceso private y a las funciones miembros
como public y la forma de acceder a los datos como usuario genérico se hace
a través de las funciones miembros.

Ejemplo:
/*Clase.cpp */

class Empleado{
char Nom[40];
float Salario;
public:
void Asignar(char *N,float S);
void Calcular();
};
//falta implementación de funciones miembros

void main()
{
Empleado E;
E.Asignar("Juan Pérez",120);
}

En la definición de la clase Empleado al principio no se define ningún tipo de


acceso, esto significa que los datos Nom y Salario tienen un tipo de acceso
private y por lo tanto solo se podrán acceder desde la propia clase. Las
funciones Asignar() y Calcular() tienen el tipo de acceso public, es decir se
pueden acceder desde cualquier parte del programa, incluyendo la función
main(), sin embargo dentro de la función principal después de crear el objeto y
llamar a la función miembro Asignar() mediante la sintaxis conocida pasando
los dos argumentos necesarios, se detecta un error pero ahora en tiempo de
enlace porque ninguna de las funciones se encuentran implementados.
Una función miembro de la clase se puede implementar de dos formas:
• Implementación dentro de la definición de la clase
• Implementación independiente.

La implementación dentro de la definición de la clase se realiza


inmediatamente después de definir la función, mientas que un implementación
independiente se realiza fuera de la definición de la clase siguiendo la
siguiente regla de sintaxis:
94 Programación C y C++

Tipo_de_retorno Nombre_Clase::Nombre_Funcion(Lista_de_parámetros)
{
...//Código
}
El siguiente ejemplo muestra la implementación de la función Asignar()
dentro de la clase, y la función Calcular() mediante una implementación
independiente.

/*Clase2.cpp */

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

class Empleado{
char Nom[40];
float Salario;
public:
//implementación dentro de la clase
void Asignar(char *N,float S) {strcpy(Nom,N);Salario=S;}
void Calcular();
};

//Implementación independiente
void Empleado::Calcular()
{ float S;
S=Salario*7;
cout<<"El salario Semanal de: "<<Nom<<" :"<<S;
}

void main()

{ clrscr();
Empleado E;
E.Asignar("Juan Pérez",120);
E.Calcular();
}
La salida del programa es:
El salario Semanal de: Juan Pérez :840

En general se recomienda una implementación dentro de la clase


cuando el código de la función es pequeño (de 1 a 4 líneas de código), y la
implementación independiente cuando el código asociado a la función es más
grande.
En el programa clase2.cpp se implementa dentro de la clase Empleado
la función Asignar() que recibe como parámetro el nombre y el salario de un
empleado, mediante un puntero a carácter N y un dato real S ambos son
asignados a los datos miembros Nom y Salario respectivamente. Observe
como los datos miembros que tienen acceso privado pueden accederse desde
una función que se encuentra en la misma clase. Por otro lado la función
Calcular() se implementa de manera independiente , este calcula el salario
semanal a partir del salario diario asignado previamente al dato miembro
Programación C y C++ 95

Salario guardando el resultado en una variable local S que se utiliza para la


visualización en pantalla. Las variables locales de una función miembro de
una clase no son considerados como datos miembros de la clase.

Las funciones de la clase se pueden llamar desde cualquier otra parte


en el orden que el programador decida o convenga, así en la función main()
primero se llama a la función Asignar() para que los datos miembros se
inicialicen y posteriormente a la función Calcular() que calcula y visualiza el
resultado. Recuerde que para acceder o llamar a una función miembro de una
clase se usa el operador . si el objeto es estático y el operador -> si es
dinámico, el programa Clase2.cpp usa el operador . para llamar a una función
miembro por que el objeto se define como una variable estática. El siguiente
fragmento de código muestra el mismo programa solamente que ahora en la
función mian() se define el objeto como una variable de tipo puntero
(dinámico).

/*Clase3.cpp */
void main()
{ clrscr();
Empleado *E;
E->Asignar("Juan Pérez",120);
E->Calcular();
getch();
}

CLASES AMIGAS

Una clase puede conceder privilegios de acceso a sus zonas privadas a


otra función o clase. Tal acceso se debe indicar explícitamente declarando la
otra clase o función miembro del tipo amiga (friend). Las amigas se tratan
como si fueran miembros de la clase, y tienen acceso no restringido a las
zonas privadas y publicas de los objetos.

• Funciones amigas

Una función amiga es una función no miembro de una clase que puede tener
acceso a los datos y funciones miembros privadas de una clase. Las funciones
amigas se declaran anteponiendo la palabra reservada friend a la definición de
la función. Sintaxis:

Classs Nombre_clase{
...
friend tipo_retorno Nombre(Lista de parámetros);
...
}

Ejemplo:
96 Programación C y C++

/*AmigaF.cpp */

#include <conio.h>
#include <iostream.h>

class Prueba{
int z;
public:
int Verz(){return z;}
friend void f1(Prueba *Obj,int a);
};

//Implementación de función amiga


void f1(Prueba *Obj, int a)
{
Obj->z=a;
}

void main()
{ clrscr();
Prueba P; //Definición del objeto
f1(&P,20); //Llamada a la función amiga
cout<<P.Verz();
getch();
}

Como la función f1() se declara como amiga de la clase Prueba se


puede acceder al miembro privado z. Se puede apreciar en la implementación
de la función f1() que no obedece a la regla de sintaxis para la implementación
independiente de una función miembro de la clase, precisamente porque una
función amiga no es una función miembro de la clase. Este es solo un
mecanismo para conceder con todos los derechos sobre los datos y funciones
miembros privados y públicos de la clase.
Para poder acceder a los datos miembros privados de una clase en una
función amiga se requiere pasar como argumento un objeto de asa clase para
que a través de él se pueda acceder al dato miembro privado, por ello la
función f1() tiene como parámetros un puntero a un objeto de la clase Prueba
y un dato de tipo entero.
Las razones fundamentales para utilizar funciones amigas es que
algunas funciones necesitan acceso privilegiado a más de una clase.

Si todas las funciones miembro de una clase son funciones amigas de


una segunda clase, entonces la segunda clase debe definirse como amiga de la
primera. Sintaxis:

Classs Nombre_clase{
...
friend class nombre_clase;
...
}
Programación C y C++ 97

En este caso todas las funciones de la clase amiga pueden acceder a las
partes privadas de la otra clase. Una clase amiga debe ser declarada antes de
que se pueda ser designada como amiga.
Ejemplo:
//Pendiente

DATOS ESTÁTICOS

Un dato miembro estático significa que existe solo una instancia de ese
miembro y es compartido por todos los objetos de una clase , existe incluso si
se define ningún objeto de esa clase. Para definir un dato miembro estático se
le antepone a la definición de la variable la palabra reservada static.
A un dato miembro estático se le asigna una zona fija de
almacenamiento en tiempo de compilación, al igual que una variable global,
por ello se debe definir fuera de la clase.

Ejemplo:
/*Estatic.cpp */

#include <conio.h>
#include <iostream.h>

class Datos{
static int Cont; //Dato miembro estático
int v;
public:
void asignac(int c){Cont=c;}
void asignav(int x){v=x;}
int verc(){return Cont;}
int verv(){return v;}
};

int Datos::Cont; //Definición del dato miembro estático

void main()
{ clrscr();
Datos d1,d2; //Creación de dos objetos
d1.asignac(10);
d2.asignac(20);
d1.asignav(255);
d2.asignav(500);
cout<<d1.verc()<<"\t"<<d2.verc();
cout<<"\n"<<d1.verv()<<"\t"<<d2.verv();
}
La salida es:
20 20
255 500

En la función principal se crean dos objetos y cada objeto llama a cada una de
las funciones miembros de la clase para asignar valores a los datos miembros
Cont y v sin embargo, se observa que la salida del programa muestra el mismo
98 Programación C y C++

valor para el dato miembro estático, esto significa que no son variables
distintos para cada objeto que se cree a partir de la clase sino que es el mismo,
mientras que en el caso del dato miembro v los valores son diferentes.

CONSTRUCTORES Y DESTRUCTORES

Las clases pueden tener cualquier número de funciones miembros, pero dos
funciones especiales se pueden declarar: el constructor y el destructor.
C++ ofrece un mecanismo para inicializar objetos, cuando se crean a
través de funciones constructores y un mecanismo correspondiente para
destruir objetos cuando ya no se necesitan dentro de su ámbito a través de una
función destructor.
Los constructores y destructores son funciones miembros especiales
que contienen la mayoría de las clases. Las clases pueden declarar uno o más
constructores para inicializar automáticamente los objetos de las clases. Las
clases pueden también declarar solamente un destructor que se llama para
realizar la limpieza cuando un objeto sale de ámbito. Los constructores y
destructores siguen reglas muy estrictas de sintaxis.

• Constructores

Un constructor es una función que permite inicializar los datos miembros


cuando se crean el objeto. Se caracteriza por:
• Tener el mismo nombre de la clase que inicializa
• No retornar ningún valor
• Puede admitir parámetros como cualquier otra función
• Pueden existir más de un constructor, e incluso no existir

Si no se define ningún constructor de una clase, el constructor genera uno por


defecto. El constructor por defecto no tiene parámetros y simplemente asigna
ceros a los datos miembros de la clase.
Ejemplo:

/*Const.cpp */

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

class Producto{
char *Nom;
int Cantidad;
float Precio;
public:
Producto(); //Primer Constructor
Producto(char *N,int C,int P); //Segundo constructor
void Ver();
};
Programación C y C++ 99

//Implementación de funciones miembros


Producto::Producto()
{
Nom=new char[50];
strcpy(Nom,"Ninguno");
Cantidad=0;
Precio=0;

Producto::Producto(char *N,int C,int P)


{int L;
L=strlen(N);
Nom=new char[L];
strcpy(Nom,N);
Cantidad=C;
Precio=P;
}

void Producto::Ver()
{
cout<<"\n"<<Nom;
cout<<"\n"<<Cantidad;
cout<<"\n"<<Precio;
}

void main()
{
clrscr();
Producto P,Pro("Disco Duro 20 GBytes",4,1600); //2 objetos
Pro.Ver();
P.Ver();
getch();
}
La salida del programa es:
Disco Duro 20 Gbytes
4
1600
Ninguno
0
0

El programa Const.cpp consta de una clase llamada Producto con 2


constructores, el primer constructor no tiene ningún parámetro mientras que el
segundo constructor cuenta con tres parámetros, ambos constructores e
inclusive la función Ver() se encuentran implementados de manera
independiente, siguiendo la regla de sintaxis ya conocida, observe que
efectivamente los constructores llevan el mismo nombre que la clase, que no
retornan ningún valor, que pueden tener parámetros y que existe más de uno.

Cuando se define un objeto de la clase Producto, según el número y


tipo de argumentos que contenga, será la función constructora que se llamará,
así en la función main() al definir primeramente el objeto P sin ningún
argumento el compilador llama a la primera función constructora que
100 Programación C y C++

inicializa los datos miembros con los valores “Ninguno”, 0,0 para Nom,
Cantidad y Precio respectivamente. El segundo objeto llamado Pro con tres
argumentos llama a la segunda función constructora.

En ambos constructores se asigna memoria dinámica al dato miembro


Nom y no se observa en ningún lugar del código que se libere, de no hacerlo
cada vez que el programa se ejecute desperdiciara este espacio de memoria, el
lugar apropiado para liberar esta memoria es en una función destructor, tema
que se trata a continuación.

• Destructores

Un destructor es una función miembro con igual nombre que la clase, pero
precedido por una carácter (∼). Un destructor realiza la función opuesta de un
constructor, limpiando el almacenamiento asignado a los objetos cuando se
crean. Las funciones destructores tienen las siguientes características:
• Tiene el mismo nombre que la clase, precedido por una tilde (∼)
• No retorna ningún valor
• No tiene parámetros
• Una clase solamente puede tener como máximo un destructor.
• Se llama automáticamente por el compilador cuando el objeto sale de
ámbito.

Ejemplo:

/*Const2.cpp */
class Producto{
char *Nom;
int Cantidad;
float Precio;
public:
Producto(); //Primer Constructor
Producto(char *N,int C,int P); //Segundo constructor
void Ver();
~Producto(){delete (Nom);} //Destructor
};

Este fragmento de código es una pequeña modificación a la clase


Producto del programa Const2.cpp, se incluye un destructor implementado
dentro de la misma función. El programa puede funcionar con el resto del
código sin ningún problema por que el destructor no se debe llamar en
ninguna parte del programa, más bien el compilador lo llama en forma
automática cuando los objetos P y Pro salen de ámbito, es decir cuando
finaliza la función main()
Programación C y C++ 101

EJERCICIOS RESUELTOS

1.- Escribir un programa que cuente el número de objetos que se crean a partir
de una clase, usando un dato miembro estático y un constructor.

/*Estatic2.cpp*/

#include <conio.h>
#include <iostream.h>

class Modelo{
static int Cont; //Dato miembro estático
public:
Modelo(){Cont++;} //Constructor
int VerC(){return Cont;}
};

int Modelo::Cont=0; //Definición e inicialización

void main()
{ clrscr();
Modelo Ob1,Ob2,Ob3;
cout<<Ob1.VerC(); //Visualiza 3

Modelo Ob4,Ob5;
cout<<"\t"<<Ob1.VerC(); //Visualiza 5
}

2.- Programa para dibujar rectángulos en modo texto.


/*Rectang.cpp */

#include <conio.h>
#include <iostream.h>

class Rect{
int x1,y1,x2,y2;
public:
Rect(){x1=35;y1=10;x2=45;y2=14;} //Constructor
void Dibujar();
void Asignar(int x,int y,int j,int k){x1=x;y1=y;x2=j; y2=k;}
};

//Implementación independiente
void Rect::Dibujar()
{ int x,y;
for (x=x1;x<x2;x++)
{
gotoxy(x,y1);cout<<"Ä";
gotoxy(x,y2);cout<<"Ä";
}
for (y=y1+1;y<y2;y++)
{
gotoxy(x1,y);cout<<"³";
gotoxy(x2,y);cout<<"³";
}
102 Programación C y C++

gotoxy(x1,y1);cout<<"Ú";
gotoxy(x2,y1);cout<<"¿";
gotoxy(x1,y2);cout<<"À";
gotoxy(x2,y2);cout<<"Ù";
}

void main()
{ clrscr();
Rect R; //Se crea un objeto
R.Dibujar(); //se dibuja con valores por defecto
R.Asignar(30,8,50,16);
R.Dibujar();
getch();
}

3.- Rescribir el ejercicio resuelto Cajero.cpp presentado en el capitulo


anterior pero ahora utilizando las técnicas de la programación Orientada a
objetos.

/*Cajero2.cpp */

#include <iostream.h>
#include <conio.h>

class Cajero{
float Sal;
public:
Cajero(){Sal=2500;} //Constructor
void Deposito();
void Saldo();
void Retiro();
};

//Implementación de funciones miembros


void Cajero::Deposito()
{int Cant;
clrscr();
cout<<"BIENBENIDO A LA OPCION DE DEPOSITO";
cout<<"\n Cantidad: ";
do{
cin>>Cant;
}
while(Cant<=0);
Sal=Sal+Cant;
Saldo(); //Llamada a una función miembro
}

void Cajero::Saldo()
{
clrscr();
cout<<"EL SALDO ES: "<<Sal;
getch();
}

void Cajero::Retiro()
Programación C y C++ 103

{int Cant;
do{
clrscr();
cout<<"BIENBENIDO A LA OPCION DE RETIRO";
cout<<"\n Cantidad: ";
cin>>Cant;
if (Cant>Sal)
{
cout<<"\nRebasa su Saldo";
getch();
}
}
while (Cant>Sal && Sal!=0);
if (Sal!=0)
Sal=Sal-Cant;
Saldo();
}

void main()
{ char op;
Cajero C; //Se crea el objeto
do{
clrscr();
cout<<"CAJERO AUTOMATICO";
cout<<"\n1.- Depósito";
cout<<"\n2.- Retiro";
cout<<"\n3.- Saldo";
cout<<"\n4.- Salir";
do{
cout<<"\n\nOpcion:";
op=getch();
}
while (op!='1'&&op!='2'&&op!='3'&&op!='4');
switch(op){
case '1':C.Deposito();
break;
case '2':C.Retiro();
break;
case '3':C.Saldo();
break;
}
}
while (op!='4');
}
104 Programación C y C++

4.- Escribir un programa que maneje una clase Hora con datos miembros tales
como horas, minutos y segundos y funciones para la asignación visualización
de los datos, deberá contar con dos constructores.
/*Hora.cpp*/

#include <iostream.h>
#include <conio.h>

class Hora{
int Hra;
int Min;
int Seg;
public:
Hora(){Hra=0;Min=0;Seg=0;} //Primer Constructor
Hora(int H,int M,int S); //Segundo constructor
void Asigna();
void Ver();
};
//Implementación de funciones miembros
Hora::Hora(int H,int M,int S)
{
Hra=H;
Min=M;
Seg=S;
}
void Hora::Asigna()
{
cout<<"\nHora: ";
cin>>Hra;
cout<<"Minutos: ";
cin>>Min;
cout<<"Segundos: ";
cin>>Seg;
}

void Hora::Ver()
{
cout<<"\nLa hora es: "<<Hra<<":"<<Min<<":"<<Seg;
}

//Función principal
void main()
{ clrscr();
Hora H(9,14,10); //Creación de un objeto
H.Ver();
H.Asigna();
H.Ver();
getch();
}
Programación C y C++ 105

5.- Programa que permita convertir números en su forma rectangular a polar.


Crear una función que devuelva la magnitud y otra el ángulo en grados

/*Complejo.cpp */

#include <math.h>
#include <iostream.h>
#include <conio.h>

class Complejo{
int x,y;
public:
Complejo(int x1,int y1){x=x1;y=y1;}
void Asignar();
float MagPolar();
float AngPolar();

};

//Implementación independiente de funciones miembros


void Complejo::Asignar()
{
cout<<"\ncoordenada en el eje X: ";
cin>>x;
cout<<"Coordenada en el eje Y: ";
cin>>y;
}

float Complejo::MagPolar()
{ float Mag;
Mag= sqrt (pow(x,2)+pow(y,2)); //Por Pitagoras
return(Mag);
}

float Complejo::AngPolar()
{ float Ang,temp;
Ang=asin(x/MagPolar())*180/M_PI; //Conversión a grados
return(Ang); //por hallarse en radianes
}

//Función principal
void main()
{ clrscr();
Complejo C(5,5);
cout<<"Magnitud:"<<C.MagPolar()<<" Angulo: "<<C.AngPolar();
C.Asignar();
cout<<"Magnitud:"<<C.MagPolar()<<" Angulo: "<<C.AngPolar();
getch();
}
106 Programación C y C++

EJERCICIOS PROPUESTOS

1.- A partir del ejercicio resuelto 2 (Rectang.cpp) realice las modificaciones


necesarias para dibujar una secuencia de 10 rectángulos en la pantalla
iniciando por uno pequeño, hasta terminar con el último que llene totalmente
la pantalla, repetir este proceso cíclicamente hasta que se pulse la tecla <Esc>.
2.- Rescribir el ejercicio resuelto Escolar.cpp y Alumnos.cpp presentado en el
capitulo anterior pero ahora utilizando las técnicas de la programación
Orientada a objetos.
3.- Explique que constructor utiliza el objeto creado en el ejercicio resuelto
Hora.cpp, y cual utilizaría si se crea el siguiente objeto: Hora H2; en cada
caso que valores toman los datos miembros.
4.- Complementar el ejercicio resuelto Complejo.cpp para que además de
convertir números en su forma rectangular a polar haga también conversión de
polar a rectangular.
Programación C y C++ 107

Capitulo 6

Herencia y Polimorfismo
Clases Derivadas
Herencia Simple y Múltiples
Problema y solución de la Herencia Múltiples
Constructores y Destructores en clases
Derivadas
Polimorfismo
Sobrecarga de funciones
Funciones virtuales
Clases Abstractas
• Ejercicios
La herencia y el polimorfismo son dos características de la programación
Orientada a objetos.
La herencia se aplica para extender y reutilizar el código existente por
medio de la creación de clases a partir de clases ya existentes, conocidos como
clases derivadas.
El polimorfismo permite que los objetos se comporten de diferentes
formas cuando aparentemente se llaman a las mismas funciones.
108 Programación C y C++

CLASES DERIVADAS

Se conoce como clase derivada a aquella clase que se crea a partir de otra u
otras clases ya existentes. La clase derivada hereda las estructuras de datos y
funciones de la clase original. Además, se puede añadir nuevos miembros a
las clases derivadas y los miembros heredados pueden ser modificados.
Una clase utilizada para derivar nuevas clases se le denomina clase
base. Una clase derivada puede ser utilizada como una clase base para derivar
más clases. Por lo consiguiente se puede construir jerarquías de clases ó
árboles de herencia en la que cada clase sirve como base de una nueva clase.
En la siguiente figura se presenta un diagrama de un árbol de herencia.

Punto Clase base

Circulo Rectang Clases Derivadas

R_Relle

Figura 6. 1.- Árbol de herencia

Esta figura se basa en la idea de la herencia, donde cada uno de los


círculos o elipses representan una clase. Índica que las clases Circulo y
Rectang son clases derivadas de la clase base Punto y que a su vez la clase
R_Relle es una clase derivada de la clase base Rectang.

Los programadores de POO pueden crear bibliotecas de clases útiles


que serán el núcleo de muchos programas, como en el caso de los lenguajes
visuales, que ya traen una serie de librerías para el manejo de ventanas,
menús, cajas de diálogos, gráficos, bases de datos, etc. El usuario puede
utilizar el contenido de esas bibliotecas de clases para crear nuevas clases o su
aplicación, minimizando de esta forma el tiempo de desarrollo.

HERENCIA SIMPLE Y MÚLTIPLES

Dependiendo de cuantas clases bases tenga una clase derivada se pueden


distinguir en el lenguaje C++ como: Herencia simple ó herencia múltiple.

Herencia Simple
Es aquella en la que cada clase derivada hereda de una única clase base.

Herencia Múltiple
Es aquella en la cual una clase derivada tiene más de una clase base
Programación C y C++ 109

En la siguiente figura se muestra un árbol de herencia donde se presentan los


dos tipos de herencia.

A B

Herencia Simple
C D E F

H
G

Herencia Múltiple

Figura 6. 2.- Herencia simple y Múltiple

La declaración de una clase derivada tiene la siguiente regla de sintaxis:

class Nombre_Derivada : <Tipo de acceso> clase base{


...
}

El operador : permite derivar de una clase base. El tipo de acceso puede ser
cualquiera de los tres conocidos public, private o protected.

Ejemplo:
class Punto{ //Clase Base
private:
int x,y;
public:
...
};

class Rectang: public Punto{ //Clase derivada


int x2,y2;
public:
Dibujar();
...
};

La clase Rectang es una clase derivada de la clase base Punto, esto significa
que los datos y funciones miembros que existen en la clase base pertenecen
también a la clase derivada además de los propios, así la clase Rectang
contiene los atributos heredados x, y, además de x2, y2 que son los propios.
110 Programación C y C++

• Tipo de acceso a la clase base


Cuando una clase hereda de otra, la clase derivada hereda los datos y
funciones públicos y protegidos de la clase base. Por consiguiente, solo tiene
que especificar los datos y funciones adicionales a la clase derivada, sin
embargo según el tipo de acceso en la derivación, será la forma en que se
reciban los datos y funciones miembros en la clase derivada.
Tipo de Acceso Descripción
:public clase base Los tipos de acceso definidos en las clase base son los
mismos en clase derivada
:private clase base Todos los datos y funciones miembros de la clase base
se convierten en privados para la clase derivada.
:protected clase base Los miembros public y protected de la clase base son
miembros protegidos de la clase derivada. Los
miembros private de la clase base son privados a la
clase derivada.
Tabla 6. 1.- Tipos de Acceso durante la derivación

Al especificar el tipo de acceso public sobre una clase base significa que los
miembros heredados pasan a la clase derivada con el mismo tipo de acceso
que fueron definidos en la clase base, es por ello que en la mayoría de los
casos se opta por utilizar al crear una clase derivada este tipo de acceso.
Ejemplo:
/*Acceso.cpp */

#include <conio.h>
#include <iostream.h>

class A{ //Clase Base


public: //Cambiar a private y compilar
int a1;
};

class B:public A{ //Clase derivada


int b1;
public:
void Asignar(int a,int b){a1=a;b1=b;}
void Ver(){cout<<a1<<" "<<b1;}
};

void main()
{
B Obj;
Obj.Asignar(10,20);
Obj.Ver(); //Visualiza 10 20
}

En este ejemplo la clase B se deriva de la clase A con acceso publico. La clase


A solamente tiene el atributo a1 con tipo de acceso publico. Es decir se hereda
desde la clase derivada. Si en lugar de que el tipo de acceso sea publico fuera
Programación C y C++ 111

protegido, también se heredaría en la clase derivada, sin embargo si se cambia


a privado ya no.
En la implementación de las funciones Asignar() y Ver() de la clase
derivada B observe como se accesa directamente al dato miembro a1 que
pertenece a la clase base A como si fuera parte de la clase B, esto se puede
hacer porque la clase B hereda el atributo de la clase base A.

En la derivación privada, los miembros públicos, privados y protegidos


de la clase base no son accesibles por las funciones miembros de la clase
derivada, ni por los usuarios genéricos ni las otras clases derivadas.

En la derivación protegida los miembros públicos y protegidos de la


clase base se comportan como miembros protegidos de la clase derivada.
Estos miembros no son accesibles por los usuarios genéricos, pero las clases
que se deriven posteriormente podrán acceder a estos miembros normalmente.

• Que cosas se heredan y que cosas no

Una clase derivada hereda datos y funciones miembros de su clase base. Los
miembros heredados se pueden utilizar con objetos de clase derivada, Si un
miembro heredado se redefine en una clase derivada , el nombre redefinido
oculta el nombre heredado. Para llamar a un miembro ocultado es necesario
utilizar el operador ::.
Ejemplo:
/*FunOcult.cpp */
#include <conio.h>
#include <iostream.h>

class B{
public:
void Funcion(){cout<<"\nFunción B";} //1a. función
};
class D:public B{
public:
void Funcion(){cout<<"\nFunción D";} //2a. función
void F2();
};

void D::F2()
{
Funcion(); //Llama a la segunda función
B::Funcion(); //Llama a la primera función
}
void main()
{
clrscr();
D Obj;
Obj.F2();
}
112 Programación C y C++

Una clase derivada hereda todos los datos y funciones miembros de la


clase base excepto los siguientes:
1. Constructores
2. Destructores
3. Funciones amigas
4. Funciones estáticas
5. Datos estáticos

Si en una clase base de una clase derivada se encuentran cualquiera de los


componentes anteriores se debe tener presente que desde la clase derivada no
se podrán acceder de manera directa.

Ejemplo:
/*NoHeren.cpp */

#include <conio.h>
#include <iostream.h>

class Base{
protected:
int b;
public:
Base(int x){b=x;}
};

class Derivada:public Base{


int d;
public:
Derivada(int x, int y){Base(x);d=y;} //Error No encontrado
void Ver(){cout<<b<<" "<<d;}
};

/*Forma correcta de poder llamar a un constructor


de una clase base
Derivada::Derivada(int x, int y):Base(x)
{
d=y;
}
*/

void main()
{
Derivada Obj(30,50);
Obj.Ver();
}

En la implementación del constructor de la clase Derivada, se intenta


llamar al constructor de la clase Base, pero el compilador detecta el error por
no poder encontrarlo, la forma correcta de poder llamar a un constructor de
una clase base desde una clase derivada es hacerlo en el constructor de la clase
derivada utilizando el operador : al final de la definición del constructor como
Programación C y C++ 113

se muestra en el programa NoHeren.cpp. Para poder ejecutar el programa se


debe comentar la implementación del constructor dentro de la clase y eliminar
el comentario de la implementación independiente.

• Sintaxis de Herencia Multiple


Class Nombre_Derivada : <Tipo de acceso> clase base1, | <Tipo de acceso>
clase base2,|...| <Tipo de acceso> clase basen {
...
}

Ejemplo: Definición del siguiente árbol de herencia A B


class A{
};
class B{
};
C
class C: public A, public B{ // Herencia Multiple
};

En este ejemplo la clase C se deriva de la clase A y B, los atributos que


pudieran tener cada una de las clases base, los tendría también la clase
derivada, además de los propios.
Ejemplo:
/*Multiple.cpp */

#include <conio.h>
#include <iostream.h>

class A{
protected:
int a1;
public:
int Vera(){return a1;}
};
class B{
protected:
int b1;
public:
int Verb(){return b1;}
};

class C:public A,public B{


int c1;
public:
void Asigna(int x,int y,int z){a1=x;b1=y;c1=z;}
void Ver(){cout<<Vera()<<" "<<Verb()<<" "<<c1;}
};

void main()
{
C Obj;
Obj.Asigna(10,20,30);
Obj.Ver();
}
114 Programación C y C++

PROBLEMA Y SOLUCIÓN DE LA HERENCIA MÚLTIPLES

La herencia múltiple puede introducir ambigüedades cuando se heredan de


dos clases base que a su vez contiene ambas la misma clase base. Ejemplo:
A

B C

En este caso la clase D hereda de manera indirecta los atributos de la clase A


por dos rutas diferentes, provocando que los atributos de A se creen por
duplicado. El compilador no sabría cual es la copia que se estaría
referenciando.
Ejemplo:

/*Pro_Mul.cpp */

#include <conio.h>
#include <iostream.h>

class A{
protected:
int a1;
};
class B:public A{
};
class C:public A{
};
class D:public B, public C{
public:
void Asignar(int x){a1=x;} //Error de ambigüedad
};

void main()
{
D Obj;
Obj.Asignar(10);
}

La clase A contiene el atributo a1 que se hereda por separado a las clases B y


C, hasta aquí todo esta bien, sin embargo cuando la clase D hereda de las
clases B y C el atributo a1 se hereda por duplicado, es por eso que el
compilador detecta el error en la función Asignar(). Este problema puede tener
dos soluciones.
Programación C y C++ 115

1. Referenciar el atributo correctamente usando el operador :: en lugar de


hacerlo directamente: Ejemplo: C::a1=x; ó B::a1=x;
2. Crear una sola instancia de la clase base heredada de manera indirecta.

El aspecto del árbol de herencia para cada una de estas soluciones sería:

A A A
2ª. Solución

B C B C

1ª. Solución
D D

Aunque las dos soluciones se pueden utilizar, la primera puede


confundir al programador, mientras que la segunda se convierte en la más
idónea porque elimina totalmente la ambigüedad que pudiera existir.
Para implementar la segunda solución las clases derivadas que
producen la intersección con la clase base deben heredar virtualmente de
la clase base (Se utiliza la palabra reservada virtual).
Ejemplo:

/*Sol_Mul.cpp */

#include <conio.h>
#include <iostream.h>

class A{
protected:
int a1;
};
class B: virtual public A{
};
class C: virtual public A{
};
class D:public B, public C{
public:
void Asignar(int x){a1=x;} //OK Libre de ambigüedad
};

void main()
{
D Obj;
Obj.Asignar(10);
}

Ejercicio:
El siguiente árbol de herencia, contiene un conjunto de clases con atributos de
tipo entero: a).- Definir las clases, b).- Que atributos tendrían los objetos que
se definan a partir de cada clase.
116 Programación C y C++

A B C
a1,a2 b1 c1

D E F
d1 e1 f1,f2

G
g1

a).- Definición de clases


class A{ class E: virtual public A, public B{
protected: Protected:
int a1,a2; Int e1;
}; };
class B{ class F: public C{
protected: Protected:
int b1; Int f1,f2;
}; };
class C{ class G: public D, public E, public F{
protected: Int e1;
int c1; };
};
class D: virtual public A {
protected:
int d1;
};
b).- Atributos de los Objetos
OBJETOS
A B C D E F G
A a1 b1 c1 a1 a1 c1 a1
T a2 a2 a2 f1 a2
R d1 b1 f2 b1
I e1 d1
B e1
U c1
T f1
O f2
S g1

El ejercicio anterior da algunas ideas sobre el procedimiento para el desarrollo


de programas reales Orientados a Objetos, aunque el orden no es el apropiado.

• Pasos para el desarrollo de un Programa Orientada a Objetos

1.- Descripción de un problema real


2.- Obtención de los objetos que existen en el dominio del problema
3.- Definición de los miembros de cada objeto
Programación C y C++ 117

4.- Desarrollo del árbol de herencia


5.- Definición e implementación de clases y funciones miembros
6.- Aplicación que use las clases.

Al finalizar este capitulo se presenta un ejemplo, donde se podrá apreciar cada


uno de estos pasos, por lo pronto se continuará con las bases.

CONSTRUCTORES Y DESTRUCTORES EN CLASES DERIVADAS

Al crear clases derivadas con una sola clase base, el constructor de la clase
base se llama siempre antes que el constructor de la clase derivada. Además se
dispone de un mecanismo para pasar los valores de los parámetros necesarios
al constructor de la clase base desde el constructor de la clase derivada.

Derivada::Derivada(int x, int y):Base(x)

En este ejemplo el dato de tipo entero x se pasa al constructor de la clase base,


este método se amplía de un modo lógico para el caso de clases base
múltiples.

Derivada::Derivada(int x, int y, int w):Base1(x),Base2(w)

Aquí se llama al constructor Base1 y se le pasa el valor entero x y el


constructor Base2 el valor entero w. Al igual que con herencia simple, los
constructores de la clase base se llaman antes que se ejecute el constructor de
la clase derivada. El orden en que se ejecutan los constructores de las clases
bases, es el orden en que están listados en la declaración de la clase derivada.
En el ejemplo anterior primero se ejecuta Base1 y luego Base2.

La relación entre el destructor de la clase derivada y el destructor de la clase


base se mantiene. El destructor de la clase derivada se llama primero que el
destructor de la clase base. Los destructores de la clase base se llaman en
orden inverso al que los objetos base se crearon.

POLIMORFISMO

El polimorfismo es la propiedad por la cual los objetos que pertenecen a


clases diferentes pueden responder al mismo mensaje (función) de diferentes
formas. Por ejemplo, supóngase que se dispone de las clase Rectángulo,
Circulo, Triangulo, Cilindro, cuyos objetos representan las figuras
geométricas, los objetos de estas clases pueden contener una función Area()
que calcule el área correspondiente. Sin embargo la respuesta al mensaje area
será diferente para cada uno de los objetos. Una característica esencial del
polimorfismo es que se pueden enviar mensajes sin conocer la clase de objeto
recipiente. Por ejemplo se puede tener una lista de los posibles objetos que
pueden aparecer en la pantalla, para visualizar cualquiera de ellos basta con
118 Programación C y C++

enviar el mensaje Show() para que se visualice, sin tener que preocuparse de
cada objeto de la lista.

En C++, el polimorfismo se implementa por medio de sobrecarga de


funciones y con funciones virtuales.

SOBRECARGA DE FUNCIONES

Consiste en definir dos ó más funciones con el mismo nombre aunque con
diferencias en el número o tipo de parámetros que reciben, así la función que
se llame será el que coincida con número y tipo de argumento. Esto permite
que una función se comporte de una forma u otra dependiendo de los
argumentos, de aquí el comportamiento polimórfico. Al permitir que una clase
tenga más de un constructor es un caso de sobrecarga de funciones.
Ejemplo:
/*Sob_Fun.cpp */

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

class Matematico{
public:
int Sumar(int A, int B){return A+B;}
float Sumar(float A, float B){return A+B;}
char * Sumar(char *A, char *B){strcat(A,B);return A;}
};

void main()
{ Matematico M;
float f1=25.3,f2=34.9;

cout<<"\n"<<M.Sumar(10,20);
cout<<"\n"<<M.Sumar(f1,f2);
cout<<"\n"<<M.Sumar("Hola ", "Amigo");
}
La salida del programa es
30
60.200001
Hola Amigo

En la clase Matemático se define la función Sumar() sobrecargada (tres


veces), permitiendo sumar dos enteros, dos reales ó concatenar dos cadenas.

FUNCIONES VIRTUALES

Las funciones miembro virtuales son similares a las funciones miembro


ordinarias con la diferencia importante de que las llamadas a las funciones
virtuales se enlazan dinámicamente (ligadura dinámica) en tiempo de
ejecución (en lugar de en tiempo de compilación) a la función que llama.
Programación C y C++ 119

En C++ por defecto, las funciones tienen ligadura estática; si se desea


ligadura dinámica se debe preceder a la declaración de la función la palabra
reservada virtual.
virtual float Area()

La ligadura dinámica tiene sentido en C++, sólo para objetos que son
parte de una jerarquía de clases (herencia).

Supóngase que se desea un programa para calcula el área de círculos y


cilindros, aplicando las formulas correspondientes:
• Área del Circulo = PI*Radio*Radio
• Área del Cilindro = 2*PI*Radio*Altura + 2*PI*Radio*Radio
El árbol de herencia quedaría como se muestra en la siguiente figura.

Circulo
Radio;
virtual float Area();

Cilindro
Altura;
virtual float Area();

Figura 6. 3.- Funciones virtuales

El programa completo se muestra en el siguiente listado, conteniendo las


funciones virtuales y el comportamiento polimórfico.

/*Polimor.cpp */
#include <conio.h>
#include <iostream.h>
#define PI 3.1416

class Circulo{
protected:
int Radio;
public:
Circulo(int r){Radio=r;} //Constructor
virtual float Area(){return(PI*Radio*Radio);}
};
class Cilindro:public Circulo{
int Altura;
public:
Cilindro(int r, int h):Circulo(r){Altura=h;} //Constructor
float Area(){return(2*PI*Radio*Altura +2*PI*Radio*Radio);}
120 Programación C y C++

};

//función que proporciona el comportamiento polimórfico


float Area (Circulo Obj)
{
return(Obj.Area()); //Según el objeto recibido es la función
} // que se llama

void main()
{ Circulo C(5); //Objeto Circulo
Cilindro Cl(10,5); //Objeto Cilindro
cout<<"Area del Circulo: "<<Area(C); //Imprime 78.54
cout<<"\nArea del Cilindro: "<<Area(Cl); //Imprime 314.16
}
En C++ las funciones virtuales siguen una regla precisa: la función debe ser
declarada como virtual en la primera clase que esta presente, (en la clase
base). Una función virtual es una función que se declara en la clase base como
virtual y se redefine en una ó más clases derivadas. Las funciones virtuales,
son especiales ya que cuando se accede a una de ellas usando una referencia a
un objeto de una clase derivada, C++ determina en tiempo de ejecución a qué
función llamar en función del tipo de objeto apuntado.

CLASES ABSTRACTAS

Una clase abstracta es aquella que sólo se puede utilizar como clase base; no
se puede utilizar para declarar objetos. Desde el punto de vista del lenguaje ,
una clase es una clase abstracta si tiene al menos una función vitual pura.
Una función virtual pura es aquella cuya declaración no está seguida
por una implementación; es decir, que la clase base no puede implementar y
que se inicializan a cero. Sintaxis:
Virtual void func_pura()=0;

El uso de una clase abstracta no es estrictamente necesaria, la ventaja


de una clase abstracta reside en la elegancia de combinar las características
comunes de una serie de clases especiales en una clase base que no se utiliza
para crear un objeto.
Una clase abstracta se utiliza en los casos donde exista polimorfismo,
partiendo del echo de que las funciones virtuales que existan en el árbol de
herencia solamente se debe definir en la clase base, si esta clase base es una
clase genérica y no se requiere la definición de objetos a partir de ella
entonces se debe definir como clase abstracta.
Programación C y C++ 121

Ejemplo:
Se desea escribir un programa que maneje los datos de los Alumnos y
Maestros según la siguiente tabla:

OBJETOS
Alumno Maestro
Datos Nombre Nombre
Dirección Dirección
No. de Control Plaza
Especialidad Departamento
Funciones Alumnos() Maestro()
void Ver() void Ver()

Se aprecia que los datos Nombre y Dirección, así como la función Ver() son
comunes a los dos objetos, se puede crear una clase genérica al que se le
llame Persona que contenga estos tres miembros, y con la finalizad de darle el
comportamiento polimórfico definir la función Ver() como virtual. En este
caso no se pretende definir ningún objeto de la clase Persona por lo que
deberá ser una clase abstracta, o sea la función Ver() será definida como una
función virtual pura. El árbol de herencia queda de la siguiente forma.

Persona
Nom, Dir;
virtual void Ver()=0

Alumno Maestro
NCtrl, Esp; Plaza, Depto;
void Ver(); void Ver();

El listado del programa es:


/*Abstrac.cpp */

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

class Persona{ //Clase Abstracta


protected:
char Nom[40];
char Dir[40];
public:
Persona (char *N, char *D){strcpy(Nom,N); strcpy(Dir,D);}
virtual void Ver()=0; //Función virtual pura
};

class Alumno: public Persona{


122 Programación C y C++

char NCtrl[15];
char Esp[30];
public:
Alumno(char *N,char *D,char *Ct,char *E);
void Ver();
};

//Implementación de funciones miembros de la clase Alumno


Alumno::Alumno(char *N,char *D,char *Ct,char *E):Persona(N,D)
{
strcpy(NCtrl,Ct);
strcpy(Esp,E);
}

void Alumno::Ver()
{
cout<<"\n\nDATOS DEL ALUMNO";
cout<<"\n"<<Nom;
cout<<"\n"<<Dir;
cout<<"\n"<<NCtrl;
cout<<"\n"<<Esp;
}

class Maestro:public Persona{


char Plaza[30];
char Depto[40];
public:
Maestro(char *N,char *D,char *P,char *Dp);
void Ver();
};

//Implementación de funciones miembro de la clase Maestro


Maestro::Maestro(char *N,char *D,char *P,char *Dp):Persona(N,D)
{
strcpy(Plaza,P);
strcpy(Depto,Dp);
}
void Maestro::Ver()
{
cout<<"\n\nDATOS DEL MAESTRO";
cout<<"\n"<<Nom;
cout<<"\n"<<Dir;
cout<<"\n"<<Plaza;
cout<<"\n"<<Depto;
}

//Función que proporciona el comportamiento polimórfico


void Ver(Persona *P) //El parámetro debe ser un apuntador
{
P->Ver();
}
void main()
{ //Persona P; //Error no se puede crear un objeto

Alumno A("Juan Pérez","Av. Juárez", "93190203", "Informática");


Maestro M("Juvenal","Av. Hidalgo","Titular A","Sistemas y Computación");
Ver(&M);
Programación C y C++ 123

Ver(&A);
}
La salida del Programa es:

DATOS DEL MAESTRO


Juvenal
Av. Hidalgo
Titular A
Sistemas y Computación

DATOS DEL ALUMNO


Juan Perez
Av. Juarez
93190203
Informática

En la función principal main() se puede apreciar en la primera línea el


intento de definir un objeto de la clase Persona(), pero el compilador detecta
el error por provenir de una clase abstracta, es por ello que aparece
comentado.
Después de crear los dos objetos A y M con cada uno de los
parámetros requeridos por sus correspondientes constructores, se llama a la
función Ver() en dos ocasiones, en la primera se le pasa como argumento la
dirección del objeto M, en la en la segunda llamada la dirección del objeto A,
esta función proporciona el comportamiento polimórfico por que según el
objeto que reciba como parámetro, será a la función que llamará en tiempo de
ejecución. Así primero despliega los datos del maestro y luego los datos del
alumno.
124 Programación C y C++

EJERCICIOS RESUELTOS

1.- Complementar el ejercicio resuelto Rectang.cpp desarrollado en el


capitulo anterior, para que además de la clase Rect (Rectángulo) contenga
también otra clase deriva que le permita dibujar rectángulos rellenados.

/*RectRell.cpp */

#include <conio.h>
#include <iostream.h>

class Rect{
protected:
int x1,y1,x2,y2;
public:

Rect(){x1=35;y1=10;x2=45;y2=14;} //Constructor
void Dibujar();
void Asignar(int x,int y, int j,int k){x1=x;y1=y;x2=j;y2=k;}

};

//Implementación independiente de funciones


void Rect::Dibujar()
{ int x,y;
for (x=x1;x<x2;x++)
{
gotoxy(x,y1);cout<<"-";
gotoxy(x,y2);cout<<"-";
}
for (y=y1+1;y<y2;y++)
{
gotoxy(x1,y);cout<<"|";
gotoxy(x2,y);cout<<"|";
}
gotoxy(x1,y1);cout<<"┌";
gotoxy(x2,y1);cout<<"┐";
gotoxy(x1,y2);cout<<"└";
gotoxy(x2,y2);cout<<"┘";
}

class RecRell:public Rect{


char Car;
public:
RecRell():Rect(){Car='░';} //Llama al constructor base
void Dibujar();
void Asignar(int x,int y, int j,int k,char c);
};

//Implementación independiente
void RecRell::Dibujar()
{ int x,y;
Rect::Dibujar(); //¿A Que función se llama?
for (y=y1+1;y<y2;y++) //rellena el rectángulo
for (x=x1+1;x<x2;x++)
{
Programación C y C++ 125

gotoxy(x,y);cout<<Car;
}
}

void RecRell::Asignar(int x,int y, int j,int k,char c)


{
Rect::Asignar(x,y,j,k);
Car=c;
}

void main()
{ clrscr();
Rect R; //Objeto de la clase Rect
RecRell Rell; //Objeto de la clase RecRell
R.Asignar(10,8,30,16);
R.Dibujar(); //Dibuja un rectángulo
Rell.Dibujar(); //Dibuja un rectángulo rellenado
Rell.Asignar(50,8,70,16,177);
Rell.Dibujar();
}

2.- Resuelva el siguiente problema, aplicando los pasos para el desarrollo de


un programa orientado a objetos:
Una empresa desea generar fichas de su personal de base y eventual.
Solución:
♣ Obtención de objetos: Trabajador de base y Trabajador Eventual
♣ Datos y funciones miembros de los objetos:
TrabajadorBase TrabajadorEventual
Datos Nombre Nombre
Dirección Dirección
Salario/día Honorario/hora
Años de antigüedad
Funciones Leer() Leer()
Visualizar() Visualizar()
PagarSalario() PagarSalario()

♣ Árbol de herencia.
Trabajador
Nom, Dir,
Leer();
Visualizar();

TBase TEventual
SalDía, Ant; HonoHra;
Leer(); Visualizar(); Leer(); Visualizar();
PagarSalario(); PagarSalario();
126 Programación C y C++

♣ Definición e implementación de las clases


/*Trabaj.cpp */

#include <conio.h>
#include <stdio.h>
#include <iostream.h>

//Definición de las Clases


class Trabajador{
private:
char Nom[40];
char Dir[40];
public:
void Leer();
void Visualizar();
};

class TBase:public Trabajador{


float SalDia;
int Ant;
public:
void Leer();
void Visualizar();
void PagarSalario();
};

class TEventual:public Trabajador{


float HonoHra;
public:
void Leer();
void Visualizar();
void PagarSalario();
};

//Implementación de funciones miembros de la clase Trabajador


void Trabajador::Leer()
{
cout<<"\nNOMBRE: ";
gets(Nom);
cout<<"DIRECCION: ";
gets(Dir);
}

void Trabajador::Visualizar()
{
cout<<"\n"<<Nom;
cout<<"\n"<<Dir;
}

//Implementación de funciones miembros de la clase TBase


void TBase::Leer()
{
cout<<"\nDATOS DEL TRABAJADOR DE BASE";
Trabajador::Leer();
cout<<"SALARIO DIARIO: ";
cin>>SalDia;
Programación C y C++ 127

cout<<"AÑOS DE SERVICIO: ";


cin>>Ant;
}

void TBase::Visualizar()
{
cout<<"\nTRABAJADOR DE BASE";
Trabajador::Visualizar();
cout<<"\n"<<SalDia;
cout<<"\n"<<Ant;
}

void TBase::PagarSalario()
{ float SalSemanal;
SalSemanal=SalDia*7 + Ant*0.02*SalDia*7; //Más el 2% por año
cout<<"\nSalario semanal: "<<SalSemanal;
}

//Implementación de funciones miembros de la clase TEventual


void TEventual::Leer()
{
cout<<"\nDATOS DEL TRABAJADOR EVENTUAL";
Trabajador::Leer();
cout<<"SALARIO POR HORA: ";
cin>>HonoHra;
}

void TEventual::Visualizar()
{
cout<<"\nTRABAJADOR EVENTUAL";
Trabajador::Visualizar();
cout<<"\n"<<HonoHra;
}

void TEventual::PagarSalario()
{ float SalSemanal;
SalSemanal=HonoHra*8*7; //*8d hrs * 7 días
cout<<"\nSalario semanal: "<<SalSemanal;
}

// Programa principal
void main()
{ TBase B; //Objeto Trabajador de Base
TEventual E; //Objeto Trabajador Eventual
B.Leer();
E.Leer();
B.Visualizar();
B.PagarSalario();
E.Visualizar();
E.PagarSalario();
}
128 Programación C y C++

3.- Suponga que en una situación real se hallaron los siguientes objetos con
sus respectivos datos miembros de tipo entero:
Objetos A B C D
b1 b1 b1 b1
Datos a1 a1 a1 d1
miembros a2 d1 d1 d2
b2 b2
c2

Construir el árbol de herencia y definir las clases.

Solución:
Base
♣ Árbol de herencia: b1

Der1 Der2
a1 d1

A B D
a2 b2 d2

C
c2

♣ Definición de clases
class Base{ class B: public Der1,public
private: Der2{
int b1; private:
}; int b2;
};
class Der1:public virtual Base{
private: class C: public B{
int a1; int c2;
}; };

class Der2: public Base{ class D: public Der2{


private: int d2;
int d1; };
};

class A:public Base,public Der1{


int a2;
};
Programación C y C++ 129

EJERCICIOS PROPUESTOS

1.- Cual es la salida en pantalla del programa RectRell.cpp


2.- Muestre un ejemplo de la salida en pantalla del programa Trabaj.cpp
3.- Modifique el ejercicio resuelto Trabaj.cpp te tal forma que se utilice
polimorfismo mediante funciones virtuales (Deberá proporcionar la misma
salida en pantalla).
4.- Suponga que en una situación real se hallaron los siguientes objetos con
sus respectivos datos miembros de tipo entero:
Objetos A B C D
a1 a1 a1 a1
Atributos d1 c1 c1 d1
b1 b1 c2 d2
a2 b2
Construir el árbol de herencia y definir las clases.
5.- Proponer un problema y resolverlo aplicando los pasos para el desarrollo
de un programa orientado a objetos.
|