Programacion C

  • Uploaded by: diabliux
  • 0
  • 0
  • February 2021
  • PDF

This document was uploaded by user and they confirmed that they have the permission to share it. If you are author or own the copyright of this book, please report to us by using this DMCA report form. Report DMCA


Overview

Download & View Programacion C as PDF for free.

More details

  • Words: 27,374
  • Pages: 129
Loading documents preview...
Programación C y C++

Programación Lenguaje C y C++

M. C. Gastón Dehesa Valencia

1

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++

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

3

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: [email protected]

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 Borland

Visual C++ Ver 6.0 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 #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. para uso de funciones de entrada/salida por flujos. para uso de funciones de entrada/salida por teclado o consola para la asignación dinámica de memoria para uso de funciones relacionadas con gráficas 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 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.

11

Programación C y C++ 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

Cl.lib

Princip.obj

Archivo2.obj

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 Tipo Texto Cuando se usa Tiempo de Compilación Contenido Definiciones

Archivo de Librería Binario Tiempo de ligado 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 unsigned char Char unsigned int short int Int unsigned long int long int Float double float

# de Bits Intervalo 8 0 a 255 8 -127 a 127 16 0 a 65535 16 Lo mismo que int 16 -32,768 a 32,767 32 0 a 4,294,967,295 32 -2,147,483,648 a 2,147,483,647 32 3.4E-38 a 3.4E38 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 unsigned char char unsigned int short int int unsigned long int long int float double float

# de Bits Intervalo 8 0 a 255 8 -128 a 126 32 0 a 4,294,967,295 16 -32,768 a 32,767 32 -2,147,483,648 a 2,147,483,648 32 Lo mismo que unsigned int 32 Lo mismo que int 32 1.2E-38 a 3.4E381 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 OPERADORES ARITMÉTICOS + Adición x=x+z; Sustracción x=x-z; * Multiplicación x=x*z / División x=x/z; % Modulo o resto x=x%z; OPERADORES LÓGICOS && AND Lógico if (x&&0xFF) || OR Lógico if (x||0xFF) NOT Lógico If (!Soltero) ! OPERADORES RELACIONALES == Igual que if (x==y) Diferente que != < Menor que > Mayor que <= Menor o igual que >= Mayor o igual que OPERADORES DE CLASE Y ESTRUCT. :: Resol. de alcance Punto::Dibujar() -> Miembro indirecto P->Dibujar(); . Miembro directo P.Borar();

Operador

Descripción Ejemplo OPERADORES DE ASIGNACIÓN = Asignación x=10; += Asignar y sumar x+=10; -= Asignar y restar x-=10; *= Asignar y multiplicar x*=10; /= Asignar y dividir x/=10; &= Asig. operador AND x&=0x02; |= Asignar operador OR x|=0x02; OPERADORES A NIVEL DE BITS & AND a nivel de bits C=A&B; | OR a nivel de bits XOR a nivel de bits C=A∧B; ∧ << Desp. a la izquierda B=A<<3; >> Desp. a la derecha C=A>>2; ~ NOT a nivel de bits A=~D; OPERADORES DE APUNTADORES * Indirección Int *Ap; & Dirección x=&Ap; OTROS ++ Incremento x++; -Decremento x--;

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

17

Programación C y C++ 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 1 2 3 4 5 6 7 8 9 10 11 12 13 14

Operadores () [] -> :: . ! ~ ++ -- & * * / % + << >> < <= > >= = != & ∧ | && || = *= -= *= %= < <= > >= &= ∧= |= ,

Asociatividad Izquierda – Derecha Derecha . Izquierda Izquierda – Derecha Izquierda – Derecha Izquierda – Derecha Izquierda – Derecha Izquierda – Derecha Izquierda – Derecha Izquierda – Derecha Izquierda – Derecha Izquierda – Derecha Izquierda – Derecha Derecha – Izquierda Izquierda – Derecha

Tabla 1. 7 Prioridad y Asociatividad de operadores de C++ Ejemplos: a).- 8/4*6 2*6 12 b).- 3/4*6 0*6 0 c).- (float) 2/4 2.0/4 0.5 d).- -3+4% 5/2 -3+4/2 -3+2 -1

8*6/4 48/4 12 3*6/4 18/4 4

28/(3*4) 28/12 2

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++ %f

Punto flotante)

%h

Entero corto

%o

Octal sin signo

%s

Cadena de caracteres

