Вы находитесь на странице: 1из 61

METODOS DE PROGRAMACION MODULO II

Contenido

TEMA 1. INTRODUCCIÓN A LA PROGRAMACIÓN

4

1.1 Algunas definiciones y conceptos importantes:

4

1.2 Historia de la Programación

5

1.3 Tipos de lenguajes de programación

8

1.4 Paradigmas de la programación

9

1.4.1 Programación Imperativa

9

1.4.2 Programación Orientada por Objeto

10

1.5

Programación Estructurada

11

1.5.1

Definición de las estructuras básicas de control lógico

11

1.6 Lenguajes de Programacion Pascal y C++

13

1.7 Diagramas de Flujo

13

1.7.1 Símbolos y operadores de los diagramas de flujo

14

1.7.2 Reglas para la construcción de un diagrama de flujo

16

1.8 Uso de pseudocódigo

16

1.9 Compilador Dev C++

18

1.10 El primer programa “Hola Mundo” en C++

20

1.11 Ejercicios

22

TEMA 2. ESTRUCTURAS DE

25

2.1

Secuencia, Condición y Ciclos de Repetición

25

2.1.1 Secuencia

25

2.1.2 Bifurcación Condicional

25

2.1.3 Ciclos De Repetición

27

2.2

Tipos de Datos y Variables

30

2.2.1 Tipos de datos

30

2.2.2 Variables

31

2.3

Uso de comentarios en programación

32

TEMA 3. PROGRAMACIÓN MODULAR (FUNCIONES Y PROCEDIMIENTOS)

33

3.1 Introducción

33

3.2 Funciones

33

3.4

Paso de parámetros por referencia

36

TEMA 4. VECTORES Y MATRICES

38

4.1 Vectores

38

4.2 Matrices

40

4.3 Estructuras

43

TEMA 5. PUNTEROS Y MEMORIAS DINAMICAS

45

5.1 Introducción

45

5.2 Aritmética de punteros

46

5.3 Comparación de punteros

47

5.4 Vectores Dinámicos

48

TEMA 6. PROGRAMACION ORIENTADA A OBJETOS

49

6.1 Que es la programación orientada a objetos?

49

6.2 Definición de clase

50

6.3 Definición de objeto

52

6.4 Herencia

54

6.5 Polimorfismo

56

6.6 Sobrecarga

57

6.7 Declaración de clases en C++

57

6.7.1.

Especificadores de acceso

58

6.7.2

Operador de resolución de ambito

60

6.7.3

El apuntador this

61

TEMA 1. INTRODUCCIÓN A LA PROGRAMACIÓN

1.1 Algunas definiciones y conceptos importantes:

¿Qué es un Algoritmo?

En matemáticas, ciencias de la computación y disciplinas relacionadas, un algoritmo es un conjunto preescrito de instrucciones o reglas bien definidas, ordenadas y finitas que permite realizar una actividad mediante pasos sucesivos que no generen dudas a quien deba realizar dicha actividad.

Dados un estado inicial y una entrada, siguiendo los pasos sucesivos se llega a un estado final y se obtiene una solución.

En la vida cotidiana, se emplean algoritmos frecuentemente para resolver problemas. Algunos ejemplos son los manuales de usuario, que muestran algoritmos para usar un aparato, o las instrucciones que recibe un trabajador por parte de su patrón. Algunos ejemplos en matemática son el algoritmo de la división para calcular el cociente de dos números, el algoritmo de Euclides para obtener el máximo común divisor de dos enteros positivos, o el método de Gauss para resolver un sistema lineal de ecuaciones.

Muchos autores definen a los algoritmos como listas de instrucciones para resolver un problema abstracto, es decir, que un número finito de pasos convierten los datos de un problema (entrada) en una solución (salida).

¿Qué es un Diagrama de Flujo?

Los diagramas de flujo son descripciones gráficas de algoritmos; usan símbolos conectados con flechas para indicar la secuencia de instrucciones y están regidos por ISO.

Los diagramas de flujo son usados para representar algoritmos pequeños, ya que abarcan mucho espacio y su construcción es laboriosa. Por su facilidad de lectura son usados como introducción a los algoritmos, descripción de un lenguaje y descripción de procesos a personas ajenas a la computación.

¿Qué es Programar? Es la acción de escribir instrucciones correctas para que sean interpretadas por una máquina.

¿Qué es el Software? Son programas que para que tengan sentido de software deben ser ejecutados sobre una máquina.

¿En qué medida nos compete programar?

Depende del interés. De todas formas hoy en día es un “must” para cualquier ingeniero o técnico debe de conocer.

¿En qué medida dependemos de software? Depende de su estilo de vida, pero para el ciudadano común la dependencia es bastante:

celulares, controles remotos (TV, DVD, radios, mp3 players, etc.), cajeros automáticos, etc.

¿Qué es un programa o código fuente? Es un programa escrito en algún lenguaje y que no ha sido traducido a lenguaje de máquina.

¿Qué es un programa o código objeto?

Es un programa que ya se encuentra en lenguaje de máquina y que ya es ejecutable.

¿Qué es un compilador? Es aquel programa de software que traduce todo el código fuente escrito y genera un código objeto que es entendible por la máquina y se encuentra listo para funcionar

¿Qué es un Lenguaje de Programación? Son conjuntos de instrucciones con que se pueden escribir los algoritmos para que un sistema lo ejecute. Existen múltiples tipos de lenguajes de programación:

1.2 Historia de la Programación

Los ordenadores son uno más de los inventos del hombre, aunque debemos decir que las tecnologías para su fabricación y explotación han tenido un desarrollo sorprendente a partir de la segunda mitad del siglo XX. Esta herramienta por sí sola no es capaz de efectuar ninguna tarea, es tan sólo un conjunto de cables y circuitos que necesitan recibir instrucción por parte de los humanos para desempeñar alguna tarea. El problema entonces, se puede fijar en ¿cómo vamos a poder hacer que un conjunto de circuitos desempeñen una determinada tarea y nos entreguen los resultados que nosotros esperamos?, es decir, ¿de qué manera se puede lograr la comunicación entre el hombre y el ordenador?

Así pues, tratando de dar una solución al problema planteado, surgieron los lenguajes de programación, que son como un lenguaje cualquiera, pero simplificado y con ciertas normas, para poder trasmitir nuestros deseos al ordenador.

Por otro lado, como se sabe, un conjunto de circuitos no entendería ningún lenguaje que nosotros conozcamos, por más sencillo que éste parezca. Los circuitos en todo caso, sólo reconocen presencia o ausencia de energía, es decir que debemos hablarle a la máquina en su propio lenguaje (presencia y ausencia de energía, 0 y 1), o nuestro lenguaje deberá de ser traducido a un lenguaje binario cuyo alfabeto es el 0 y el 1, mediante las herramientas desarrolladas para llevar a cabo esta tarea, las cuales reciben el nombre de traductores, y como veremos más adelante, los hay de muchos tipos, dependiendo de características más específicas del lenguaje a traducir y de la manera de llevar a cabo su traducción.

1946: Konrad Zuse , un ingeniero Alemán mientras trabajaba en los Alpes de Bavaria, desarrolló el lenguaje Plankalkul, el cual, fue aplicado entre otras cosas para jugar al ajedrez.

1949: Aparece Short Code, que viene a ser el primer lenguaje que fue usado en un dispositivo de cómputo electrónico, aunque se debe decir que se trata de un lenguaje traducido a mano.

1951: Grace Hopper , trabajando para Remington Rand, comenzó el trabajo de diseño del primer compilador conocido ampliamente, el A-0, el cual, al ser liberado por la compañía en 1957, lo hizo con el nombre de MATH-MATIC.

1952: Alick E. Glennie, durante su tiempo libre en la Universidad de Manchester, concibe un sistema de programación llamado AUTOCODE, que viene a ser un compilador muy rudimentario.

1957: aparece FORTRAN (FORmula TRANslating) sistema traductor de fórmulas matemáticas. Fue desarrollado por un equipo, al frente del cual se encontraba John Backus quien después vendría a contribuir en el desarrollo del compilador para el lenguaje ALGOL y de la notación usada para la especificación sintáctica de los lenguajes, conocida como BNF (Backus Naur Form).

A partir de los años sesenta, empiezan a surgir diferentes lenguajes de programación, atendiendo

a diversos enfoques, características y propósitos, que más adelante describiremos. Por lo pronto, puede decirse, que actualmente existen alrededor de más 2000 lenguajes de programación y

continuamente, están apareciendo otros más nuevos, que prometen hacer mejor uso de los recursos computacionales y facilitar el trabajo de los programadores.

Tratando de resumir un poco, se presenta a continuación un cuadro donde aparecen los lenguajes que por su uso y comercialización, han resultado ser los más populares a lo largo de este medio siglo.

1.3

Tipos de lenguajes de programación

Los lenguajes más próximos a la arquitectura hardware se denominan lenguajes de bajo nivel y los que se encuentran más cercanos a los programadores y usuarios se denominan lenguajes de alto nivel. A continuación hacemos una breve descripción de ambos tipos.

Lenguajes de Bajo Nivel Son lenguajes totalmente dependientes de la máquina, es decir que el programa que se realiza con este tipo de lenguajes no se pueden migrar o utilizar en otras maquinas.

Al estar prácticamente diseñados a medida del hardware, aprovechan al máximo las características del mismo.

Dentro de este grupo se encuentran:

El lenguaje maquina: este lenguaje ordena a la máquina las operaciones fundamentales para su funcionamiento. Consiste en la combinación de 0's y 1's para formar las ordenes entendibles por el hardware de la máquina. Este lenguaje es mucho más rápido que los lenguajes de alto nivel.

La desventaja es que son bastantes difíciles de manejar y usar, además de tener códigos fuente enormes donde encontrar un fallo es casi imposible.

El lenguaje ensamblador es un derivado del lenguaje máquina y está formado por abreviaturas de letras y números llamadas mnemotécnicos. Con la aparición de este lenguaje se crearon los programas traductores para poder pasar los programas escritos en lenguaje ensamblador a lenguaje máquina. Como ventaja con respecto al código máquina es que los códigos fuentes eran más cortos y los programas creados ocupaban menos memoria. Las desventajas de este lenguaje siguen siendo prácticamente las mismas que las del lenguaje de maquina añadiendo la dificultad de tener que aprender un nuevo lenguaje difícil de probar y mantener.

Lenguajes de Alto Nivel Son aquellos que se encuentran más cercanos al lenguaje natural que al lenguaje máquina. Están dirigidos a solucionar problemas mediante el uso de Estructura Dinámicas de Datos, algo muy utilizado en todos los lenguajes de programación. Son estructuras que pueden cambiar de tamaño durante la ejecución del programa. Nos permiten crear estructuras de datos que se adapten a las necesidades reales de un programa.

Se tratan de lenguajes independientes de la arquitectura del ordenador. Por lo que, en principio, un programa escrito en un lenguaje de alto nivel, lo puedes migrar de una máquina a otra sin ningún tipo de problema.

Estos lenguajes permiten al programador olvidarse por completo del funcionamiento interno de la maquina/s para la que están diseñando el programa. Tan solo necesitan un traductor que entiendan el código fuente como las características de la máquina. Algunos de ellos son: Basic, C, C++, Pascal, Cobol, Fortran, etc.

1.4

Paradigmas de la programación

Un paradigma de programación es una propuesta tecnológica que es adoptada por una comunidad de programadores cuyo núcleo central es incuestionable en cuanto a que unívocamente trata de resolver uno o varios problemas claramente delimitados. La resolución de estos problemas debe suponer consecuentemente un avance significativo en al menos un parámetro que afecte a la ingeniería de software. Tiene una estrecha relación con la formalización de determinados lenguajes en su momento de definición. Un paradigma de programación está delimitado en el tiempo en cuanto a aceptación y uso ya que nuevos paradigmas aportan nuevas o mejores soluciones que la sustituyen parcial o totalmente.

Si bien existen varios paradigmas de programación haremos una descripción de aquellos que consideramos más importantes como son la programación imperativa y la orientada a objetos.

1.4.1 Programación Imperativa

El paradigma imperativo debe su nombre al papel dominante que desempeñan las sentencias imperativas, es decir aquellas que indican realizar una determinada operación que modifica los datos guardados en memoria. Su esencia es resolver un problema complejo mediante la ejecución repetitiva y paso a paso de operaciones y cálculos sencillos con la asignación de los valores calculados a posiciones de memoria.

La programación en este paradigma consiste en determinar qué datos son requeridos para el cálculo, asociar a esos datos una dirección de memoria, y efectuar, paso a paso, una secuencia de transformaciones en los datos almacenados, de forma que el estado final represente el resultado correcto.

Los conceptos básicos del paradigma son representados por la arquitectura Von Neumann, ya que utiliza este modelo de máquina para conceptualizar las soluciones.

Principales características

Celdas de memoria

El principal componente de la arquitectura es la memoria, compuesta por un gran número de celdas que contienen los datos. Las celdas, llamadas variables, tienen nombres que las referencian y se organizan en diferentes estructuras de datos.

Asignación

Estrechamente ligado a la arquitectura de memoria se encuentra la idea de que cada valor calculado debe ser almacenado, o sea, asignado, en una variable. Estas asignaciones se realizan repetitivamente sobre la misma celda de memoria, remplazando los valores anteriores. La asignación determina el estado de una variable, que consiste en el valor que

contiene en un momento en particular. Por lo tanto los resultados corresponden al estado final que asumen las variables.

Algoritmos

Un programa imperativo, normalmente realiza su tarea ejecutando repetidamente una secuencia de pasos elementales, ya que en este modelo computacional la única forma de ejecutar algo complejo es repitiendo una secuencia de instrucciones. La programación requiere la construcción de “algoritmos”, a modo de receta, método, técnica, procedimiento o rutina, que se definen como conjuntos finito de sentencias, ordenadas de acuerdo a sus correspondientes estructuras de control, que marcan el flujo de ejecución de operaciones para resolver un problema específico.

Programación estructurada y estructuras de control

La programación estructurada surge como un conjunto de técnicas para facilitar el desarrollo de sistemas en lenguajes del paradigma imperativo, pero presenta ideas que también fueron tenidas en cuenta en lenguajes de otros paradigmas.

La base teórica de la programación estructurada plantea que cualquier programa, por más grande y complejo que fuera, puede representarse mediante tres tipos de estructuras de control: secuencia, selección e iteración.

Modularización

También propone desarrollar el programa en forma TOP-DOWN, de arriba hacia abajo. Es decir, modularizar el programa creando porciones más pequeñas de programas con tareas específicas, que se subdividen a su vez en otros “subprogramas”, cada vez más pequeños y operativos. Estos subprogramas, denominados rutinas, funciones, procedimientos, módulos y de otras maneras en los diferentes lenguajes, deben tener sólo un punto de entrada y uno de salida, y realizar conceptualmente una sola tarea.

Existen numerosos lenguajes del paradigma imperativo, como son PASCAL, C, C++, FORTRAN, ALGOL, COBOL, ADA, CLIPPER, FOX, PL/1, etc. Muchos de ellos incluyen extensiones que permiten soportar otros paradigmas, en mayor o menor grado; no obstante, sigue predominando en ellos el modelo de máquina imperativa.

1.4.2 Programación Orientada por Objeto

La programación Orientada a objetos (POO) es una forma especial de programar, más cercana a como expresaríamos las cosas en la vida real que otros tipos de programación.

La POO no es difícil, pero es una manera especial de pensar, a veces subjetiva de quien la programa, de manera que la forma de hacer las cosas puede ser diferente según el programador.

Aunque podamos hacer los programas de formas distintas, no todas ellas son correctas, lo difícil no es programar orientado a objetos sino programar bien. Programar bien es importante porque así nos podemos aprovechar de todas las ventajas de la POO.

Actualmente el paradigma de programación más usado es el de la programación orientada a objetos, donde podemos mencionar los siguientes lenguajes: C++, Objective C, Java, Smalltalk, Eiffel, Ruby, Python, Object Pascal, Visual .net, Actionscript, Perl, C#, PHP, Delphi, etc.

Otros paradigmas. Programación Funcional (Tarea) Programación por Eventos (Tarea) Programación Concurrente (Tarea) Programación en la Nube (Tarea)

1.5 Programación Estructurada

La programación estructurada es un estilo de programación con el cual el programador elabora programas, cuya estructura es la más clara posible, mediante el uso de tres estructuras básicas de control lógico, a saber: SECUENCIA, SELECCIÓN, ITERACIÓN

Un programa estructurado se compone de funciones, segmentos, módulos y/o subrutinas, cada una con una sola entrada y una sola salida. Cada uno de estos módulos, se denomina programa apropiado cuando, además de estar compuesto sólamente por las tres estructuras básicas, tiene sólo una entrada y una salida y en ejecución no tiene partes por las cuales nunca pasa ni tiene ciclos infinitos.

La programación estructurada tiene un teorema estructural o teorema fundamental, el cual afirma que cualquier programa, no importa el tipo de trabajo que ejecute, puede ser elaborado utilizando únicamente las tres estructuras básicas (secuencia, selección, iteración).

1.5.1 Definición de las estructuras básicas de control lógico

Secuencia Indica que las instrucciones de un programa se ejecutan una después de la otra, en el mismo orden en el cual aparecen en el programa. Se representa gráficamente como una caja después de otra, ambas con una sola entrada y una única salida.

A
A
B
B

Las cajas A y B pueden ser definidas para ejecutar desde una simple instrucción hasta un módulo o programa completo, siempre y cuando que estos también sean programas apropiados.

Selección También conocida como la estructura SI-CIERTO-FALSO, plantea la selección entre dos alternativas con base en el resultado de la evaluación de una condición o predicado; equivale a la instrucción IF de todos los lenguajes de programación y se representa gráficamente de la siguiente manera:

F C B
F
C
B

V

A
A
representa gráficamente de la siguiente manera: F C B V A En el diagrama de flujo

En el diagrama de flujo anterior, C es una condición que se evalúa; A es la acción que se ejecuta cuando la evaluación de este predicado resulta verdadera y B es la acción ejecutada cuando indica falso. La estructura también tiene una sola entrada y una sola salida; y las funciones A y B también pueden ser cualquier estructura básica o conjunto de estructuras.

Iteración También llamada la estructura HACER-MIENTRAS-QUE, corresponde a la ejecución repetida de una instrucción mientras que se cumple una determinada condición. El diagrama de flujo para esta estructura es el siguiente:

A V C F
A
V
C
F

Aquí el bloque A se ejecuta repetidamente mientras que la condición C se cumpla o sea cierta. También tiene una sola entrada y una sola salida; igualmente A puede ser cualquier estructura básica o conjunto de estructuras.

1.6 Lenguajes de Programacion Pascal y C++

¿Qué es Pascal?

Pascal es un lenguaje de programación de ordenadores, de propósito general. Es muy estructurado, y bastante adecuado para introducirse en la programación de ordenadores.

Turbo Pascal ha sido durante mucho tiempo la versión más extendida del lenguaje Pascal. Fue desarrollada por la compañía Borland, para el sistema operativo Dos. Este puesto actualmente lo ocupa una versión de libre distribución (y código fuente abierto) llamada Free Pascal, disponible para varios sistemas operativos.

¿Qué es Pascal C/C++?

C es un lenguaje de programación de ordenadores, de propósito general. Su uso está muy extendido, y existen compiladores para muchos sistemas operativos.

C++ (C plus plus o C más más) es una evolución del lenguaje C, que soporta la Programación Orientada a Objetos.

1.7 Diagramas de Flujo

Un Diagrama de Flujo representa la esquematización gráfica de un algoritmo, el cual muestra gráficamente los pasos o procesos a seguir para alcanzar la solución de un problema. Su correcta construcción es sumamente importante porque, a partir del mismo se escribe un programa en algún Lenguaje de Programación. Si el Diagrama de Flujo está completo y correcto, el paso del mismo a un Lenguaje de Programación es relativamente simple y directo.

La creación del diagrama de flujo es una actividad que agrega valor, pues el proceso que representa está ahora disponible para ser analizado, no sólo por quienes lo llevan a cabo, sino también por todas las partes interesadas que aportarán nuevas ideas para cambiarlo y mejorarlo.

Ventajas de los Diagramas de Flujo:

Favorecen la comprensión del proceso a través de mostrarlo como un dibujo. El cerebro humano reconoce fácilmente los dibujos. Un buen diagrama de flujo reemplaza varias páginas de texto.

Permiten identificar los problemas y las oportunidades de mejora del proceso. Se identifican los pasos redundantes, los flujos de los reprocesos , los conflictos de autoridad, las responsabilidades, los cuellos de botella, y los puntos de decisión.

Muestran las interfases cliente-proveedor y las transacciones que en ellas se realizan, facilitando a los empleados el análisis de las mismas.

Son una excelente herramienta para capacitar a los nuevos empleados y también a los que desarrollan la tarea, cuando se realizan mejoras en el proceso

Determinar las necesidades de una aplicación. División de la aplicación en tareas. Utilización de organigramas para representar algoritmos. Utilización de pseudocódigo para la representación de algoritmos. Utilización de pseudocódigo para especificar algoritmos.

1.7.1 Símbolos y operadores de los diagramas de flujo

Los Diagramas de flujo se dibujan generalmente usando algunos símbolos estándares; sin embargo, algunos símbolos especiales pueden también ser desarrollados cuando séan requeridos. Los símbolos tienen significados específicos y se conectan por medio de flechas que indican el flujo entre los distintos pasos o etapas.

Algunos símbolos estándares, que se requieren con frecuencia para diagramar programas de computadora se muestran a continuación:

Inicio o Fin del Programa

Inicio o Fin del Programa

Pasos, procesos o líneas de instrucción de programa de computo

Pasos, procesos o líneas de instrucción de programa de computo

Operaciones de entra y salida

Operaciones de entra y salida

Toma de decisiones

Toma de decisiones

Conector para unir flujo a otra parte del diagrama.

Conector para unir flujo a otra parte del diagrama.

Líneas de flujo

Líneas de flujo

Disco magnético o base de datos

Disco magnético o base de datos

Envía datos a la impresora.

Envía datos a la impresora.

Los operadores son utilizados específicamente para para operaciones aritméticas y relaciones condicionales. La siguiente es una lista de los símbolos más comúnmente utilizados:

+

Sumar

-

Menos

*

Multiplicación

/

División

±

Más o menos

=

Equivalente a

>

Mayor que

<

Menor que

>=

Mayor o igual que

<=

Menor o igual que

<>

Diferente de

 

Si

 

No

 

True

False

False

1.7.2 Reglas para la construcción de un diagrama de flujo