%x

Hexadecimal sin signo

19

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 \a \b \f \n \r \t \v \\

Acción realizada Alarma Retroceso Avance de pagina Nueva Linea Retorno de carro Tabulación (horizontal) 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 #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 getchar() getche() getch() putchar() gets() puts()

Operación Lee un carácter del teclado; espera un salto de carro. Lee un carácter con eco; no espera un salto de carro. Lee un carácter sin eco; no espera un salto de carro. Escribe un carácter en pantalla. Lee una cadena del teclado. 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 #include

//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<
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 */ #include #include

/*Archivo2.cpp proyecto:exten.prj */ #include #include

float Prom;

extern float Prom; //Ojo

void Promedio();

void Promedio() {int C1,C2,C3; cout<<"\nESCRIBA LAS 3 CALIFICACIONES DEL ALUMNO"; cout<<"\n\nI UNIDAD: "; cin>>C1; cout<<"\nII UNIDAD: "; cin>>C2; cout<<"\nIII UNIDAD: "; cin>>C3; Prom=(C1+C2+C3)/3; }

void main() { clrscr(); cout<<"\nCALCULO DEL PROMEDIO DE CALIFCACIONES"; Promedio(); cout<<"\n\nEl Promedio es: "<
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 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 + 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 #include 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 cont=cont+10; cout<<"\t"<
static

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 #include

static int cont; //ojo

void Inicia_Semilla(int i); int serie(); void main() {int x; clrscr(); Inicia_Semilla(0); for (x=0;x<5;x++) cout<<"\t"<<serie(); getch(); }

void Inicia_Semilla(int i) { cont=i; } int serie() { cont=cont+10; return(cont); }

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 <
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 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 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 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 # include # 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: "<
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 # include 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 : "<
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 Proyecto: Venta.prj */

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

#include #include

#include #include

float Vta;

extern float Vta; externa

void func1(); void main() { clrscr(); func1(); cout<<"\n EL TOTAL DE VENTAS ES:$ "<
//Variable

void func1() { float a,b,c; cout<<"INTRODUSCA LOS PRECIOS DE LOS 3 PRODUCTOS:"; cout<<"\n\nPRIMER PRODUCTO: "; cin>>a; 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 proyecto: Banco.prj */

/* Banco2.cpp Proyecto: Banco.prj */

# include # include

# include extern float Inversion, Interes;

float Inversion,Interes,Deuda; void Calcula(); void Calcula2();

void Calcula() { float Taza; int N; cout<<"\nINVERSION :"; cin>>Inversion; cout<<"\nTAZA DE INTERES : "; cin>>Taza; cout<<"\nCUANTOS AÑOS: "; cin>>N; Interes=Inversion*Taza*N; }

void main() { clrscr(); cout<<"CALCULO DE INTERESES\n\n "; Calcula(); Calcula2(); cout<<"\nLOS INTERESES SON : "<
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. Area= 4 Π r2 Volumen = 4/3 Π r3 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.

33

Programación C y C++

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 #include 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 #include 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 #include 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: "<
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 #include void main() {clrscr(); int x, Ini=0, Inc=5, Fin=20; //Probar con otros valores for (x=Ini;x
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 1 2 3 4 5

x 0 5 10 15 20

condicion 0<20 5<20 10<20 15<20 20<20

Acción Imprime 0 Imprime 5 Imprime 10 Imprime 15 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 #include 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 6

2 4 6 8 10 12

3 6 9 12 15 18

4 8 12 16 20 24

5 10 15 20 25 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 #include void main() {int ini=50,fin=0,inc=10; int x=ini; //Inicialización while (x>fin) { cout<<x<<"\t"; x=x-inc; } } La salida sería: 50 40 30 20



//Decremento

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 #include 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 #include 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 #include 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 #include 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 #include #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 "<=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 #include 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: "<
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 #include 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: "<
Programación C y C++ 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 #include 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
47

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.

49

Programación C y C++

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 #include #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
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 #include #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;xMayor) 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 #include #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
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 strcpy(c1,c2) strcat(c1,2) strlen(c1,c2) strcmp(c1,c2)

Función

Copia c2 en c1 Concatena c2 al final de c1 Devuelve la longitud de la cadena Devuelve 0 si c1 y c2 son iguales; menor que 0 si c1c2 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 #include void main() { clrscr(); int L,x; char Cad[]="HOLA AMIGO"; L=strlen(Cad); for (x=L-1;x>=0;x--) cout<
//Esta es la cadena //Longitud de la cadena

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 #include #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: "<
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++

0 1 2 3 4

0 0 20 40 60 80

1 5 25 45 65 85

2 10 30 50 70 90

55

3 15 35 55 75 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
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 #include #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>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
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.

*P

Car

Dirección de memoria 2C00 2C01 2C02 2C03

Variable en memoria 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 #include void main() { clrscr(); int x; int *P; for (x=0;x<5;x++) { cout<
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 #include #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(PTope-N); } } while (Val!=0); getch(); } La salida Sería: Agregue elementos en la pila, 0 para terminar: 80

60

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

Programación C y C++

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 #include 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 #include #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 #include 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 #include 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 #include #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 "<>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=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
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 #include #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>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>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
}

}

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 #include #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 "<>*Num; //Asigna el numero Num++; //Incrementa el apuntador } Num=Num-N; //Apunta al primer elemento for (x=1;x=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
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 #include #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 "<>Cal[x]; if (Cal[x]>=70) NApr++; else NRep++; } IApr=(NApr/N)*100; IRep=(NRep/N)*100; cout<<"\nNo. de Aprobados "<
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

69

Programación C y C++

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 #include void Intercambio(int *A, int *B); void main() { int x=10,y=50; Intercambio(&x, &y); cout<<x<<" "<
//Llamada por referencia //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 #include #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; void main() { float SalDia[N], SalSem[N];

//Variable global

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 "<>SD[x]; } } void Calcular(float SD[],float SS[]) //Como arreglo Indet. { int x; for (x=0;x
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 #include 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"<
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 rel="nofollow"> #include #include #define N 5 struct Alumnos{ char N_Ctrl[9];

//definición de la estructura

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>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 #include 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) { cout<<"\n"<
//Parámetro por valor

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 #include struct Producto{ char Prod[40];

79

Programación C y C++ float Pr_unid; int Cant; }; void Leer(Producto *Pr); void Visualizar(Producto Pr); void main() { clrscr(); Producto P; Leer(&P); Visualizar(P); getch(); }

//Definicón de la variable de

estructura

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) { cout<<"\n"<
//Parámetro por valor

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]; Altas(A);