Las reglas para la creación de diagramas de flujo son:

1. Los Diagramas de flujo deben escribirse de arriba hacia abajo, y/o de izquierda a derecha.

2. Los símbolos se unen con líneas, las cuales tienen en la punta una flecha que indica la dirección que fluye la información procesos, se deben de utilizar solamente líneas de flujo horizontal o verticales (nunca diagonales).

3. Se debe evitar el cruce de líneas, para lo cual se quisiera separar el flujo del diagrama a un sitio distinto, se pudiera realizar utilizando los conectores. Se debe tener en cuenta que solo se van a utilizar conectores cuando sea estrictamente necesario.

4. No deben quedar líneas de flujo sin conectar

5. Todo texto escrito dentro de un símbolo debe ser legible, preciso, evitando el uso de muchas palabras.

6. Todos los símbolos pueden tener más de una línea de entrada, a excepción del símbolo final.

7. Solo los símbolos de decisión pueden y deben tener más de una línea de flujo de salida.

Ejemplo: Sumar dos números:

una línea de flujo de salida. Ejemplo: Sumar dos números: 1.8 Uso de pseudocódigo El principal

1.8 Uso de pseudocódigo

El principal objetivo del pseudocódigo es el de representar la solución a un algoritmo de la forma más detallada posible, y a su vez lo más parecida posible al lenguaje que posteriormente se utilizara para la codificación del mismo.

Las principales características de este lenguaje son:

Se puede ejecutar en un ordenador

Es una forma de representación sencilla de utilizar y de manipular.

Facilita el paso del programa al lenguaje de programación.

Es independiente del lenguaje de programación que se vaya a utilizar.

Es un método que facilita la programación y solución al algoritmo del programa.

Todo documento en pseudocódigo debe permitir la descripción de:

Instrucciones primitivas

Instrucciones de proceso

Instrucciones de control

Instrucciones compuestas

Instrucciones de descripción

La Estructura a seguir en su realización:

Cabecera:

Programa:

Modulo:

Tipos de datos:

Constantes:

Variables:

Cuerpo

Inicio

Instrucciones

Fin

Para comentar en pseudocódigo se le antepone al comentario dos asteriscos (*)

Ejemplos

* Programa que calcula el área de un cuadrado a partir de un lado dado por teclado. Programa: area_cuadrado Modulo: main **( también se puede llamar principal) Variables:

lado: natural area: natural Inicio Visualizar "Introduce el lado del cuadrado" Leer lado Area<- lado * lado Visualizar "El área del cuadrado es", area Fin

* Programa que visualice la tabla de multiplicar del numero introducido por teclado

Programa: Tabla multiplicar Modulo: main Variables:

t: entero num : entero Inicio Visualizar "Introduce un número" Leer num Desde t=1 hasta t=10 repetir Visualizar num, " X", t, "=", num*t Fin desde Fin

Una vez que tenemos preparado un diagrama de flujos (ordinograma u organigrama) y un pseudocódigo ya podemos comenzar con la codificación del programa en nuestro ordenador. A partir de aquí todo varía dependiendo del lenguaje de programación que utilicemos, pero en todos los programas tendremos que definir los tipos de datos que utilizaremos. De todo esto hablaré en el siguiente artículo.

1.9 Compilador Dev C++

El compilador del cual se va a hacer uso durante el desarrollo del curso será el Dev C++ v. 4.9.9.2. Dev C++ es totalmente gratuito que genera código objeto para DOS (modo consola) y para Windows (95/98/2000/NT) con un entorno de programación visual integrado (IDE). Está basado en el compilador Mingw (Minimalist GNU* for Windows) version MSCVRT 2.95.2-1 que está incluido en el propio entorno, el cual a su vez es una particularización del compilador GCC (el compilador g++ del GNU).

También permite integrar el depurador gratuito para C++ Cygnus Insight Debugger.

Alguna de las principales características del entorno Dev C++ son:

Depurador integrado (GDB o Insight)

Editor de programas fuente sensible a la sintaxis C-C++ configurable.

Editor multiventana con múltiples opciones de edición.

Se puede trabajar con ficheros independientes o con proyectos multi-ficheros.

Generador de paquetes de instalación de programas para entorno Windows.

Puede generar programas DOS (modo consola), aplicaciones Windows y DLLs.

Edición de ficheros de recursos windows con generador de menús integrado

Ventanas independientes para el gestor de proyectos, editor y resultados de compilación.

Resultados de compilación, enlace (linker) y generación de recursos.

Genera automáticamente esqueletos de programas en C y C++.

Generación automática de "Makefile"

Dos juegos de iconos para los menús y las barras de herramientas

Permite integrar herramientas externas mediante el "tool manager”

Plantillas para la creación de nuevos tipos de proyectos.

En el siguiente gráfico se muestra la interfaz del compilador. Las configuraciones y distribución de opciones y herramientas generalmente son muy parecidas entre estos tipos de programas. Es así por ejemplo que la barra de herramientas contiene las opciones de creación y guardado de proyectos, búsquedas y ediciones de texto. También se encuentran las opciones de compilación y ejecución del código.

Son también frecuentes las ventas de navegación que facilitan la manipulación de diferentes archivos.

El editor de texto, el cual es sensible a la sintaxis del lenguaje.

Y finalmente la barra de estado que facilita la identificación de errores.

Barra de herramientas Editor de Texto Barra de Navegación Barra de Estado
Barra de
herramientas
Editor de
Texto
Barra de
Navegación
Barra de
Estado

1.10 El primer programa “Hola Mundoen C++

Como en la mayoría de los cursos de lenguajes de programación uno de los primeros programas que generalmente se escriben es el famoso “Hola Mundo”, a través del cual se nos permite dar un vistazo a la sintaxis y estructura del programa.

Pasos a seguir:

1. En primer lugar creamos un nuevo proyecto, haciendo click en el menú “Archivo->Nuevo”, selecciones la opción “Nuevo Proyecto”.

2. A continuación se muestra la siguiente pantalla, donde se deberá seleccionar el tipo de proyecto que vamos a construir. Seleccionamos la opción “Console Application”, proporcionamos el nombre del proyecto, que nuestro caso será “Hola Mundo” y finalizamos haciendo click en el botón aceptar.

Mundo” y finalizamos haciendo click en el botón aceptar. 3. Terminamos estableciendo la ubicación donde se

3. Terminamos estableciendo la ubicación donde se guardarán todos los archivos del proyecto.

Una vez construido el proyecto, el editor de texto contiene las siguientes líneas de código.

#include <cstdlib>

#include <iostream> using namespace std int main(int argc, char *argv[])

{

system("PAUSE"); return EXIT_SUCCESS;

}

Tal como vimos en la sección de “pseudocódigo”, este programa contiene una cabecera donde se definen el uso de librerías, variables, módulos y un cuerpo que contiene las instrucciones.

El significado de cada palabra se verá más adelante, simplemente nos concentramos en el cuerpo del programa donde se establece todo lo necesario para construir nuestros programas.

/* Primer Programa Hola Mundo */ #include <cstdlib> #include <iostream>

using namespace std;

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

{

cout<<"Hola Mundo\n"; system("PAUSE"); return EXIT_SUCCESS;

}

Para compilar nuestro código ingresamos la menú->ejecutar y seleccionamos la opción Compilar y para ejecutarlo realizamos los mismos pasos solo que en este caso seleccionamos la opción Ejecutar. Es importante hacer notar que siempre es necesario compilar primero nuestro código antes de ejecutarlo puesto que esto nos permite actualizar los cambios que hayamos hecho en nuestro código.

Un acceso rápido a estas opciones es mediante la combinación de teclas CTRL + F9 para compilar y F9 para ejecutar el programa.

La salida en pantalla se muestra a continuación:

de teclas CTRL + F9 para compilar y F9 para ejecutar el programa. La salida en

1.11

Ejercicios

a) Hacer el diagrama de flujo para sumar dos números leídos por teclado y escribir el resultado.

dos números leídos por teclado y escribir el resultado. b) Modificar el anterior pero para sumar

b) Modificar el anterior pero para sumar 100 números leídos por teclado.

leídos por teclado y escribir el resultado. b) Modificar el anterior pero para sumar 100 números

c)

Hacer un diagrama de flujo que permita escribir los 100 primeros pares

de flujo que permita escribir los 100 primeros pares P: Variable para contener el siguiente par

P: Variable para contener el siguiente par que se debe escribir. I: Contador de pares que quedan por escribir. El proceso es similar al anterior. Necesitamos un bucle para contar 100 veces y dentro de él escribimos el par e incrementamos para obtener el siguiente.

d) La sucesión de Fibonacci se define de la siguiente forma: a1=1, a2=1 y an=an-1+an-2 para n>2, es decir los dos primeros son 1 y el resto cada uno es la suma de los dos anteriores,

Hacer un diagrama de flujo para calcular el

los primeros son: 1, 1, 2, 3, 5, 8, 13, 21, Nésimo término de la sucesión.

Dado N, el proceso es el siguiente: - si N£2 se escribe directamente 1 y

Dado N, el proceso es el siguiente:

- si N£2 se escribe directamente 1 y se acaba.

- en otro caso se guardan en A y B los 2 últimos (al principio 1) y se suman, pasando

después a llamar A al antiguo B, y B a la suma. Se decrementa N y cuando valga 2, en B tenemos lo que queremos.

Vamos a ver como funciona paso a paso. Para ello vamos a numerar cada uno de los pasos

y ver como se van realizando.

(1) Leemos N, supongamos N=4.

(2) ¿N£2? ® NO (3) A=1 (4) B=1 (5) C=A+B=1+1=2 (6) A=B=1 (7) B=C=2 (8) N=N-1=4-1=3 (9) ¿N=2? ® NO (5) C=A+B=1+2=3

TEMA 2. ESTRUCTURAS DE CONTROL.

Las estructuras de control determinan la secuencia en que deben ejecutarse las instrucciones de un algoritmo.

Existen tres Estructuras de control básicas ó primitivas y combinándolas se puede escribir cualquier algoritmo. Estas estructuras primitivas son: la secuencia, la bifurcación condicional y el ciclo.

2.1 Secuencia, Condición y Ciclos de Repetición

2.1.1 Secuencia

La estructura de control más simple es la secuencia. Esta estructura permite que las instrucciones que la constituyen se ejecuten una tras otra en el orden en que se listan. Por ejemplo, considérese el siguiente fragmento de un algoritmo:

considérese el siguiente fragmento de un algoritmo: En este fragmento se indica que se ejecute la

En este fragmento se indica que se ejecute la operación 1 y a continuación la operación 2.

2.1.2 Bifurcación Condicional

a) Condición Simple (if else)

La estructura de bifurcación condicional permite elegir una de dos opciones en una alternativa, dependiente del resultado obtenido al evaluar la condición. Véase el siguiente fragmento de algoritmo:

La palabra clave si indica que estamos en una sentencia de bifurcación condicional. Si la

La palabra clave si indica que estamos en una sentencia de bifurcación condicional. Si la condición es verdadera se ejecuta la operación 1, de otra manera se ejecutará la operación 2.

Cuando la condición se hace falsa el ciclo termina.

En lenguaje C++ la estructura condicional seria de la siguiente manera:

if (condicion) {

// instrucciones que hay que ejecutar si la condición es verdadera } else { // Instrucciones que hay que ejecutar si la condición es falsa

}

b) La sentencia switch

Mediante la sentencia switch se puede seleccionar entre varias sentencias según el valor de cierta expresión.

La sentencia switch evalúa la expresiónMultivalor y ejecuta el conjuntoDeSentencias que aparece junto a la cláusula case cuyo valor corresponda con el de la expresiónMultivalor.

Cada sentencia case debe ser única y el valor que evalúa debe ser del mismo tipo que el devuelto por la expresiónMultivalor de la sentencia switch.

Las sentencias break que aparecen tras cada conjunto de sSentencias provocan que el control salga del switch y continúe con la siguiente instrucción al switch. Las sentencias break son necesarias porque sin ellas se ejecutarían secuencialmente las sentencias case siguientes. Existen ciertas situaciones en las que se desea ejecutar secuencialmente algunas o todas las sentencias case, para lo que habrá que eliminar algunos break.

Finalmente, se puede usar la sentencia default para manejar los valores que no son explícitamente contemplados por alguna de las sentencias case. Su uso es altamente recomendado.

En lenguaje C++ la estructura condicional switch seria de la siguiente manera:

switch ( expresionMultivalor ) { case valor1 : conjuntoDeSentencias; break; case valor2 : conjuntoDeSentencias; break; case valor3: conjuntoDeSentencias; break; default: conjuntoDeSentencias; break;

}

Por ejemplo, supongamos un programa con una variable entera meses cuyo valor indica el mes actual, y se desea imprimir el nombre del mes en que estemos. Se puede utilizar la sentencia switch para realizar esta operación:

switch (meses){ case 1: cout<<"Enero"<<endl; break; case 2: cout<<"Febrero"<<endl; break; case 3: cout<<"Marzo"<<endl; break; case 4: cout<<"Abril"<<endl; break; case 5: cout<<"Mayo"<<endl; break; case 6: cout<<"Junio"<<endl; break; case 7: cout<<"Julio"<<endl; break; case 8: cout<<"Agosto"<<endl; break; case 9: cout<<"Septiembre"<<endl; break; case 10: cout<<"Octubre"<<endl; break; case 11: cout<<"Noviembre"<<endl; break; case 12: cout<<"Diciembre"<<endl; break; default: cout<<"No existe el mes"<<endl; break;

}

2.1.3 Ciclos De Repetición

Los ciclos son estructuras de control que permiten ejecutar varias veces una operación. Existen varios tipos de ciclos:

a) Ciclo Mientras (WHILE)

Este ciclo repite una operación, mientras se cumpla una cierta condición. Por ejemplo:

En lenguaje C++ la estructura “mientras” seria de la siguiente manera: While (condicion) { //

En lenguaje C++ la estructura “mientras” seria de la siguiente manera:

While (condicion) {

// Instrucciones a ejecutar una y otra //vez mientras la condición sea cierta

}

La palabra clave mientras, señala que se trata de un ciclo mientras. La condición se verifica antes de ejecutar la operación.

b) Ciclo Hacer-mientras (DO-WHILE)

En este ciclo la operación se ejecuta y después se evalúa la condición. Si es verdadera, la operación se evalúa de nuevo, y así sucesivamente.

se ejecuta y después se evalúa la condición. Si es verdadera, la operación se evalúa de

En lenguaje C++ la estructura “hacer mientras” seria de la siguiente manera:

do {

// Instrucciones a ejecutar una y otra //vez mientras la condición sea cierta }while(condicion)

c) Ciclo Desde (FOR)

En este ciclo se ejecuta una operación un cierto número de veces, especificando en un contador el incremento unitario, desde un Valor Inicial hasta un Valor Final que marcará la condición de salida del ciclo. Por ejemplo:

que marcará la condición de salida del ciclo. Por ejemplo: En lenguaje C++ la estructura “repetir”

En lenguaje C++ la estructura “repetir” seria de la siguiente manera:

for (valor inicial; condición; ejecutar en cada bucle) {

// Instrucciones a ejecutar mientras la condición sea cierta

}

2.2 Tipos de Datos y Variables

2.2.1 Tipos de datos

Es uno de los conceptos fundamentales de cualquier lenguaje de programación. Estos definen los métodos de almacenamiento disponibles para representar información, junto con la manera en que dicha información ha de ser interpretada.

Todos los programas gestionan algunos tipos de información que normalmente se pueden representar utilizando uno de los ocho (8) tipos de datos básicos de C y C++: texto o char, valores enteros o int, valores de coma flotante o float, valores en como flotante de doble precisión o doublé (long double), enumerados o enum, sin valor o void, punteros y booleanos.

1. Texto (tipo de dato char) está constituido por caracteres simples, como a, Z, ¿, 3 y cadenas, como “Esto es una prueba” (normalmente, de 8 bits o un byte por carácter, con un rango de 0 a 255).

2. Los valores enteros (tipo de dato int) son aquellos números que se aprendieron a contar (1, 4, -2, 1354); normalmente, tienen un tamaño de 16 bits, 2 bytes o una palabra, con rango de -32768 a 32767. En Windows 98 y Windows NT, los valores enteros tienen un tamaño de 32 bits con un rango de -2147483648 a 2147483647.

3. Los valores en coma flotante (tipo de datofloat) son números que tienen una parte fraccional, como por ejemplo pi (3,14159), y exponentes (7,5631021). También se conocen como números reales (normalmente, son de 32 bits, 4 bytes o 2 palabras, con un rango de +/-3,4E-38 a 3,4E+38).

4. Los valores en coma flotante de doble precisión (tipo de datodouble) tienen un rango superior (normalmente de 64 bits, 8 bytes ó 4 palabras, con un rango de 1, 7E-308 a 1, 7E+308). Los valores en coma flotante long double (doble precisión largos) son incluso más precisos (normalmente, tamaño de 80 bits ó 5 palabras, con un rango de +/-1,18E-4932 a

1,18E-4932).

5. Los tipos de datos enumerados (tipo de dato enum) permiten al usuario definir tipos de datos.