//Arreglo de estructura //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 #include #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++

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>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();

81

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 "<>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 "<
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 #include 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 #include #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]; void void void void

//Variable global.-

Altas(); Grado(); Especialidad(); Inicializa();

Arreglo de estructura

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>reg; } while (reg>N || reg<0); if (reg!=0) { reg--; if (E[reg].Edad==0) { cout<<"\nNombre: "; gets(E[reg].Nom); cout<<"Edad: ";

0 para terminar: ";

Programación C y C++ cin>>E[reg].Edad; cout<<"Especialidad:(1 a "<>E[reg].Esp; cout<<"Grado:(1 a "<>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 "<>gr; } while (gr>G || gr<0); cout<<"\nLOS ALUMNOS DEL "<>es; } while (es>C || es<0); cout<<"\nLOS ALUMNOS DE LA ESPECIALIDAD "<<es<<" SON:"; cout<<"\nNombre Edad Grado"; for (x=0;x
getch();

87

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.

89

Programación C y C++

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 #include 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: "<
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 #include 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; f1(&P,20); cout<
//Definición del objeto //Llamada a la función amiga

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 #include 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<
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 #include class Producto{ char *Nom; int Cantidad; float Precio; public: Producto(); Producto(char *N,int C,int P); void Ver(); };

//Primer Constructor //Segundo constructor

99

Programación C y C++ //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"<
//2 objetos

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 #include 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<
//Visualiza 3

Modelo Ob4,Ob5; cout<<"\t"<
2.- Programa para dibujar rectángulos en modo texto. /*Rectang.cpp */ #include #include 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
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 #include class Cajero{ float Sal; public: Cajero(){Sal=2500;} void Deposito(); void Saldo(); void Retiro(); };

//Constructor

//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++ {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'); }

103

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 #include 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: "<
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 #include 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:"<
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 Circulo

Clase base 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

109

Programación C y C++

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{ private: int x,y; public: ... };

//Clase Base

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 Los tipos de acceso definidos en las clase base son los :public clase base mismos en clase derivada Todos los datos y funciones miembros de la clase base :private 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 #include class A{ public: int a1; };

//Clase Base //Cambiar a private y compilar

class B:public A{ //Clase derivada int b1; public: void Asignar(int a,int b){a1=a;b1=b;} void Ver(){cout<
//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

111

Programación C y C++

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 #include class B{ public: void Funcion(){cout<<"\nFunción B";} }; class D:public B{ public: void Funcion(){cout<<"\nFunción D";} void F2(); }; void D::F2() { Funcion(); B::Funcion(); } void main() { clrscr(); D Obj; Obj.F2(); }

//1a. función

//2a. función

//Llama a la segunda función //Llama a la primera función

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 #include 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<
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

113

Programación C y C++

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 class A{ }; class B{ }; class C: public A, public B{ };

A

// Herencia Multiple

B

C

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 #include 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<
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

D

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 #include 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.

115

Programación C y C++

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 #include 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{ protected: int a1,a2; }; class B{ protected: int b1; }; class C{ protected: int c1; }; class D: virtual public A { protected: int d1; }; b).- Atributos de los Objetos

A T R I B U T O S

A a1 a2

B b1

class E: virtual public A, public B{ Protected: Int e1; }; class F: public C{ Protected: Int f1,f2; }; class G: public D, public E, public F{ Int e1; };

C c1

OBJETOS D a1 a2 d1

E a1 a2 b1 e1

F c1 f1 f2

G a1 a2 b1 d1 e1 c1 f1 f2 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 #include 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 #include #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: "<
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:

Datos

Funciones

Alumno Nombre Dirección No. de Control Especialidad Alumnos() void Ver()

OBJETOS Maestro Nombre Dirección Plaza Departamento Maestro() 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 NCtrl, Esp; void Ver();

Maestro Plaza, Depto; void Ver();

El listado del programa es: /*Abstrac.cpp */ #include <string.h rel="nofollow"> #include #include 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"<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 #include 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
Programación C y C++

}

125

gotoxy(x,y);cout<
} 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 RecRell Rell; //Objeto R.Asignar(10,8,30,16); R.Dibujar(); //Dibuja Rell.Dibujar(); //Dibuja Rell.Asignar(50,8,70,16,177); Rell.Dibujar(); }

de la clase Rect de la clase RecRell un rectángulo un rectángulo rellenado

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 Nombre Nombre Datos Dirección Dirección Salario/día Honorario/hora Años de antigüedad Leer() Leer() Funciones Visualizar() Visualizar() PagarSalario() PagarSalario() ♣ Árbol de herencia.

TBase SalDía, Ant; Leer(); Visualizar(); PagarSalario();

Trabajador Nom, Dir, Leer(); Visualizar();

TEventual HonoHra; Leer(); Visualizar(); PagarSalario();

126

Programación C y C++

♣ Definición e implementación de las clases /*Trabaj.cpp */ #include #include <stdio.h> #include //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"<>SalDia;

127

Programación C y C++ cout<<"AÑOS DE SERVICIO: "; cin>>Ant; } void TBase::Visualizar() { cout<<"\nTRABAJADOR DE BASE"; Trabajador::Visualizar(); cout<<"\n"<<SalDia; cout<<"\n"<
//Más el 2% por año

//Implementación de funciones miembros de la clase TEventual void TEventual::Leer() { cout<<"\nDATOS DEL TRABAJADOR EVENTUAL"; Trabajador::Leer(); cout<<"SALARIO POR HORA: "; cin rel="nofollow">>HonoHra; } void TEventual::Visualizar() { cout<<"\nTRABAJADOR EVENTUAL"; Trabajador::Visualizar(); cout<<"\n"<
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: ♣ Árbol de herencia:

Base b1

Der1 a1

A a2

Der2 d1

B b2

D d2

C c2

♣ Definición de clases class Base{ private: int b1; }; class Der1:public virtual Base{ private: int a1; }; class Der2: public Base{ private: int d1; }; class A:public Base,public Der1{ int a2; };

class B: public Der1,public Der2{ private: int b2; }; class C: public B{ int c2; }; class D: public Der2{ int d2; };

129

Programación C y C++

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 Atributos

A a1 d1 b1 a2

B a1 c1 b1 b2

C a1 c1 c2

D a1 d1 d2

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. |

Related Documents

Programacion C
February 2021 1
Programacion En C++
February 2021 1
Programacion
February 2021 1
Programacion Sql
March 2021 0

More Documents from "Harold Jose Arguello Morales"

Programacion C
February 2021 1