6. El tipo void se utiliza para especificar valores que ocupan cero bits y no tienen valor (este tipo también se puede utilizar para la creación de punteros genéricos.

7. El tipo de dato puntero no contiene información en el mismo sentido que el resto de los tipos de datos; en su lugar, cada puntero contiene la dirección de la posición de memoria que almacena el dato actual.

8. El tipo de dato bool, al que se le puede asignar las constantes true (verdadero) y false (falso)

Rango de los tipos de datos

Rango de los tipos de datos 2.2.2 Variables Es un nombre que representa el valor de

2.2.2

Variables

Es un nombre que representa el valor de un dato. Es una zona o posición de memoria en la computadora donde se almacena información. Un objeto de datos que el programador define y nombra explícitamente en un programa. Una variable simple es un objeto elemental de datos con nombre. El valor o valores de una variable es modificable por operaciones de asignación; es decir, el enlace de objeto de datos a valor puede cambiar durante su tiempo de vida. Las operaciones que se pueden realizar con dos o más valores exigen que éstas sean del mismo tipo de datos. No se puede sumar una variable carácter a otra numérica y/o viceversa.

Para crear una variable (de un tipo simple) en memoria debe declararse indicando su tipo de variable y su identificador que la identificará de forma única. La sintaxis de declaración de variables es la siguiente:

TipodeDato Identificador1, Identificador2;

Esta sentencia indica al compilador que reserve memoria para dos variables del tipo de dato TipodeDato con nombres Identificador1 e Identificador2.

Reglas para definición de variables

Pueden tener hasta 40 caracteres.

Debe empezar obligatoriamente con una letra (a-z / A-Z), el resto de los dígitos pueden ser números.

No pueden contener espacios en blanco, se pueden incluir caracteres especiales como el guión o el punto.

2.3

Uso de comentarios en programación

Los comentarios son anotaciones, observaciones, recordatorios, etc. en el programa. Son para uso exclusivo del programador, y eliminados del código fuente en la fase de preprocesado.

Aunque los comentarios son voluntarios (no es obligatorio escribirlos), representan una ayuda inestimable durante la construcción del programa. Siendo imprescindibles para el programador original, o los que le sucedan en las tareas de mantenimiento, cuando es necesario habérselas con el código un tiempo después de que fue escrito. Además de clarificar ideas, los comentarios son también un valioso instrumento de depuración, pues permiten eliminar provisionalmente secciones enteras de código.

Un comentario C/C++ es cualquier secuencia de caracteres contenida entre los delimitadores /* */. La totalidad de la secuencia, incluyendo los delimitadores /* y */ son sustituidos por un simple espacio después de la expansión de macros.

C++ también admite comentarios de una sola línea utilizando dos barras inclinadas (//) como señal de comienzo. El comentario empieza en este punto (incluyendo las señales de comienzo) y continúa hasta el próximo carácter de nueva línea.

Ejemplos de comentarios:

/* Esto es un comentario */

A = B + C;

//Esto es un comentario de una línea

C = C + D;

TEMA 3. PROGRAMACIÓN MODULAR (FUNCIONES Y PROCEDIMIENTOS)

3.1 Introducción

Uno de los métodos más conocidos para resolver un problema es dividirlo en problemas más pequeños, llamados subproblemas. De esta manera, en lugar de resolver una tarea compleja y tediosa, resolvemos otras más sencillas y a partir de ellas llegamos a la solución. Esta técnica se usa mucho en programación ya que programar no es más que resolver problemas, y se le suele llamar diseño descendente, metodología del divide y vencerás o programación top-down.

La programación modular consiste en dividir un algoritmo (programa) en unidades de menor tamaño, donde cada fragmento realiza una tarea explícita y única. Cada uno de esos fragmentos recibe el nombre de subalgoritmo o módulo. Por tanto, los subalgoritmos (sean funciones o procedimientos) nos permiten hacer pequeñas porciones de código reutilizable, que pueden ser llamados desde el resto del programa sin necesidad de repetir esas partes de código cada vez que se necesiten.

En el siguiente grafico se muestra con un ejemplo sencillo la programación modular, donde para obtener la nota final recurrimos a utilizar una función o procedimiento “promedio” que realiza el cálculo.

la nota final recurrimos a utilizar una función o procedimiento “promedio” que realiza el cálculo. 3.2

La función desde el punto de vista de programación, se define como un proceso que recibe valores de entrada (llamados argumentos) y el cual retorna un valor resultado. Adicionalmente las funciones son subprogramas que se pueden invocar (ejecutar) , desde cualquier parte del programa, es decir desde otra función, desde la misma función o desde el programa principal, cuantas veces sea necesario.

Las funciones se utilizan cuando dos o más porciones de un algoritmo que son iguales o similares, para lo cual se extrae es parte de código similar y se crea una funciona que tiene como cuerpo dicho codigo. Luego se reemplaza el código extraído en el algoritmo principal con el nombre de la nueva función creada.

Una función en C++, se declara de la siguiente manera

tipo nombre(arg1:tipo1, arg2:tipo2,…, argn:tipon){ <instrucciones> retornar <valor>

}

Donde:

tipo: es tipo de dato que retorna o devuelve la función

nombre: es el nombre de la función, Dicho nombre no puede contener espacios, símbolos o letras acentuadas. El nombre debe comenzar siempre con una letra y puede contener cualquier letra o número

arg1, arg2, argn: son los argumentos o datos de entrada de la función

tipo1, tipo2, tipon: son los tipos de datos de los argumentos

instrucciones son el conjunto de instrucciones que realiza la función

valor es el dato que devuelve o retorna la función.

En el siguiente ejemplo podemos definir la función sumar que recibe como datos de entrada dos valores de tipo numérico y retorna como resultado un valor de tipo también numérico, que en esta caso será como resultado de la suma.

En el programa principal invocamos a la función sumar con las variables ‘a’ y ‘b’ como datos de entrada, el programa devuelve el resultado que es asignado a la variable que es posteriormente impreso en pantalla.

#include <cstdlib> #include <iostream>

using namespace std; /*Funcion sumar recibe: dos parametros devuelve: la suma de ambos */ int sumar(int a, int b){ int c; c = a + b; return c;

}

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

{

int a = 2; int b = 3; int c; c = sumar(a,b); cout<<"La suma de "<<a<<" + "<<b<<" es de: "<<c<<endl; system("PAUSE"); return EXIT_SUCCESS;

}

NOTA: Toda función debe tener una instrucción return en su interior para devolver el resultado calculado.

3.3

Procedimientos

La declaración de procedimientos en C++ es muy similar a la declaración de las funciones. Los más puristas dirían que en C++ no existen los procedimientos como tales, ya que se declaran como funciones pero que no devuelven nada (void). Entonces, en el cuerpo de un procedimiento no devolveremos nada (no se escribe la instrucción return).

Veamos un ejemplo:

#include <cstdlib> #include <iostream>

using namespace std;

/* Procedimiento que muestra la tabla de multiplicar

* Recibe: el numero de la tabla a imprimir

* No duevelve ningun valor */ void tabla_mult(int num){ int x;

for (x=1;x<=10;x++){ cout<<x<<" X "<<num<<" = "<<x*num<<endl;

}

}

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

{

int n;

cout<<"Introduzca el numero para la tabla multiplicar:"; cin>>n; tabla_mult(n); system("PAUSE"); return EXIT_SUCCESS;

}

Como puedes comprobar la declaración del procedimiento es exactamente igual que la declaración de una función. La única diferencia está en que el procedimiento devuelve un valor de tipo void, es decir, vacío/nada.

3.4 Paso de parámetros por referencia

Como los procedimientos no devuelven ningún tipo de valor en la llamada, la única forma de devolver valores es incluir la posibilidad de modificar el valor de sus parámetros. Vamos a ver un ejemplo de cómo hacer eso. El siguiente programa muestra cómo pasar 2 parámetros por referencia, llamando a una función que intercambia los valores de los 2 parámetros dados.

#include <cstdlib> #include <iostream>

using namespace std;

/*Procedimientoq que intercambia valores de a y b

* recibe: los dos valor para intercambiar

* no devuelve ningun valor */ void intercambiar(int &a, int &b){ int aux; aux = a;

a = b;

b = aux;

}

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

{

int a = 2, b = 3; cout<<"Estos son los valores iniciales: a = "<<a<<", b = "<<b<<endl; intercambiar(a,b); cout<<"Estos son los valores intercambiados: a = "<<a<<", b = "<<b<<endl; system("PAUSE"); return EXIT_SUCCESS;

}

Para indicar que los dos parámetros se pasan por referencia se coloca el símbolo & delante del nombre de cada parámetro.

void intercambiar(int &a, int &b)

La utilización de dichos parámetros dentro del procedimiento es exactamente igual que el uso de cualquier otra variable.

NOTA: La utilización del símbolo & para indicar el paso de parámetros por variable es propio de C++, pero por su facilidad es el método que usaremos para pasar parámetros por referencia. En C el paso de parámetros por referencia es totalmente distinto.

TEMA 4. VECTORES Y MATRICES

Hasta ahora hemos trabajado con datos elementales (enteros, reales, caracteres; int, float, char) donde podíamos almacenar 1 sólo dato del tipo especificado. En muchas ocasiones es necesario trabajar con gran cantidad de datos de un mismo tipo.

Por ejemplo imagina que tienes que hacer un programa donde el usuario es un profesor que quiere almacenar las notas de sus alumnos para posteriormente hacer estadísticas y cálculos como nota máxima, mínima, la media, etc.

Podríamos hacerlo declarando una variable de tipo float para cada nota pero eso puede llegar a ser intratable si la cantidad de notas a almacenar fuera muy grande. Además el programa sería muy engorroso y difícil de entender.

Con los vectores y las matrices conseguimos, bajo un único nombre, es decir, con una única variable, almacenar tantos valores de un mismo tipo como queramos (según el tamaño que definamos en su declaración). En el ejemplo anterior, tendríamos un vector de float con longitud, por ejemplo 100, donde podríamos almacenar hasta 100 valores reales.

4.1

Vectores

Los vectores, también llamados tablas unidimensionales, son tipos de datos compuestos o estructurados caracterizados por:

- Todos los elementos del vector son del mismo tipo

- Todos los elementos del vector son referenciados con el mismo nombre y la diferencia entre ellos es el índice de la posición que ocupan dentro del vector

- Todos sus elementos son almacenados en posiciones de memoria contiguas.

El formato general para la declaración de una variable de tipo vector es la siguiente:

Tipo_de_datos nombre_del_vector [tamaño_del_vector]

Donde:

Tipo_de_datos indica el tipo de datos almacenados en la variable vector que se está declarando (int, float, char o cualquier otro tipo definido).

Nombre_del_vector indica el nombre de la variable vector que se está declarando. Con este nombre refenciaremos a cada elemento del vector.

Tamaño es un entero, o una constante de tipo entero, que indica cuántas posiciones tiene ese vector, es decir, cuál es el número máximo de elementos del tipo Tipo_de_datos que se pueden guardar en la variable Nombre_del_vector que estamos declarando. El tamaño no puede ser una variable.

Por ejemplo:

float notas[100];

Sería la declaración de la variable notas donde podríamos almacenar hasta 100 números reales.

Gráficamente podríamos representarlo así:

notas

reales. Gráficamente podríamos representarlo así: notas Para acceder a cada casilla del vector utilizamos un número

Para acceder a cada casilla del vector utilizamos un número entero llamado subíndice, que indica la posición del elemento dentro del vector. En C, los elementos empiezan a numerarse en el 0. Así el primer elemento del vector notas será notas sub 0, el segundo notas sub 1, etc., y el último notas sub 99. Fíjate que el último elemento es el que ocupa la posición tamaño_del_vector menos

1.

notas

que ocupa la posición tamaño_del_vector menos 1. notas 0 1 2 3 99 La sitaxis para

0

1

2

3

99

La sitaxis para acceder a estos elementos en C es la siguiente:

notas[0] (primer elemento)

notas[1] (segundo elemento)

….

notas[99] (último elemento)

Operaciones que podemos hacer con un vector

a) Consultar o leer el valor de un elemento del vector

Si queremos poner en la variable nota_alu5, la nota del alumno que tenemos en la posición 5 del vector anterior, haríamos la siguiente asignación:

float notas[100], nota_alu5; /*Declaración de variables */

nota_alu5 = notas[5];

b) Asignar o escribir un valor en un elemento del vector

Si queremos poner un 8 al alumno que está en la posición 5 haríamos la siguiente asignación:

notas[5] = 8;

En estos dos casos, el acceso al elemento del vector que queremos leer o en el que queremos escribir es directo, porque solamente leemos o modificamos un elemento, el 5 en ambos ejemplos. Si queremos recorrer todo el vector, ya sea para leer como para escribir, tendremos que utilizar un bucle.

c) Recorrido de un vector

El bucle más utilizado para recorrer un vector es el for, porque a priori sabemos cuántas vueltas dará el bucle (tantas como elementos del vector a visitar).

Mira el siguiente ejemplo:

#define MAX 20

int main()

{

 

float notas[MAX];

int i;

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

{

cout<<“Dame la nota de la posición :”<<i; cout>>notas[i];

}

return 1;

}

Este programa pide las notas de los alumnos y las almacena en el vector notas.

4.2

Matrices

Las matrices, también llamados tablas bidimensionales, son tipos de datos compuestos o estructurados caracterizados por:

- Todos los elementos de la matriz son del mismo tipo

- Todos los elementos de la matriz son referenciados con el mismo nombre y la diferencia entre ellos son los índices de la posición que ocupan dentro de la matriz

El formato general para la declaración de una variable de tipo matriz es la siguiente:

Tipo_de_datos nombre_de_la_matriz [número_filas] [número_columnas]

Donde:

Tipo_de_datos indica el tipo de datos almacenados en la variable matriz que se está declarando (int, float, char o cualquier otro tipo definido).

Nombre_de_la_matriz indica el nombre de la variable matriz que se está declarando. Con este nombre refenciaremos a cada elemento de la matriz.

Número_filas es un entero, o una constante de tipo entero, que indica cuántas filas tiene esa matriz

Número_columnas es un entero, o una constante de tipo entero, que indica cuántas columnas tiene esa matriz

El número de filas y de columnas no puede ser una variable.

Por ejemplo:

#define MAX_ASIG 6

#define MAX_ALUM 40

/*Definición de las constantes */

float notas[MAX_ASIG][MAX_ALUM];

Sería la declaración de la variable notas donde almacenaríamos todas las notas de los alumnos de cada asignatura. MAX_ASIG y MAX_ALUM serían dos constantes con valores enteros declaradas anteriormente.

Gráficamente podríamos representarlo así:

notas columna
notas
columna

fila

constantes con valores enteros declaradas anteriormente. Gráficamente podríamos representarlo así: notas columna fila

En este ejemplo la fila indica la asignatura y la columna el alumno.

Para acceder a cada elemento de la matriz utilizamos dos números enteros llamados subíndices, que indica la fila y la columna donde se encuentra el elemento dentro de la matriz. En C/C++, los elementos empiezan a numerarse en el 0. Así el primer elemento de la matriz notas será notas[0][0] y el último notas [MAX_ASIG -1][MAX_ALUM 1].

Si te fijas notarás que es exactamente igual que los vectores, pero con 2 dimensiones (la fila y la columna) en lugar de 1.

Operaciones que podemos hacer con una matriz

a) Consultar o leer el valor de un elemento de la matriz

Si queremos poner en la variable nota_alu5, la nota del alumno que tenemos en la posición 5 dentro de la asignatura 0, haríamos la siguiente asignación:

float nota_alu5, notas[MAX_ASIG][NAX_ALUM]; /*Declaración de variables*/

nota_alu5 = notas[0][5];

b) Asignar o escribir un valor en un elemento de la matriz

Si queremos poner un 8 en la asignatura 0 al alumno que está en la posición 5 haríamos la siguiente asignación:

notas[0][5] = 8;

En estos dos casos, el acceso al elemento de la matriz que queremos leer o en el que queremos escribir es directo, porque solamente leemos o modificamos un elemento, el fila 0, columna 5 en estos ejemplos. Si queremos recorrer toda la matriz, ya sea para leer como para escribir, tendremos que utilizar un bucle.

c) Recorrido de una matriz

Al igual que para recorrer un vector, para recorrer una matriz, el bucle más utilizado para recorrer es el for, porque a priori sabemos cuántas vueltas dará el bucle (tantas como elementos de la matriz a visitar).

Podemos recorrer la matriz por filas o por columnas. La manera de hacerlo dependerá del ejercicio.

En el siguiente ejemplo se piden las notas por teclado haciendo un recorrido por filas, pidiendo consecutivamente las notas de todos los alumnos de cada asignatura:

#define MAX_ASIG 6

#define MAX_ALUM 40

int main()

{

float notas [MAX_ASIG][MAX_ALUM];

int i,j;

for (i=0; i<MAX_ASIG; i++) /*Para cada asignatura (para cada fila)*/

4.3

{cout<<“Dame las notas de la asignatura”<<i;

for(j=0;

j<MAX_ALUM;

j++)

cada columna de cada fila) */

/*

Para

cada

alumno

{

cout<<“Nota del alumno:”<<j;

cout>>notas[i][j];

}

}

return 1;

}

Estructuras

de

cada

asignatura

(para

Una estructura es un grupo de variables las cuales pueden ser de diferentes tipos sostenidas o mantenidas juntas en una sola unidad. La unidad es la estructura.

En C/C++ se forma una estructura utilizando la palabra reservada struct, seguida por un campo etiqueta opcional, y luego una lista de miembros dentro de la estructura. La etiqueta opcional se utiliza para crear otras variables del tipo particular de la estructura:

struct nombre_estructura{ tipo1 campo1; tipo2 campo2; tipo3 campo3;

.

.

.

TipoN campoN;

};

Un punto y coma finaliza la definición de una estructura puesto que ésta es realmente una sentencia C/C++.

En un programa, podemos asociar una variable con una estructura utilizando una sentencia similar a la siguiente:

nombre_estructura nombre_variable;

Para acceder a los miembros de las estructuras se usa el punto u operador miembro (.). La sintaxis es:

nombre_variable.miembroNombre

Veamos su funcionamiento con un ejemplo:

#include <cstdlib> #include <iostream>

using namespace std;

struct persona{ string nombre; string apellido; string direccion; string telefono; int edad;

};

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

{

persona p1; p1.nombre = "Roberto"; p1.apellido = "Ramos"; p1.direccion = "Calvo #44"; p1.telefono = "6433434"; p1.edad = 21;

cout<<"Nombre:"<<p1.nombre<<endl;

cout<<"Apellido:"<<p1.apellido<<endl;

cout<<"Direccion:"<<p1.direccion<<endl;

cout<<"Telefono:"<<p1.telefono<<endl;

cout<<"Edad:"<<p1.edad<<endl;

system("PAUSE"); return EXIT_SUCCESS;

}

En el ejemplo declaramos una estructura llamada “persona” con los campos “nombre, apellido, dirección, teléfono y edad”. En la función principal, definimos la variable de tipo estructura y asignamos los valores a sus campos correspondientes y finalizamos mostrando en pantalla los valores de cada campo.

TEMA 5. PUNTEROS Y MEMORIAS DINAMICAS

5.1

Introducción

Por definición un puntero es una variable que puede almacenar la dirección en memoria de otra variable. Según mi definición, un puntero es una entidad, que puede ser variable o constante, que siempre contiene una dirección de memoria (valida, inválida o nula), en la cual puede estar alojada una entidad de nuestro programa o no.

El valor de todas las variables que manejamos en nuestros programas se almacenan en memoria y tienen una dirección. Un puntero es una variable especial que apunta a la dirección de memoria de una variable. El puntero tiene a su vez su propia dirección. Todas estas direcciones tienen un formato hexadecimal.

Los punteros son herramientas muy poderosas con muchas utilidades y enormes ventajas como veremos más adelante. A grandes rasgos, un puntero me permite desplazarme en la memoria, apuntar, redireccionar a ciertas variables, funciones, métodos, objetos sin necesidad de mover grandes bloques de datos, lo cual nos ahorra muchísimo el consumo de memoria en los programas.

La declaración de una variable tipo puntero debe ser de la forma:

TipoVariableApuntada *NombreVariablePuntero;

Una variable de tipo puntero debe ser de igual tipo que la variable cuya dirección en memoria contiene, o dicho de otra forma, la variable a la que apunta.

Un puntero no tiene asociación directa con el dato actual, pero si una asociación indirecta. Por ese motivo se usa el término “dirección” cuando se hace referencia a una asociación indirecta.

Un puntero se debe declarar de acuerdo al tipo de dato al que apunta. Ejem:

1 int *var; //Un puntero llamado var que podra apuntar a cualquier variable de tipo entero.

2 char *u;//puntero de tipo char

3 Persona *per;//puntero de tipo persona

Para determinar, asignar la dirección de una variable en c++, se usa el operador & y para obtener el contenido de un puntero utilizamos el operador * Ejem:

1

int a;//entero

2

int *b;//puntero a entero

3

a = 20;//a tiene 20

4

b=&a;//asigno la dirección de a al puntero b

5

6

cout << b << endl; // imprimirá la dirección de memoria de a;

7

cout << *b;// imprimirá 20, es decir el contenido de a

5.2 Aritmética de punteros

Utilizando los operadores de adición y sustracción podemos movernos a partir de la dirección relativa a la que apunta un puntero.

p++; cout << *p; P--; cout<<*p;

Pueden visualizar que estoy incrementando el puntero p en 1. Esto quiere decir que el puntero se desplazara 4 bytes en memoria (en este caso por ser entero) y entonces apuntara a otra direccion. Por eso es que el nuevo contenido de p es basura o bueno el contenido de lo que tiene esa nueva direccion a la que apunta.

Supongamos que definir un entero y puntero de tipo char:

1

char c;

2

char *d;

3

4

d= &c;//asigno la direccion de c a d

5

c='u';//asigno el valor u a mi variable c

6

c--;//desplazo una posicion a c

7

cout << *d;//

No Imprimira ‘u’ porque fijense que desplazé c en sentido negativo 1 byte (los char ocupan a 1 byte). Es decir, que si d estaba apuntado a una direccion como por ejemplo 0x22ff99, despues del cestara apuntando a algo como 0x22ff98

Para tomar en cuenta cosas que no puedo hacer con punteros:

01

int a=15;

02

int *p;

03

04

double *q;

05

void *r;

06

07

p = a; //No puedo hacer esto porque estoy asignando una variable a un puntero y un puntero es

08

una direccion. p = &50; // 50 es un valor constante en este caso y no una variable,por lo tanto no tiene direccion

09

p = &(a+1); //una expresion no tiene direccion

10

p = 30;//igual que el primer error, 30 es un entero.

11

&a = p;//no puedo cambiar la direccion de una variable

12

p = q;//p es puntero de tipo entero y q de tipo double

Un puntero de tipo void, es un puntero al cual le podemos asignar cualquier tipo de puntero. Por lo tanto si podriamos hacer esto:

r = p;

5.3 Comparación de punteros

Para la comparación de punteros se utilizan los operadores básicos de comparación que usábamos con variables bases, tales como int. Por lo que para saber por ejemplo si un puntero apunta a la misma dirección a la que apunta otro, utilizaríamos: p1 == p2, para saber si son distintos utilizaríamos el operador !=, para saber si p1 apunta a una dirección de memoria mas baja que p2 colocaríamos p1 < p2, y así con los demás operadores de comparación.

VECTORES Y PUNTEROS

Cuando declaramos un vector int v[10]; el nombre del vector es un puntero al primer elemento del vector, es decir a v[0]. Entonces como un vector es un puntero al primer elemento del mismo, también podríamos hacer aritmética de punteros con el vector.

1

(v+1) ;//apuntara a v[1];

2

*(v+5);//me refiero al contenido de v[5]

3

4

//Y también a los punteros les puedo poner índices:

5

6

int *p; //puntero de tipo entero

7

p = &v[0];//p apunta a la direccion del vector v[0] o tambien a v. p = v

8

p[8] = 80; //le asigno el valor 80 al puntero en la posicion 8, es decir a v[8]

5.4 Vectores Dinámicos

Lo que vimos en el inicio de este post, son vectores estáticos, puesto que tienen una cantidad fija de memoria asignada y tamaño definido que no podemos modificarlo. Sin embargo, un vector podría tener una cantidad variable de datos, a este se le llama un vector dinámico.

Para usar vectores dinámicos necesitamos gestionar memoria dinámica. Si bien es cierto que esto trae enormes ventajas, el hacer un mal uso de la memoria dinámica nos podría traer problemas desastrozos. Por eso es importante que cuando creemos vectores dinámicos también liberemos la memoria utilizada. Obviamente eliminaremos la memoria utilizada cuando ya no necesitamos más usar, en este caso, un determinado vector.

El operador new sirve para reservar memoria dinámica. El operador delete se usa para liberar la memoria dinámica reservada con new. Para liberar memoria de un array dinámico usamos delete[] .

El espacio de memoria que hemos reservado con new tendrá vida hasta que finalize la ejecución del programa o cuando liberemos ese espacio con delete. Siempre es recomendable liberar memoria para posteriormente no tener problemas con excesivo consumo de memoria.

Un simple ejemplo:

01

#include <iostream>

02

using namespace std;

03

04

int main()

05

{

07

int dim;

08

09

cout << "Ingresa el tamanyo del vector" << endl;

10

cin >>dim;

11

pv = new int[dim];

12

13

for(int i=0;i<dim;i++){

14

pv[i] = i * i;

15

cout << pv[i] << " ";

16

}

17

18

delete[] pv;

19

return 0;

20 }

TEMA 6. PROGRAMACION ORIENTADA A OBJETOS.

6.1 Que es la programación orientada a objetos?

Hasta ahora estamos estado "cuadriculando"• todo para obtener algoritmos: tratábamos de convertir cualquier cosa en funciones y variables que pudieramos emplear en nuestros programas.

Pero no todo lo que nos rodea es tan fácil de cuadricular. Supongamos por ejemplo que tenemos que describir una puerta desde nuestro programa. En la zona de declaración de variables detallaríamos datos como su tamaño o su color. Pero no basta con eso. De hecho, eso no es lo más importante: lo que hace que una puerta sea una puerta es el hecho de que se pueda abrir y se pueda cerrar. Por tanto, si queremos simular o controlar una puerta desde nuestro programa, deberíamos crear también las funciones AbrirPuerta y CerrarPuerta en alguna otra parte de nuestro fuente.

No está mal, pero es antinatural: una puerta es un conjunto: no podemos separar su color de su tamaño, o de la forma en que debemos abrirla o cerrarla. Sus características son tanto las físicas (lo que hasta ahora llamábamos variables) como sus comportamientos en distintas circunstancias (lo que para nosotros eran las funciones). Todo ello va siempre unido, formando un OBJETO.

Por otra parte, si tenemos que explicar a alguien lo que es el portón de un garaje, y ese alguien no lo ha visto nunca, pero conoce cómo es la puerta de su casa, le podemos decir "se parece a una puerta de una casa, pero es más grande para que quepan los coches, está hecha de metal en vez

de madera

"•.

Las dos cosas son puertas: se trata de dos objetos que pertenecen a la misma

CLASE.

Finalmente, conviene recordar que "abrir"• no se refiere sólo a una puerta. También podemos hablar de abrir una ventana o un libro, por ejemplo.

Pues con esta discusión hemos comentado casi sin saberlo las tres características más importantes de la Programación Orientada a Objetos (POO):

ENCAPSULACION: No podemos separar los comportamientos de las características de un objeto. Los comportamientos del objeto serán funciones, que en OOP llamaremos METODOS. Las características del objeto serán variables, como las que hemos usado siempre, que en OOP llamaremos ATRIBUTOS. La apariencia de un objeto en C++, como veremos un poco más adelante, recordará a un registro o "struct"•, que contendrá funciones además de datos.

HERENCIA: Unos objetos pueden "heredar"• métodos y atributos de otros. Esto hace más fácil definir objetos nuevos a partir de otros que ya teníamos anteriormente (como

ocurría con el portón y la puerta) y facilitará la reutilización de los programas, pudiendo

aprovechar buena parte de los anteriores

si están bien diseñados.

POLIMORFISMO: Un mismo nombre de un método puede hacer referencia a comportamientos relativamente distintos (como abrir una puerta o abrir un libro). En C++ el polimorfismo llega incluso al nivel de los operadores: podremos "redefinir"• el operador + para que sume matrices, ficheros o cualquier tipo de objeto que nos interese.

(Nota: en C++ es frecuente llamar también "variables de instancia" o "variables miembro" a los atributos, y "funciones miembro" a los métodos).

Comentado esto, vamos a empezar a ver ejemplos en C++ para tratar de fijar estos primeros conceptos y de ver la sintaxis de este lenguaje. A partir de unos primeros ejemplos sencillos, iremos profundizando paulatinamente.

6.2 Definición de clase

Una clase, es simplemente una abstracción que hacemos de nuestra experiencia sensible. El ser humano tiende a agrupar seres o cosas -objetos- con características similares en grupos -clases-. Así, aun cuando existen por ejemplo multitud de vasos diferentes, podemos reconocer un vaso en cuanto lo vemos, incluso aun cuando ese modelo concreto de vaso no lo hayamos visto nunca. El concepto de vaso es una abstracción de nuestra experiencia sensible.

Quizás el ejemplo más claro para exponer esto lo tengamos en las taxonomías; los biólogos han dividido a todo ser (vivo o inerte) sobre la tierra en distintas clases.

Tomemos como ejemplo una pequeña porción del inmenso árbol taxonómico:

Ellos, llaman a cada una de estas parcelas reino, tipo, clase, especie, orden, familia, género,

Ellos, llaman a cada una de estas parcelas reino, tipo, clase, especie, orden, familia, género, etc.; sin embargo, nosotros a todas las llamaremos del mismo modo: clase. Así, hablaremos de la clase animal, clase vegetal y clase mineral, o de la clase félidos y de las clases leo (león) y tigris (tigre).

Cada clase posee unas cualidades que la diferencian de las otras. Así, por ejemplo, los vegetales se diferencian de los minerales -entre otras muchas cosas- en que los primeros son seres vivos y los minerales no. De los animales se diferencian en que las plantas son capaces de sintetizar clorofila a partir de la luz solar y los animales no.

Situémonos en la clase felinos (felis), aquí tenemos varias subclases (géneros en palabras de los biólogos): león, tigre, pantera, gato, etc. cada una de estas subclases, tienen características comunes (por ello los identificamos a todos ellos como felinos) y características diferenciadoras (por ello distinguimos a un león de una pantera), sin embargo, ni el león ni la pantera en abstracto existen, existen leones y panteras particulares, pero hemos realizado una abstracción de esos rasgos comunes a todos los elementos de una clase, para llegar al concepto de león, o de pantera, o de felino.

La clase león se diferencia de la clase pantera en el color de la piel, y comparte ciertos atributos con el resto de los felinos -uñas retráctiles por ejemplo- que lo diferencian del resto de los animales. Pero la clase león, también hereda de las clases superiores ciertas cualidades: columna vertebral (de la clase vertebrados) y es alimentado en su infancia por leche materna (de la clase mamíferos).

Vemos cómo las clases superiores son más generales que las inferiores y cómo, al ir bajando por este árbol, vamos definiendo cada vez más (dotando de más cualidades) a las nuevas clases. Hay cualidades que ciertas clases comparten con otras, pero no son exactamente iguales en las dos clases. Por ejemplo, la clase hombre, también deriva de la clase vertebrado, por lo que ambos

poseen columna vertebral, sin embrago, mientras que en la clase hombre se halla en posición vertical, en la clase león la columna vertebral está en posición horizontal.

En OOP existe otro concepto muy importante asociado al de clase, el de "clase abstracta". Una clase abstracta es aquella que construimos para derivar de ella otras clases, pero de la que no se puede instanciar. Por ejemplo, la clase mamífero, no existe como tal en la naturaleza, no existe ningún ser que sea tan solo mamífero (no hay ninguna instanciación directa de esa clase), existen humanos, gatos, conejos, etc. Todos ellos son mamíferos, pero no existe un animal que sea solo mamífero.

Del mismo modo, la clase que se halla al inicio de la jerarquía de clases, normalmente es creada sólo para que contenga aquellos datos y métodos comunes a todas las clases que de ella derivan:

Son clases abstractas. En árboles complejos de jerarquías de clases, suele haber más de una clase abstracta.

Este es un concepto muy importante: el de "clase abstracta". Como hemos dicho, una clase abstracta es aquella que construimos para derivar de ella otras clases, pero de la que no se puede instanciar. Por ejemplo, la clase mamífero, no existe como tal en la naturaleza, no existe ningún ser que sea tan solo mamífero (no hay ninguna instanciación directa de esa clase), existen humanos, gatos, conejos, etc.

Todos ellos son mamíferos, pero no existe un animal que sea solo mamífero. Por último, adelantemos algo sobre el concepto de objeto. El león, como hemos apuntado antes, no existe, igual que no existe el hombre; existen leones en los circos, en los zoológicos y, según tenemos entendido, aún queda alguno en la sabana africana. También existen hombres, como usted, que está leyendo este libro (hombre en un sentido neutro, ya sea de la subclase mujer o varón), o cada uno de los que nos encontramos a diario en todas partes.

Todos estos hombres comparten las características de la clase hombre, pero son diferentes entre sí, en estatura, pigmentación de la piel, color de ojos, complexión, etc. A cada uno de los hombres particulares los llamamos "objetos de la clase hombre". Decimos que son objetos de tipo hombre o que pertenecen a la clase hombre. Más técnicamente, José Luis Aranguren o Leonardo da Vinci son instanciaciones de la clase hombre; instanciar un objeto de una clase es crear un nuevo elemento de esa clase, cada niño que nace es una nueva instanciación a la clase hombre.

6.3 Definición de objeto

En POO, un objeto es un conjunto de datos y métodos, donde los datos son lo que antes hemos llamado características o atributos, los métodos son los comportamientos que pueden realizar. Lo importante de un sistema OOP es que ambos, datos y métodos están tan intrínsecamente ligados, que forman una misma unidad conceptual y operacional. En POO, no se pueden desligar los datos de los métodos de un objeto. Así es como ocurre en el mundo real.

Vamos ahora a dar una serie de ejemplos en los que nos iremos acercando paulatinamente a los objetos informáticos. Los últimos ejemplos son para aquellos que ya conocen Java y/o C++; sin embargo, estos ejemplos que exigen conocimientos informáticos, no son imprescindibles para entender plenamente el concepto de clase y el de objeto.

Observe que aunque los datos y los métodos se han enumerado verticalmente, no existe ninguna correspondencia entre un dato y el método que aparece a su derecha, es una simple enunciación.

Ejemplo 1 Tomemos la clase león de la que hablamos antes y veamos cuales serían algunos de sus datos y de sus métodos.

cuales serían algunos de sus datos y de sus métodos. Ejemplo 2 Nuestros objetos (los informáticos),

Ejemplo 2

Nuestros objetos (los informáticos), como hemos comentado antes, se parecen mucho a los del mundo real, al igual que estos, poseen propiedades (datos) y comportamientos (métodos). Tomemos para nuestro ejemplo un cassette. Veamos cómo lo definiríamos al estilo de POO.

un cassette. Veamos cómo lo definiríamos al estilo de POO. Ejemplo 3 Pongamos otro ejemplo algo

Ejemplo 3

Pongamos otro ejemplo algo más próximo a los objetos que se suelen tratar en informática: un recuadro en la pantalla. El recuadro pertenecería a una clase a la llamaremos marco. Veamos sus datos y sus métodos.

6.4 Herencia Esta es la cualidad más importante de un sistema POO, la que nos

6.4

Herencia

Esta es la cualidad más importante de un sistema POO, la que nos dará mayor potencia y productividad, permitiéndonos ahorrar horas y horas de codificación y de depuración de errores. Es por ello que me niego a considerar que un lenguaje es OOP si no incluye herencia, como es el caso de Visual Basic (al menos hasta la versión 5, que es la última que conozco).

Como todos entendemos lo que es la herencia biológica, continuaremos con nuestro ejemplo taxonómico del que hablábamos en el epígrafe anterior.

La clase león, como comentábamos antes, hereda cualidades -métodos, en lenguaje POO- de todas las clases predecesoras -padres, en POO- y posee métodos propios, diferentes a los del resto de las clases.

Es decir, las clases van especializándose según se avanza en el árbol taxonómico. Cada vez que creamos una clase heredada de otra (la padre) añadimos métodos a la clase padre o modificamos alguno de los métodos de la clase padre. Veamos qué hereda la clase león de sus clases padre:

Veamos qué hereda la clase león de sus clases padre: La clase león hereda todos los

La clase león hereda todos los métodos de las clases padre y añade métodos nuevos que forman su clase distinguiéndola del resto de las clases: por ejemplo el color de su piel. Observemos ahora algo crucial que ya apuntábamos antes: dos subclases distintas, que derivan de una misma clase

padre común, pueden heredar los métodos de la clase padre tal y como estos han sido definidos en ella, o pueden modificar todos o algunos de estos métodos para adaptarlos a sus necesidades. En el ejemplo que exponíamos antes, en la clase león la alimentación es carnívora, mientras que en la clase hombre, se ha modificado éste dato, siendo su alimentación omnívora.

Pongamos ahora un ejemplo algo más informático: supongamos que usted ha construido una clase que le permite leer números enteros desde teclado con un formato determinado, calcular su IVA y almacenarlos en un fichero. Si desea poder hacer lo mismo con números reales (para que admitan decimales), solo deberá crear una nueva subclase para que herede de la clase padre todos sus métodos y redefinirá solo el método de lectura de teclado. Esta nueva clase sabe almacenar y mostrar los números con formato porque lo sabe su clase padre.

Las cualidades comunes que comparten distintas clases, pueden y deben agruparse para formar una clase padre -también llamada superclase-. Por ejemplo, usted podría derivar las clases presupuesto, albarán y factura de la superclase pedidos, ya que estas clases comparten

características comunes. De este modo, la clase padre poseería los métodos comunes a todas ellas

y sólo tendríamos que añadir aquellos métodos propios de cada una de las subclases, pudiendo

reutilizar el código escrito en la superclase desde cada una de las clases derivadas. Así, si enseñamos a la clase padre a imprimirse, cada uno de los objetos de las clases inferiores sabrán automáticamente y sin escribir ni una solo línea más de código imprimirse.

La herencia como puede intuir, es la cualidad más importante de la POO, ya que le permite reutilizar todo el código escrito para las superclases re-escribiendo solo aquellas diferencias que existan entre éstas y las subclases.

Veamos ahora algunos aspectos más técnicos de la herencia:

A la clase heredada se le llama, subclase o clase hija, y a la clase de la que se hereda superclase o

clase padre. Al heredar, la clase heredada toma directamente el comportamiento de su superclase, pero puesto que ésta puede derivar de otra, y esta de otra, etc., una clase toma indirectamente el comportamiento de todas las clases de la rama del árbol de la jerarquía de clases a la que pertenece.

Se heredan los datos y los métodos, por lo tanto, ambos pueden ser redefinidos en las clases hijas, aunque lo más común es redefinir métodos y no datos. Muchas veces las clases especialmente

aquellas que se encuentran próximas a la raíz en el árbol de la jerarquía de clasesson abstractas, es decir, sólo existen para proporcionar una base para la creación de clases más específicas, y por

lo tanto no puede instanciarse de ellas; son las clases virtuales.

Una subclase hereda de su superclase sólo aquellos miembros visibles desde la clase hija y por lo tanto solo puede redefinir estos.

Una subclase tiene forzosamente que redefinir aquellos métodos que han sido definidos como abstractos en la clase padre o padres. Normalmente, como hemos comentado, se redefinen los métodos, aun cuando a veces se hace necesario redefinir datos de las clases superiores. Al redefinir un método queremos o bien sustituir el funcionamiento del método de la clase padre o bien ampliarlo.

En el primer caso (sustituirlo) no hay ningún problema, ya que a la clase hija la dotamos con un método de igual nombre que el método que queremos redefinir en la clase padre y lo implementamos según las necesidades de la clase hija. De este modo cada vez que se invoque este

método de la clase hija se ejecutará su código, y no el código escrito para el método homónimo de

la clase padre.

Pero si lo que queremos es ampliar el funcionamiento de un método existente en la clase padre (lo que suele ser lo más habitual), entonces primero tiene que ejecutarse el método de la cla se padre, y después el de la clase hija. Pero como los dos métodos tienen el mismo nombre, se hace necesario habilitar alguna forma de distinguir cuando nos estamos refiriendo a un método de la clase hija y cuando al del mismo nombre de la clase padre.

Esto se hace mediante el uso de dos palabras reservadas, las cuales pueden variar dependiendo del lenguaje que se utilice, pero que normalmente son: this (en algunos lenguajes se utiliza la palabra reservada Self) y super:

this

Con esta palabra, podemos referirnos a los miembros de la clase.

En esencia, una clase en C++ es una estructura en el estilo de C con algunas ventajas sencillas pero muy potentes.

6.5

Polimorfismo

Por polimorfismo entendemos aquella cualidad que poseen los objetos para responder de distinto modo ante el mismo mensaje. Pongamos por ejemplo las clases hombre, vaca y perro, si a todos les damos la orden -enviamos el mensaje- Come, cada uno de ellos sabe cómo hacerlo y realizará este comportamiento a su modo.

Veamos otro ejemplo algo más ilustrativo. Tomemos las clases barco, avión y coche, todas ellas derivadas de la clase padre vehículo; si les enviamos el mensaje Desplázate, cada una de ellas sabe cómo hacerlo. Realmente, y para ser exactos, los mensaje no se envían a las clases, sino a todos o algunos de los objetos instanciados de las clases. Así, por ejemplo, podemos decirle a los objetos Juan Sebastián el Cano y Kontiqui, de la clase barco que se desplacen, con los que el resto de los objetos de esa clase permanecerán inmóviles.

Del mismo modo, si tenemos en pantalla cinco recuadros (marcos) y tres textos, podemos decirle

a tres de los recuadros y a dos de los textos que cambien de color y no decírselo a los demás

objetos. Todos estos sabrán cómo hacerlo porque hemos redefinido para cada uno de ellos su método Pintarse que bien podría estar en la clase padre Visual (conjunto de objetos que pueden visualizarse en pantalla).

En programación tradicional, debemos crear un nombre distinto para la acción de pintarse, si se

trata de un texto o de un marco; en OOP el mismo nombre nos sirve para todas las clases creadas

si así lo queremos, lo que suele ser habitual. El mismo nombre suele usarse para realizar acciones

similares en clases diferentes.

Si enviamos el mensaje Imprímete a objetos de distintas clases, cada uno se imprimirá como le

corresponda, ya que todos saben cómo hacerlo. El polimorfismo nos facilita el trabajo, ya que

gracias a él, el número de nombres de métodos que tenemos que recordar disminuye extensiblemente.

La mayor ventaja la obtendremos en métodos con igual nombre aplicados a las clases que se encuentran próximas a la raíz del árbol de clases, ya que estos métodos afectarán a todas las clases que de ellas se deriven.

6.6 Sobrecarga

La sobrecarga puede ser considerada como un tipo especial de polimorfismo que casi todos los

lenguajes de POO incluyen. Varios métodos (incluidos los "constructores", de los que se hablará más adelante) pueden tener el mismo nombre siempre y cuando el tipo de parámetros que recibe

o el número de ellos sea diferente.

De este modo, por ejemplo la clase File puede tener tantos método Write() como tipos de datos queramos escribir (no se preocupe si no entiende la nomenclatura, céntrese en la idea):

File::Write( int i ); Escribe un integer File::Write( long l ); Escribe un long File::Write( float f ); Escribe un flota File::Write( string s ); Escribe una cadena File::Write( string s, boolean b ); Escribe una cadena pasándola a mayúsculas

6.7 Declaración de clases en C++

Para declarar una clase, todo lo que se necesita es escribir una definición de estructura y sustituir la palabra reservada struct por class. Por ejemplo, una clase empleado con campos como el nombre, el departamento, la posición, el una función que nos imprima la información de este quedaría así:

class Empleado { char* m_nombre; char* m_departamento; char* m_posicion; long m_salario; void Imprimir( Empleado infoEmpleado);

}

Cuando usted declara una clase en C++, no se reserva memoria para la clase hasta que usted crea un objeto de la clase. Crear un objeto de una clase se llama instanciar un objeto. Un objeto creado de una clase de denomina instancia de una clase. Por ejemplo, yo puedo tener una instancia de

empleado con el valor en m_nombre=Jose, m_departamento=Sistemas, m_posicion=programador y m_salario=3000000 por ejemplo.

6.7.1. Especificadores de acceso

C++ utiliza especificadores de acceso para permitir controlar a una clase el acceso a las variables de datos de esa clase. Los especificadores de acceso permiten acceder a algunos miembros de la clase y restringir el acceso a otros.

Hay tres especificadores de acceso en C++: public, private y protected. Cuando usted declara público (public) un miembro de una clase, usted permite el acceso a tal miembro desde dentro y fuera de la clase. Los miembros de datos que son declarados protegidos ( protected ) son únicamente accesibles por funciones miembro de la clase, pero no se pueden acceder a ellos desde otras clases. Cuando un miembro de una clase es declarado privado ( private ) es ináccesible no sólo desde otras clases y otras partes del programa, sino también desde sus clases derivadas. Las clases derivadas se explicaran posteriormente.

Miremos el siguiente programa de ejemplo. Se compone de tres partes: la primera una declaración de una clase llamada Empleado:

class Empleado { private:

char* m_nombre; char* m_departamento; char* m_posicion; long m_salario;

public:

void ImprimirInfo(); void SetNombre( char* nombre ) { m_nombre = nombre } void SetDepartamento( char * departamento) { m_departamento = departamento } void SetPosicion ( char* posicion ) { m_posicion = posicion } void SetSalario ( long salario ) { m_salario = salario } const char* GetNombre( ){ return m_nombre } const char* GetDepartamento( ){ return m_departamento } const char* GetPosicion( ){ return m_posicion } const char* GetSalario( ){ return m_salario }

};

Las funciones SetNombre, SetDepartamento, setPosicion, setSalario, GetNombre, GetDepartamento, GetPosicion y GetSalario se denominan funciones intercaladas, que son funciones que se declaran en una sola línea.

Las variables de miembro son declaradas privadas para que funciones de miembro de otras

funciones no tengan acceso a ellas sino a travez de la correspondiente funcion Get o Set. Las funciones de miembro si son declaradas públicas de tal modo que se pueda acceder a ellas desde otras funciones.

La definición de la función PrintInfo puede quedar así:

void Empleado::ImprimirInfo( )

{

cout << "Nombre: " << m_nombre << '\n'; cout << "Departamento: " << m_departamento << '\n'; cout << "Puesto: " << m_posicion << '\n'; cout << "Salario: " << m_salario << '\n';

}

Los dos puntos ( :: ) se denomina operador de resolución de ambito. Nos indica que la función que estamos definiendo que en este caso es ImprimirInfo, pertenece a la clase Empleado.

La tercera parte es la función main. Veamos como podría ser:

void main()

{

//creacion de un objeto de la clase Empleado Empleado empleado12;

//asignacion de valores a las variables miembro

empleado12.SetNombre("Jose");

empleado12.SetDepartamento("Sistemas");

empleado12.SetPosicion("Programador");

empleado12.SetSalario(3000000);

//impresion de los datos

empleado12.ImprimirInfo();

}

Entonces, primero en:

Empleado empleado12;

Se instancia un objeto de la clase Empleado con nombre empleado12. Entonces empleado12 tiene la estructura de la clase Empleado.

Luego, en las líneas siguientes a la instanciación del objeto, se le asignan los valores iniciales a sus variables:

//asignacion de valores a las variables miembro

empleado12.SetNombre("Jose");

empleado12.SetDepartamento("Sistemas");

empleado12.SetPosicion("Programador");

empleado12.SetSalario(3000000);

Finalmente se llama ImprimirInfo para imprimir el contenido de las variables:

//impresion de los datos

empleado12.ImprimirInfo();

que lo que hará es imprimir el valor de las variables en la pantalla.

Permitir el acceso a las variables solo a través de funciones, que en la mayoría de los casos se llaman SetXxx y GetXxx, se llama encapsulación de datos. Las funciones que necesitan valores de otra clase, llaman a las funciones que les dan acceso y obtienen estos datos sin conocimiento de detalles específicos de como se manipulan los datos.

6.7.2 Operador de resolución de ambito

El operador de ámbito permíte acceder de otra manera funciones de miembro y variables de miembro de una clase. Cuando aparece el operador de resolución de ámbito entre el nombre de la clase y el nombre de la función en un programa significa que la función especificada es un miembro de la clase especificada:

Empleado::ImprimirInfo();

El operador de resolución de ambito se suele utilizar para llamar funciones que se encuentran fuera del ambito de la función de llamada. Entonces, para llamar la función ImprimirInfo() de la clase Empleado se fuera de su ambito se debe utilizar este operador.

La principal diferencia entre este operador y los operadores punto y flecha es que el operador de resolución de ambito se utiliza para acceder a miembros de clases, y el operador punto y flecha para acceder a miembros de objetos específicos.

Veamos el siguiente código:

::MessageBox("Prueba del operador de resolucion");

Si el operador de resolución de ambito aparece sin un nombre de clase delante, significa que la función que esta llamando ( MessageBox ) no es miembro de ninguna clase.

6.7.3

El apuntador this

Este apuntador lo tiene todo objeto en C++, apuntando a sí mismo. Se puede utilizar este apuntador en cualquier lado para acceder a todos los miembros del objeto al cual está apuntando este apuntador this. Veamos el siguiente código:

#include <iostream.h>

class Miclase { public:

Miclase() {} //constructor por defecto ~Miclase() {} //destructor void yoMismo() { return this } };

int main()

{

void* pClase; Miclase unObjeto; pClase = unObjeto.yoMismo();

cout<< "El puntero pClase es " << pClase <<'\n.'; return 0;

}

En este ejemplo la clase yoMismo() devuelve un apuntador al objeto que lo posee de la clase Miclase. El main() crea un objeto de la clase Miclase y luego llama a yoMismo(). Lo almacena en pClase y luego enseña el contenido, que en este caso es el valor de la referencia. Entonces este apuntador nos permitira realizar muchas cosas sobre los propios objetos con esta referencia.