Texto Sis2210 - 2021
Texto Sis2210 - 2021
Contenido
CAPITULO 1 ........................................................................................................................................6
FUNCIONES ........................................................................................................................................6
1.1 PROPIEDADES DE UNA FUNCION. ................................................................................................6
1.2. FUNCIONES EN C++. ....................................................................................................................7
1.3. DECLARACIÓN DE UNA FUNCIÓN. ...............................................................................................8
1.4. DEFINICION DE UNA FUNCION. ...................................................................................................9
1.5. ACCESO O LLAMADA A UNA FUNCION. .....................................................................................11
1.6. VARIABLES L.OCALES. ................................................................................................................12
1.7. VARIABLES GLOBALES. ..............................................................................................................13
1.8. PASO DE PARAMETROS POR VALOR Y POR REFERNCIA. ...........................................................14
1.9. FUNCIONES RECURSIVAS. ..........................................................................................................16
CAPITULO 2. .....................................................................................................................................19
Programación Orientada a Objetos ..................................................................................................19
2.1 ORIGENES DE LA PROGRAMACION ORIENTADA A OBJETOS. .....................................................20
2.2 CONCEPTOS DE LA PROGRAMACION ORIENTADA A OBJETOS. ..................................................21
2.3 PRESENTACION DE LAS CLASES Y LOS OBJETOS .........................................................................23
2.3.1 DEFINICIÓN DE UNA CLASE. ....................................................................................................24
2.3.2 IDENTIFICADORES DE CLASE. ...................................................................................................24
2.3.2.1 CUERPO DE UNA CLASE. .......................................................................................................25
2.3.3 USO DE UNA CLASE. ................................................................................................................26
2.4 ACCESO A UNA CLASE. ...............................................................................................................29
2.4.1 MIEMBROS DE LA CLASE PRIVATE. ..........................................................................................30
2.4.2 MIEMBROS DE LA CLASE PUBLIC: ............................................................................................31
2.4.3 MIEMBROS DE LA CLASE PROTECTED. ....................................................................................31
2.5. APUNTADORES COMO MIEMBROS DE DATOS. .........................................................................33
2.5.1 APUNTADORES A MIEMBROS DE DATOS DE CLASES. ..............................................................33
2.6. CONSTRUCTORES. .....................................................................................................................34
2.6.1 CONSTRUCTORES PRIVATE. .....................................................................................................36
2.6.2 CONSTRUCTORES CON ARGUMENTOS. ...................................................................................36
2.6.3 CONSTRUCTORES PARA COPIAR OBJETOS. .............................................................................38
CAPITULO 4 ......................................................................................................................................88
ESTRUCTURAS Y UNIONES ...............................................................................................................88
4.1. ESTRUCTURAS. ..........................................................................................................................88
4.1.1. ESQUEMA DE LA ESTRUCTURA. ..............................................................................................89
4.1.2. DECLRACION DE UN TIPO DE DATO ESTRUCTURA..................................................................89
4.1.3. DECLARACION DE UNA VARIABLE ESTRUCTURA. ...................................................................90
4.1.4. ACCESO A LOS CAMPOS DE UNA VARIABLE ESTRUCTURA .....................................................91
4.1.5. OPERACIONES CON ESTRUCTURAS. .......................................................................................92
4.1.6. ARREGLOS DE ESTRUCTURAS. ................................................................................................93
4.1.7. ESTRUCTURAS ANIDADAS ......................................................................................................97
4.2 UNIONES. ...................................................................................................................................99
CAPITULO 5 ....................................................................................................................................101
PUNTEROS Y ESTRUCTURAS DINÁMICAS DE DATOS ......................................................................101
5.1. OPERADORES PARA EL MANEJO DE PUNTEROS. .....................................................................101
5.1.1. OPERADOR DE DIRECCION ‘&’. .............................................................................................101
5.1.2 OPERADOR DE INDIRECCION O CONTENIDO ‘*’. ...................................................................102
5.2. DECLARACIÓN DE PUNTEROS. .................................................................................................103
5.2.1. ALMACENAMIENTO EN UNA VARIABLE PUNTERO. ..............................................................103
5.3. OPERACIONES CON PUNTEROS. ..............................................................................................105
5.3.1. ASIGNACION.........................................................................................................................105
5.3.2. COMPARACION. ...................................................................................................................106
5.3.3. ARITMETICA CON PUNTEROS. ..............................................................................................106
5.4. PUNTEROS Y ARREGLOS. .........................................................................................................108
5.4.1. PUNTERO A CADENA DE CARACTERES. ................................................................................110
5.4.2. PUNTEROS A ESTRUCTURAS. ................................................................................................112
5.5. ESTRUCTURAS DINÁMICAS DE DATOS. LISTAS SIMPLES. .........................................................114
5.5.1. OPERACIONES BASICAS EN UNA LISTA SIMPLE. ...................................................................115
CAPITULO 6 ....................................................................................................................................120
ARCHIVOS.......................................................................................................................................120
6.1. DEFINICION. ............................................................................................................................120
6.2. PROPIEDADES DE UN ARCHIVO. ..............................................................................................120
6.3. ORGANIZACIÓN DE UN ARCHIVO. ...........................................................................................122
CAPITULO 1
FUNCIONES
Ventajas:
Desde el punto de vista de la definición matemática una función debe cumplir con las
siguientes propiedades:
realicen las mismas tareas varias veces, lo que se facilita mediante la utilización de
funciones. Sin embargo, es probable que ciertas funciones no sean reutilizables, pero al
usarlas se mejora la legibilidad del programa.
Donde:
Vamos a suponer que queremos crear un programa para calcular el precio de un producto
basándose en el precio base del mismo y el impuesto aplicable. A continuación mostramos
el código fuente de dicho programa:
#include <stdio.h>
Por otra parte, en la función main declaramos dos variables de tipo float que contienen el
precio base del producto y el impuesto aplicable. La siguiente sentencia dentro de la
función main es la llamada a la función de biblioteca printf, que recibe como parámetro
Una llamada a la función precio, que devuelve un valor de tipo float. De esta manera, la
función printf imprime por la salida estándar el valor devuelto por la función precio. Es
importante tener en cuenta que las variables importe y tasa (argumentos) dentro de la
función main tienen una correspondencia con las variables base e impuesto (parámetros)
dentro de la función precio respectivamente.
tipo_de_retorno nombre_de_la_función(lista_de_parámetros);
En este caso se declara la función cubo que recibe como parámetro una variable de tipo
entero (numero) y devuelve un valor del mismo tipo. En ningún momento estamos
especificando qué se va a hacer con la variable numero, sólo declaramos las características
de la función cubo.
Cabe señalar que el nombre de los parámetros es opcional y se utiliza para mejorar la
comprensión del código fuente. De esta manera, el prototipo de la función cubo podría
expresarse de la siguiente manera:
int cubo(int);
Los prototipos de las funciones son utilizados por el compilador para verificar que se
accede a la función de la manera adecuada con respecto al número y tipo de parámetros, y
al tipo de valor de retorno de la misma. Veamos algunos ejemplos de prototipos de
funciones:
Luego de declarar una función, el siguiente paso es implementarla. Generalmente, este paso
se conoce como definición. Es precisamente en la definición de una función donde se
especifican las instrucciones que forman parte de la misma y que se utilizan para llevar a
cabo la tarea específica de la función. La definición de una función consta de dos partes:
tipo_de_retorno nombre_de_la_función(lista_de_parámetros)
{
sentencias;
}
El tipo_de_retorno representa el tipo de dato del valor que devuelve la función. Este tipo
debe ser uno de los tipos simples de C, un puntero a un tipo de C o bien un tipo struct. De
forma predeterminada, se considera que toda función devuelve un tipo entero (int). En otras
palabras, si en la declaración o en la definición de una función no se especifica el
tipo_de_retorno, el compilador asume que devuelve un valor de tipo int.
Se recomienda que el nombre de la función esté relacionado con la tarea que lleva a cabo.
En caso de que la función utilice parámetros, éstos deben estar listados entre paréntesis a
continuación del nombre de la función, especificando el tipo de dato y el nombre de cada
parámetro. En caso de que una función no utilice parámetros, se pueden dejar los paréntesis
vacíos o incluir la palabra void, que indica que la función no utiliza parámetros. Después
del encabezado de la función, debe aparecer, delimitado por llaves ({ y }), el cuerpo de la
función compuesto por las sentencias que llevan a cabo la tarea específica de la función.
Veamos la definición de la función cubo declarada
en el apartado anterior:
Es muy importante hacer notar que la definición de la función de la puede realizar después
de la función principal ‘main’, en este caso es necesario la declaración del prototipo o
simplemente antes de la función ‘main’.
#include <stdio.h>
main()
{
int numero;
for(numero=1; numero<=5; numero++)
{
printf("El cubo del número %d es %d\n", numero, cubo(numero));
}
return 0;
}
int cubo(int base)
{
int potencia;
potencia = base * base * base;
return potencia;
}
#include <stdio.h>
main()
{
int numero;
for(numero=1; numero<=5; numero++)
{
printf("El cubo del número %d es %d\n", numero, cubo(numero));
}
return 0;
}
Para que una función realice la tarea para la cual fue creada, debemos acceder o llamar a la
misma. Cuando se llama a una función dentro de una expresión, el control del programa se
pasa a ésta y sólo regresa a la siguiente expresión de la que ha realizado la llamada cuando
encuentra una instrucción return o, en su defecto, la llave de cierre al final de la función.
Generalmente, se suele llamar a las funciones desde la función main, lo que no implica que
dentro de una función se pueda acceder a otra función.
Cuando queremos acceder a una función, debemos hacerlo mediante su nombre seguido de
la lista de argumentos que utiliza dicha función encerrados entre paréntesis. En caso de que
la función a la que se quiere acceder no utilice argumentos, se deben colocar los paréntesis
vacíos.
Cualquier expresión puede contener una llamada a una función. Esta llamada puede ser
parte de una expresión simple, como una asignación, o puede ser uno de los operandos de
una expresión más compleja. Por ejemplo:
a = cubo(2);
calculo = b + c / cubo(3);
Debemos recordar que los argumentos que utilizamos en la llamada a una función
sedenominan argumentos reales o parámetros actuales. Estos argumentos deben
coincidir en el número y tipo con los argumentos formales o parámetros de la función.
M.Cs. Ing. Julio Cesar Bermudez Vargas Página 11
SIS 2210 “METODOLOGIA DE LA PROGRAMACION II
No olvidemos que los argumentos formales son los que se utilizan en la definición y/o
declaración de una función.
Los argumentos reales pueden ser variables, constantes o incluso expresiones más
complejas.
El valor de cada argumento real en la llamada a una función se transfiere a dicha función y
se le asigna al argumento formal correspondiente.
Generalmente, cuando una función devuelve un valor, la llamada a la función suele estar
dentro de una expresión de asignación, como operando de una expresión compleja o como
argumento real de otra función. Sin embargo, cuando la función no devuelve ningún valor,
la llamada a la función suele aparecer sola. Veamos un ejemplo:
z = potencia( a, b);
imprime_valores (x, y, z);
Cuando declaramos variables dentro de la función principal del programa, es decir, dentro
de la función main, están únicamente asociadas a esta función, en otras palabras, son
variables locales de la función main y no se puede acceder a ellas a través de ninguna otra
función.
Al igual que sucede con las variables declaradas dentro de la función main, cualquier
variable que declaremos dentro de una función, es local a esa función, es decir, su ámbito
esta confinado a dicha función. Esta situación permite que existan variables con el mismo
nombre en diferentes funciones y que no mantengan ninguna relación entre sí.
Debemos tener en cuenta que cualquier variable declarada dentro de una función se
considera como una variable automática (auto) a menos que utilicemos algún modificador
de tipo.
#include <stdio.h>
void imprimeValor()
{
int contador = 5; // variable local
printf("El valor de contador es: %d\n", contador);
}
main()
{
int contador = 0; //variable local
M.Cs. Ing. Julio Cesar Bermudez Vargas Página 12
SIS 2210 “METODOLOGIA DE LA PROGRAMACION II
contador++;
printf("El valor de contador es: %d\n", contador);
imprimeValor();
printf("Ahora el valor de contador es: %d\n", contador);
return 0;
}
A diferencia de las variables locales cuyo ámbito estaba confinado a la función donde
estaban declaradas, el ámbito de las variables globales se extiende desde el punto en el que
se definen hasta el final del programa. En otras palabras, si definimos una variable al
principio del programa, cualquier función que forme parte de éste podrá utilizarla
simplemente haciendo uso de su nombre.
#include <stdio.h>
void unaFuncion()
{
printf("En la función unaFuncion, variable es: %d\n", variable);
}
void otraFuncion()
{
variable++;
printf("En la función otraFuncion, variable es: %d\n",variable);
}
main()
{
variable = 9;
printf("El valor de variable es: %d\n", variable);
unaFuncion();
otraFuncion();
printf("Ahora el valor de variable es: %d\n", variable);
return 0;
}
En C++ todos los argumentos que se pasan a una función se pasan por valor. En otras
palabras, se pasa una copia del valor del argumento y no el argumento en sí (por ello, este
procedimiento se conoce en algunas ocasiones como paso por copia). Al pasar una copia
del argumento original a la función, cualquier modificación que se realice sobre esta copia
no tendrá efecto sobre el argumento original utilizado en la llamada de la función. Se puede
considerar un argumento pasado por valor como una variable local de la función a la que se
ha pasado, de tal modo que los cambios que se realicen sobre ésta tendrán efecto sólo
dentro de la función.
#include <stdio.h>
main()
{
int i = 1;
printf("\ni=%d antes de llamar a la función modificar", i);
modificar(i);
printf("\ni=%d después de llamar a la función modificar", i);
}
Dado que lo que se pasa a la función modificar es una copia de la variable i, el valor de ésta
en la función main no se ve alterado cuando dentro de la función modificar se cambia el
valor de variable. De ahí, la salida del ejemplo anterior es la siguiente:
Como ya hemos visto, cuando se pasa un argumento por valor, realmente se pasa una copia
de éste, y si esta copia se modifica el argumento original no se ve alterado. Sin embargo, en
muchas ocasiones lo que queremos es que una función cambie los valores de los
argumentos que le pasamos. Para lograrlo se utiliza lo que se conoce como paso de
parámetros por referencia. En estos casos, no se pasa una copia del argumento, sino el
argumento mismo.
Como hemos visto en la unidad anterior, esta operación se logra mediante la utilización del
operador de dirección ‘&’. De este modo, cuando llamamos a una función, lo que realmente
le pasamos son punteros a los argumentos que deseamos modificar. Veamos el ejemplo
anterior utilizando el paso de parámetros por referencia:
#include <stdio.h>
main()
{
int i = 1;
printf("\ni=%d antes de llamar a la función modificar", i);
modificar(i);
printf("\ni=%d después de llamar a la función modificar", i);
return 0;
}
void modificar(int &variable)
{
printf("\nvariable = %d dentro de modificar", variable);
variable = 9;
printf("\nvariable = %d dentro de modificar", variable);
}
Las funciones en C++ pueden ser recursivas, en otras palabras, pueden llamarse a sí
mismas directa o indirectamente.
La recursividad directa es el proceso mediante el que una función se llama a sí misma desde
el propio cuerpo de la función, mientras que la recursividad indirecta implica más de una
función.
Un proceso recursivo tiene que tener una condición de finalización, ya que de lo contrario
podría continuar infinitamente.
En principio, la solución a este problema podría realizarse sin tener que utilizar la
recursividad con el siguiente programa:
#include <stdio.h>
main()
{
int valor = 4;
int resultado;
resultado = factorial(valor);
printf("El factorial de %d es %d \n", valor, resultado);
return 0;
}
Sin embargo, resulta más intuitivo dada la definición de número factorial utilizar una
función recursiva como la siguiente:
Veamos un ejemplo:
Llamada # 1:
numero = 4
numero != 1 entonces ejecutamos la siguiente sentencia
return ( 4 * (realizamos la segunda llamada))
Llamada # 2:
numero = 3
numero != 1 entonces ejecutamos la siguiente sentencia
return ( 3 * (realizamos la tercera llamada))
Llamada # 3:
numero = 2
numero != 1 entonces ejecutamos la siguiente sentencia
return ( 2 * (realizamos la cuarta llamada))
Llamada # 4:
numero = 1
numero == 1 entonces se ejecuta la sentencia del if:
return 1
Fin Llamada # 4 -> DEVUELVE 1
return ( 2 * 1)
Fin Llamada # 3 -> DEVUELVE 2
return ( 3 * 2)
Fin Llamada # 2 -> DEVUELVE 6
return ( 4 * 6)
Fin Llamada #1 -> DEVUELVE 24
Caso básico:
Si numero=1 entoces factorial(1)=1
Proceso recursivo
si numero>1 entonces factorial(numero)= numero *factorial(numero-1)
CAPITULO 2.
definir POO como una técnica o estilo de programación que utiliza objetos como bloque
esencial de construcción.
Un objeto es una unidad que contiene datos y las funciones que operan sobre esos datos. A
los elementos de un objeto se les conoce como miembros; las funciones que operan sobre
los objetos se denominan métodos y los datos se denominan miembros datos.
Al principio se empezaron a hacer lenguajes de alto nivel, orientados a procesos. Esa forma
de trabajar daba más importancia a los procesos que a los datos.
Entonces muchas personas se dieron cuenta de que los datos merecían mayor atención y así
aparecieron los lenguajes orientados a datos. Estos lenguajes tenían mucho cuidado con los
datos, pero los procesos eran muy sencillos o casi inexistentes. El problema era que cuando
había que cambiar la forma de los datos era necesario revisar todas las estructuras
anteriores.
Los objetos y operaciones del mundo real se podían modelar mediante datos y estructuras
de control separadamente. En esta etapa el diseño del software se enfoca sobre la
representación del detalle procedimental y en función del lenguaje elegido. Conceptos
como: refinamiento progresivo, modularidad procedimientos y programación estructurada
son conceptos básicos que se utilizan en esta etapa. Existe mayor abstracción de datos.
Etapa 3.
Etapa 4.
A partir de los años setenta se trabaja sobre una nueva clase de lenguajes de simulación y
sobre la construcción de prototipos tales como Simula-70 y basado en parte de éste, el
Smalltalk. En estos lenguajes, la abstracción de datos tiene una gran importancia y los
problemas del mundo real se representan mediante objetos de datos a los cuales se les
añade el correspondiente conjunto de operaciones asociados a ellos. Términos como
Abstracción de datos, objeto, encapsulación entre otros, son conceptos básicos sobre la que
se fundamenta la POO.
Encapsulación: proceso por el que se ocultan los detalles del soporte de las características
esenciales de una abstracción.
Objeto:
Para poder describir todos los objetos de un programa, conviene agrupar éstos en clases.
Clase:
Podemos considerar una clase como una colección de objetos que poseen
características y operaciones comunes. Una clase contiene toda la información
necesaria para crear nuevos objetos.
Encapsulación:
Es una técnica que permite localizar y ocultar los detalles de un objeto. La
encapsulación previene que un objeto sea manipulado por operaciones distintas de
las definidas. La encapsulación es como una caja negra que esconde los datos y
solamente permite acceder a ellos de forma controlada.
Abstracción:
En el sentido mas general, una abstracción es una representación concisa de una
idea o de un objeto complicado. En un sentido mas especifico, la abstracción
localiza y oculta los detalles de un modelo o diseño para generar y manipular
objetos.
Una abstracción tiene un significado más general que la encapsulación, pudiendo hablar de
abstracción de datos en lugar de encapsulación de datos.
Una clase es un tipo: Un objeto es una instancia de ese tipo. Además, la clase es un
concepto estático: una clase es un elemento reconocible en el texto del programa.
Objetos: Un objeto es una entidad lógica que contiene datos y un código que manipula
estos datos; el enlazado de código y de datos, de esta manera suele denominarse
encapsulación.
Polimorfismo: Significa que un nombre se puede utilizar para especificar una clase
genérica de acciones.
Objeto: Un objeto es una entidad abstracta que tiene las características de un objeto real.
Los objetos se crean y eliminan durante la ejecución del programa, además interactúan con
otros objetos. Los objetos son construcciones de programación que se obtienen a partir de
entidades llamadas clases. La definición de una clase se conoce como instanciación de
clases.
Para crear un objeto, es preciso definir primero su forma general utilizando la palabra
reservada class. Una class es parecida a una estructura, es un tipo definido por el usuario
que determina las estructuras de datos y las operaciones asociadas con este tipo.
Las clases son como plantillas o modelos que describen como se construyen ciertos tipos de
objetos, cada vez que se construye un objeto de una clase se crea una instancia de esa clase,
por consiguiente; los objetos son instancias de clases.
Una clase es una colección de objetos similares y un objeto es una instancia de una
definición de una clase; una clase puede tener muchas instancias y cada una es un objeto
independiente.
Una clase es simplemente un modelo que se utiliza para describir uno o mas objetos del
mismo tipo.
Así, por ejemplo sea una clase ventana, un tipo de dato, que contenga los miembros dato:
posx, posy
tipo_ventana
tipo_borde
color_ventana
mover_horizontal
mover_vertical
Un objeto de la clase ventana, es una ventana concreta (una instancia de la clase) cuyos
datos tienen por valores:
Posx x
posy y
tipo_ventana desplegable
tipo_borde linea doble
color_ventana amarillo
Las clases son estructuras que contienen no sólo declaraciones de datos, sino también
declaraciones de funciones. Las funciones se conocen como funciones miembro, e indican
qué tipos de cosas puede hacer una clase. La palabra reservada class introduce una
declaración de clase.
class Nombre_de_la_clase{
private:
datos y funciones privados
public:
datos y funciones publicas
}lista de objetos;
Una clase puede contener tanto partes públicas como partes privadas, por defecto, todos los
elementos que se definen en la clase son privados; esto significa que no pueden acceder a
ellas ninguna función que no sea miembro de la clase.
Class Counter{
long count; // variable privada , variable miembro de la clase
Public:
void SetValue(long); // Funciones públicas, funciones miembro de la
clase
long GetValue();
};
La variable long count, no está disponible o no se puede usar por otras funciones que no
están declaradas en la clase, por lo que tratar de hacer esto es erróneo:
void main()
{
count = 3.111;
}
Una clase puede tener tantas variables como necesite. Estas pueden ser de cualquier tipo,
incluyendo otras clases, apuntadores a objetos de clases e incluso apuntadores a objetos
dinámicamente asignados.
long Counter::GetValue()
{
return count;
}
Ya que se ha definido la clase, se debe definir un objeto con ella. Las variables de una clase
se definen de igual manera que como se definen las variables de tipo estructura.
Para el ejemplo de la clase anterior, si quiere declarar un objeto Gente de tipo Counter, lo
podría hacer asi:
Class Counter
{
.
.
public:
.
.
}Gente;
Counter Gente;
Una instanciación es simplemente una instancia de una clase en la forma de una variable
específica.
void main()
{
Counter Gente; // Declaración de un objeto
Gente.SetValue(1000); // Invocación a función miembro de Counter
long value = GetValue(); // Invocación a función miembro de Counter
}
La iniciación se tiene que hacer a través de sus funciones miembro, por lo que hacer lo
siguiente sería un error.
void main()
{
Counter Gente;
Gente = 1000; // error, la variable no está disponible en la función main()
long value = GetValue();
}
En otro ejemplo, ésta clase define un tipo llamado cola, que se utiliza para crear un objeto
de tipo cola.
# include <iostream.h>
class cola
{
int c[100];
int posfin, posprin;
public:
void iniciar(void);
void ponent(int i);
int quitaent(void);
};
Cuando llega el momento de codificar realmente una función que es miembro de una clase,
es preciso decir al compilador a que clase pertenece la función, calificando el nombre de la
función con el nombre de la clase del cual es miembro. p.e.
Para llamar a una función miembro desde una parte del programa que no sea parte de la
clase, se debe utilizar el nombre del objeto y el operador punto. p.e.
# include <iostream.h>
class cola
{
int c[100];
int posfin, posprin;
public:
void iniciar(void);
void ponent(int i);
int quitaent(void);
};
main(void)
{
cola a, b;
a.iniciar();
b.iniciar();
a.ponent(15);
b.ponent(39);
a.ponent(55);
b.ponent(19);
cout<<a.quitaent() << " ";
cout<<b.quitaent() << " ";
cout<<a.quitaent() << " ";
cout<<b.quitaent() << " ";
return 0;
}
void cola::iniciar()
{
posprin=posfin=0;
}
void cola::ponent(int i)
{
if(posfin==0)
{
cout<<"la cola esta llena ";
return;
}
posfin++;
c[posfin] = i;
}
int cola::quitaent(void)
{
if(posfin==posprin)
{
cout<<"la cola está vacía";
return 0;
}
posprin++;
return c[posprin];
}
La tarea de una clase consiste en ocultar la mayor cantidad de información posible. Por lo
tanto es necesario imponer ciertas restricciones a la forma en que se puede manipular una
clase. Existen 3 tipos de usuario de una clase:
Cada tipo de usuarios tiene privilegios de acceso asociados a una palabra clave:
1.- Private.
2.- Public.
3.- Protected.
Ejemplo:
class controlAcceso
{
int a;
public:
int b;
int fi(int a);
protected:
int c;
float C1(float t);
};
Cualquier declaración que aparezca antes de cualquiera de las tres palabras clave, por
default es private; así, int a; es private.
Los miembros de la clase private tienen el mas estricto control de acceso. Solo la clase
misma puede tener acceso a un miembro private. En este ejemplo nadie puede usar la clase
ya que todo es private.
Class Privada
{
long valor;
void F1();
void F2();
};
void main()
{
privada objeto1; // Se crea objeto1 de clase privada.
long L = &objeto.valor; // acceso no valido por ser private.
objeto1.F1(); // acceso no valido por ser private.
objeto1.F2(); // acceso no valido por ser private.
}
Para poder tener acceso necesitaría que las funciones miembro fueran declaradas en la
sección public.
Para utilizar un objeto de una clase, usted debe tener acceso a datos miembro, a funciones
miembro o a ambos. Para hacer que algunos datos o funciones sean accesibles, se declaran
en la sección public.
class Ej_public
{
public:
int variable;
void función1();
};
void Ej_public::función1(){}
void main()
{
Ej_public Objeto2;
int i = Objeto2.variable;
Objeto2.función1();
}
Cualquier cosa que se declara en la sección public, hace posible el acceso ilimitado a
cualquier persona.
Cuando se define una clase que se utiliza subsiguientemente como clase de base para otras
clases, se puede hacer que los miembros estén accesibles solo para funciones de las clases
derivadas mediante el uso de la palabra clave protected.
class A
{
Protected:
int valor_A;
};
class B
{
public:
void funB();
};
class C
{
public:
void funC();
};
void funB()
{
valor_A = 0;
}
void funC()
{
valor_A = 1000;
}
Los miembros de datos pueden ser también apuntadores. Los apuntadores no se pueden
inicializar dentro de la declaración de una clase. Si un miembro de datos apunta a un tipo de
clase, el apuntador no se iniciliza, ni las construcciones son llamadas de manera automática.
Ejemplo.
class Segunda
{
int id;
public:
Primera* Object;
Segunda();
Int getnom() { return id; }
};
Aquí, el miembro se declara para apuntar a un elemento de otra clase llamada primera. En
la construcción de la clase segunda, el espacio de almacenamiento se asigna al objeto
apuntador, pero el apuntador se deja sin inicializar.
Las clases no son objetos, pero aveces puede utilizarlas como si lo fueran. Un ejemplo es la
declaración de un apuntador a un miembro de clase.
class Ejemplo
{
public:
int valor;
int identificador;
};
void main()
{
Ejemplo Objeto1;
Ejemplo Objeto2;
SetValue(Objeto1);
SetValue(Objeto2);
}
Esta instrucción declara la variable ip que apunta a un valor de miembro de datos int en un
objeto de clase Ejemplo sin indicar un objeto especifico.
2.6. CONSTRUCTORES.
Un constructor es una función especial que es miembro de esa clase y que tiene el mismo
nombre de la clase.
Es muy frecuente que una cierta parte de un objeto necesite una iniciación antes de que
pueda ser utilizada; como el requisito de iniciación es tan frecuente C++ permite que los
objetos se den a sí mismos valores iniciales cuando se crean. Esta iniciación
automáticamente se lleva a cabo mediante el uso de una función de construcción o
constructor.
Por ejemplo este es el aspecto que tiene la clase cola cuando se modifica para utilizar las
iniciaciones:
# include <iostream.h>
class cola
{
int c[100];
int posfin, posprin;
public:
cola(void); // este es el constructor de la clase cola
void ponent(int i);
int quitaent(void);
};
M.Cs. Ing. Julio Cesar Bermudez Vargas Página 34
SIS 2210 “METODOLOGIA DE LA PROGRAMACION II
cola::cola(void)
{
posfin=posprin=0;
cout<<"la cola ya tiene valores iniciales \n";
}
Como lo dice el nombre, un constructor es una función que se utiliza para construir un
objeto de una clase dada; esto puede implicar la presencia de diferentes escenarios.
Cada uno de estos procesos implica un tipo diferente de constructor. Un constructor tiene el
nombre de la clase a la que pertenece.
Un sub objeto es un objeto de clase que se declara dentro de otra clase. Cuando se tiene una
instancia en una clase, su constructor debe crear un objeto de esa clase. Si la clase tiene sub
objetos declarados en ella, el constructor tiene que invocar los constructores de estos
objetos. Considere el ejemplo siguiente.
class counter
{
int value;
public:
Counter() { value = 0; }
};
class Example
{
int value;
public:
Counter cars;
Example() { value = 0; }
};
void main()
{
Example e;
}
Un constructor private, impediría que los usuarios genéricos crearan objetos a partir de esa
clase y forzarán el cumplimiento de una de las condiciones siguientes antes de que se pueda
crear un objeto.
Counter(long);
Counter::Counter(long value)
{
count = value;
}
void main()
{
Counter object(5);
}
Observe los paréntesis después del nombre de la variable, que hacen que la definición del
objeto se asemeje a una llamada a función. La definición del objeto es en realidad una
llamada a función con argumentos.
Suponga que desea crear una clase counter que sea lo suficientemente flexible para aceptar
cualquier tipo de inicialización, utilizando elementos float, long, int, cadena o incluso
ningún argumento. Estas son las construcciones que se deben declarar.
class Counter
{
public:
Counter(int = 0);
Counter(long);
Counter(double);
Counter(char *);
};
// declaración de constructores.
Counter::Counter(long val_inic)
{
count = val_inic;
}
Counter::Counter(double val_inic)
{
count = val_inic;
}
Counter::Counter(char* val_inic)
{
count = atol(val_inic);
}
class Counter
{
.
.
.
public:
Counter(Counter&);
.
.
};
Counter::Counter(Counter &referencia)
{
count = referencia.count;
}
void main()
{
Counter Object(5); // Constructor entero
Counter Object1 = Object; // Constructor de copia
}
2.7 DESTRUCTORES.
Los destructores entran en la misma categoría que los constructores. Se utilizan para
realizar ciertas operaciones que son necesarias cuando ya no se utiliza un objeto como es la
liberación de memoria.
Se podría escribir una clase que se encargue de manejar todas las gráficas generadas por un
programa de la siguiente manera:
class Graphics
{
public:
Graphics();
~Graphics();
void DrawCircle(int x, int y, int radio);
void DrawDot(int x, int y);
};
Por ejemplo vea la clase cola con su constructor y destructor (en el ejemplo de la clase cola
no es necesario un destructor, pero en este caso se pone para ejemplificar su uso).
# include <iostream.h>
class cola
{
int c[100];
int posfin, posprin;
public:
cola(void); // este es el constructor de la clase cola
~cola(void); // este es el destructor de la clase cola
void ponent(int i);
int quitaent(void);
};
// Función de Construcción
cola::cola(void)
{
posfin=0;
posprin=0;
}
// Función de destrucción.
cola::~cola(void)
{
cout<<"la cola ha sido destruida \n";
}
Veamos como funcionan los constructores y destructores en la nueva versión del programa
que crea una cola.
# include <iostream.h>
class cola
{
int c[100];
int posfin, posprin;
public:
cola(void); // este es el constructor de la clase cola
~cola(void); // este es el destructor de la clase cola
void ponent(int i);
int quitaent(void);
};
main(void)
{
cola a, b;
a.ponent(15);
b.ponent(39);
a.ponent(55);
b.ponent(19);
cout<<a.quitaent() << " ";
cout<<b.quitaent() << " ";
cout<<a.quitaent() << " ";
cout<<b.quitaent() << " ";
return 0;
}
// Función de Construcción
cola::cola(void)
{
posfin=0;
posprin=0;
cout<<"La cola ya tiene valores iniciales \n"
}
// Función de destrucción.
cola::~cola(void)
{
cout<<"La cola ha sido destruida \n";
}
void cola::ponent(int i)
{
if(posfin>=100)
{
cout<<"la cola esta llena ";
return;
}
posfin++;
c[posfin] = i;
}
int cola::quitaent(void)
{
if(posfin==posprin)
{
cout<<"la cola está vacía";
return 0;
}
posprin++;
return c[posprin];
}
A veces se necesitan 2 clases que son tan conceptualmente cercanas que usted desearía que
una de ellas tuviera acceso irrestricto a los miembros de la otra. Considere la implantación
de una lista asociada: necesita una clase que represente nodos individuales, y una que se
encargue de la lista misma. El acceso a los miembros de la lista es a través del manejador
de la misma, pero el manejador debe tener acceso absoluto a los miembros de la clase o una
función. Una clase que no ha sido declarada aún se puede definir como friend de esta
manera:
class Node
{
friend class ObjectList;
int value;
Node* Predecesor;
Node* Sucesor;
public:
void value(int i)
{
value = i;
}
};
class ObjectList
{
Node* head;
Node* tail;
Node* current;
public:
void InsertNode(Node * ) {}
void DeleteNode(Node *) {}
int CurrentObject(Node* node) { return node->value;}
};
Es posible que una función de una clase que no sea un miembro tenga acceso a las partes
privadas de esa clase, declarando que se trata de un friend (amigo) de esa clase. Por
ejemplo amg() se declara como friend de la class C1
Class C1
{
.
.
.
public:
friend void amg(void);
.
.
.
};
Como se puede ver, la palabra reservada friend precede a toda la declaración de la función,
que es lo que se hace en general.
La razón por la cual se permite en C++ las funciones friend es la de resolver situaciones en
las cuales dos clases deban compartir una misma función, para así aumentar la eficiencia.
Para ver un ejemplo, consideremos un programa que defina dos clases llamadas linea y
recuadro. La clase linea contiene todos los datos y código necesarios para dibujar una línea
horizontal discontinua de cualquier longitud, empezando en la coordenada X, Y que se
indique y utilizando un color especificado, La clase recuadro contiene todo el código y los
datos necesarios para dibujar un recuadro en las coordenadas especificadas para la esquina
superior izquierda y para la esquina inferior derecha, y con el color que se indique. Las dos
clases tienen la misma función mismo_color() para determinar si una línea y un recuadro
están pintados del mismo color. Las clases se declaran según se muestra a continuación:
class linea;
class recuadro
{
int color;
int xsup, ysup;
int xinf, yinf;
public:
friend int mismo_color(linea l, recuadro b);
void pon_color(int c);
void definir_recuadro(int x1, int y1, int x2, int y2);
void mostrar_recuadro(void);
};
class linea
{
int color;
int xinicial, yinicial;
int longitud;
public:
friend int mismo_color(linea l, recuadro b);
void pon_color(int c);
void definir_linea(int x, int y, int l);
void mostrar_linea();
};
También puede declarar una función no miembro como friend antes que el identificador de
la función esté en el campo de acción. Por ejemplo.
class Node
{
friend int GetObject(Node*);
int value;
Node* Predecesor;
Node* Sucesor;
public:
void value(int i){ value = i; }
};
int GetObject(Node* n)
{
return n->value;
}
Las funciones y clases declaradas friend para otras clases gozan de privilegios especiales.
Si la función FUN0() es un elemento friend de la clase B y la clase B se deriva de la clase
A. FUN0(), tiene acceso también a los miembros de datos de las clases A, B, y C:
class A
{
friend class FRIEND;
int a1;
protected:
int a2;
public:
int a3;
};
class B
{
int b1;
protected:
friend class FRIEND;
int b2;
public:
int a3;
};
class C
{
int c1;
protected:
int c2;
public:
friend class FRIEND;
int c3;
};
2.9 HERENCIA
Class Figura{
.
.
public:
.
.
};
Aquí acceso es opcional, sin embargo, si está presente tiene que ser public, private o
protected.
El uso de public significa que todos los elementos public del antecesor también serán public
para la clase que lo hereda.
El siguiente ejemplo muestra 2 clases donde la segunda de ellas hereda las propiedades de
la primera.
class Box
{
public:
int width, height;
void SetWidth(int w) { width = w; }
void SetHeight(int h) { height = h; }
};
La clase Box recibe el nombre de clase base de la clase ColoredBox. Que a su vez recibe el
nombre de clase derivada. La clase Colored Box se declara solo con una función, pero
también hereda 2 funciones y 2 variables de su clase base. Así, se puede crear el código
siguiente:
void main()
{
Cb.Setcolor(5); // función miembro de ColoredBox.
Cb.SetWidth(30); // función heredada.
Cb.setHeight(50); // función heredada.
}
Observe como las funciones heredadas se utilizan exactamente como si fueran miembro.
Cómo y cuándo se deriva una clase de otra es puramente decisión del programador. Esto
puede parecer obvio, pero es una limitación. El diseñador de un programa debe decidir al
momento de la compilación quien hereda qué, de quién, cómo y cuándo se lleva a cabo la
herencia.
Tal y como en la vida real, en C++ no todo se puede transmitir a través de la herencia. Esto
se puede considerar en un principio como una desventaja o limitación, pero en realidad solo
algunos casos especiales inconsistentes por definición con la herencia:
1.- Constructores.
2.- Destructores.
3.- Nuevos operadores definidos por el usuario.
4.- Relaciones friend.
El constructor de una clase de base no puede ser invocado de manera explícita en una clase
derivada como otras funciones heredadas. Considere el código siguiente:
class Parent
{
int value;
public:
Continua…
Parent(){ value = 0; }
Parent(int v){ value = 0; }
};
void Child::SetTotal(int t)
{
Parent::Parent(i); // Esto no se puede hacer, ya que el constructor de la
// clase no es heredado como otras funciones.
Total = t;
}
De manera análoga, los destructores están diseñados para ser invocados automáticamente
cuando un objeto sale del campo de acción.
La relación friend no es heredada. Esto es similar a la vida real; los amigos de sus padres no
son automáticamente amigos suyos.
Una clase puede heredar los atributos de dos o más clases. Para lograr esto, se utiliza una
lista de herencia separada mediante comas en la lista de clases base de la clase derivada. La
forma General es:
# include <iostream.h>
class X
{
protected:
int a;
public:
void hacer_a(int i);
};
class Y
{
protected:
int b;
public:
void hacer_b(int i);
};
main(void)
{
Z var;
var.hacer_a(10);
var.hacer_b(25);
cout << var.hacer_ab();
return 0;
}
En este ejemplo, Z tiene acceso a las partes public y protected tanto de X como de Y.
Remarcando, una clase puede tener muchos padres y heredar propiedades de cada una de
sus clases base. Considere crear una clase MesaRedonda, que no solo tenga las propiedades
de las mesas, sino también la característica geométrica de ser redonda.
#include <stdio.h>
class Circle
{
float radio;
public:
Circle(float r){ radio = r; }
Float Area(){ return radio*radio*3.1416; }
};
class Mesa
{
float height;
public:
Mesa(float h) { height = h; }
float Height() { return height; }
};
void main()
{
MesaRedonda Mesa1(15.0, 3.0, 5);
printf("\n Las propiedades de la Mesa son:");
printf("\n Altura = %f ", Mesa.Height());
printf("\n Area = %f ", Mesa.Area());
printf("\n Color = %d ", Mesa.Color());
}
Las clases de base virtual se utilizan sólo en el contexto de la herencia múltiple. Dada la
complejidad de relaciones que pueden surgir en un árbol de herencia construido en torno a
la herencia múltiple, existen situaciones en las que el programador necesita tener cierto
nivel de control sobre la forma en que se heredan las clases de base. Considere el árbol de
herencia de la siguiente figura.
La clase D tiene a A como clase de base. El problema es que hay dos clases A diferentes
que aparecen como clases de base de D, cada una con datos propios.
class A
{
public:
int value;
};
Tener múltiples copias de la misma clase de base en árbol de herencia no sólo es confuso,
sino que puede ser un desperdicio de espacio de almacenamiento.
Declarar una base virtual resuelve el problema. Obliga al compilador a admitir sólo una
copia de la clase de base dada en la declaración de una clase derivada. Por lo tanto el
ejemplo anterior lo podemos corregir de la siguiente manera:
2.10 SOBRECARGA.
Una de las maneras que tiene el C++ de llegar al polimorfismo es a través de la sobrecarga
de funciones. En C++ , dos o mas funciones pueden compartir un nombre, siempre y
cuando en la declaración, sus parámetros sean diferentes.
a = b;
if( a = b)
La sobrecarga otorga flexibilidad, permite a las personas utilizar código con menos
esfuerzo, ya que extiende operaciones que son conceptualmente similares en naturaleza.
El tipo de retorno no se utiliza para distinguir funciones sobrecargadas, por lo tanto las
funciones:
Cualquier nombre de función puede ser sobrecargada en C++, pero la sobrecarga solo
funciona dentro de un campo de acción dado.
Cuando se declara en una clase más de una función miembro con el mismo nombre, se dice
que el nombre de la función está sobrecargado en esa clase, y su campo de acción será en el
ámbito de esa clase.
class Ejemplo
{
int value;
public:
void value(int v) { value = v; }
int value() { return value; }
};
void main()
{
Ejemplo Ee;
Ee.value(3);
Int i = Ee.value();
}
Este código demuestra que la clase Ejemplo tiene 2 funciones sobrecargadas: una función
para escribir y una para leer una variable.
Las funciones sobrecargadas necesitan diferir en una u otra o en las dos formas siguientes:
# include<iostream.h>
int al_cuadrado(int i);
double al_cuadrado(double d);
long al_cuadrado(long l);
main(void)
{
cout << al_cuadrado(10) <<" \n";
cout << al_cuadrado(1.25) <<" \n";
int al_cuadrado(int i)
{
cout<<" función al_cuadrado con parámetro entero"
return i*i;
}
double al_cuadrado(double d)
{
cout<<" función al_cuadrado con parámetro doble"
return d*d;
}
long al_cuadrado(long l)
{
cout<<" función al_cuadrado con parámetro largo"
return l*l;
}
Así, al_cuadrado(), representa la acción general que se realiza; el programador solo necesita
recordar la acción general que se lleva a cabo, por lo tanto al aplicar el polimorfismo se han
reducido a una las 3 cosas que había que recordar. Aunque este ejemplo es bastante trivial,
si expande el concepto se puede ver que el polimorfismo puede ayudarnos a entender
programas muy complejos.
Para sobrecargar la función de construcción de una clase, solo hay que declarar las
diferentes formas que tiene que adoptar y hay que definir su acción con respecto a esas
formas. Por ejemplo el programa siguiente declara una clase llamada temporizador, que se
comporta como un temporizador descendente. Cuando se crea un objeto del tipo
temporizador, se le da un valor inicial de la hora. Cuando se invoca a la función ejecutar(),
el temporizador cuenta hasta llegar a cero, y hace sonar el timbre. En ese ejemplo, se ha
sobrecargado el constructor para especificar la hora como un entero, como una cadena, o
como dos enteros que especifican los minutos y los segundos.
# include <iostream.h>
# include <stdlib.h>
# include <time.h>
class temporizador
{
int segundos;
public:
// Se especifican los segundos como una cadena.
temporizador(char *t) {segundos = atoi(t);}
// Se especifican los segundos como un entero
temporizador(int t) {segundos = t;}
// Se especifica la hora en minutos y segundos
temporizador(int min, int seg) {segundos = min* 60 + seg; }
void ejecutar(void);
};
void temporizador::ejecutar(void)
{
clock_t t1, t2;
t1 = t2 = clock()/CLK_TCK;
while(segundos)
{
if(t1/CLK_TCK+1 <= (t2=clock()) /CLK_TCK)
{
segundos --;
t1 = t2;
}
}
cout << "\a"; // toca el timbre
}
main(void)
{
temporizador a(10), b("20"), c(1, 10);
a.ejecutar(); // cuenta 10 segundos
b.ejecutar(); // cuenta 20 segundos
c.ejecutar(); //cuenta 1 minuto, 10 segundos
return 0;
}
Como se puede ver, cuando a, b, y c se crean dentro de main(), se les dan valores iniciales
utilizando los tres métodos diferentes que admiten las funciones de construcción
sobrecargadas.
Uno de los usos más comunes de la sobrecarga de funciones es con los constructores. La
razón es que cuando se instancía una clase, se deben conservar las cosas lo más flexibles
que sea posible; de modo que los usuarios pueden realizar diferentes clases de instancias.
class PopupWindow
{
Int x, y, Width, Height;
public:
PopupWindow();
PopupWindow(int, int);
PopupWindow(int, int, int, int);
PopupWindow(PopupWindow&);
};
PopupWindow:: PopupWindow()
{
x = y = 100;
Widht = Heigth = 100;
}
Con constructores sobrecargados, usted puede permitir que el usuario especifique qué
variables han de ser inicializadas de manera explícita y cuales deben asumir valores
definidos.
Lo que da lugar a una adición byte por byte de las estructuras b y c que se copiarán en la
estructura a. Esta sintaxis implica que el operador de adición está sobrecargado para
estructuras, aunque con ciertas reglas de apego a tipos. En el ejemplo anterior se utiliza
también un operador de asignación sobrecargado, ya que la instrucción y no una asignación
escalar definida, fue la que activó una operación de copia de estructuras.
Hay dos formas en las que se pueden implantar operadores para objetos de clase: como
funciones miembro y como amigos. Un operador unario aplicado a un objeto es equivalen
te a una llamada a una función. Dados un objeto W y un operador unario @ , la expresión
@W es equivalente a las llamadas a funciones:
Un operador binario aplicado a los objetos es equivalente también a una llamada a función.
El código anterior demuestra que los operadores pueden invocar dos funciones diferentes;
una función que es miembro y otra que es amigo.
Las funciones que implantan operadores son un tanto inusuales. Para comenzar, sus
nombres deben comenzar con la cadena operator, seguida de los caracteres que representan
el operador que se implanta. Por ejemplo la función miembro para implantar el operador de
adición tendría que llamarse operator+ . La segunda restricción se aplica al número de
argumentos que pueden tomar estas funciones. Las funciones miembro que implantan
operadores unarios no deben tomar argumentos, en tanto que las que implantan operadores
binarios pueden tomar solo un argumento.
class Counter
{
public:
int value;
Counter(int i)
{
value = i;
}
Counter operator!(); // operador unario.
Counter Counter::operator!()
{
return Counter( ! value );
}
void main()
{
Counter c1(3), c2(5); // se crean 2 objetos tipo counter.
c1 = ! c1; // se aplica el operador unario
c1 = c1 + c2; // se aplica el operador binario
}
class M
{
public:
int value;
M(int i) { value = i; }
M operator+(M& m);
M operator+(int i);
M operator+(double d);
};
M M::operator+(M& m)
{
return M(value + m.value);
}
M M::operator+(int i)
{
return M(value + i);
}
M M::operator+(double d)
{
returnM(value + d);
}
void main()
{
M m1(3), m2(10); // se crean dos objetos de clase M.
m1 = m1 + m2; // uso M::operator+(M&)
m1 = m2 + 200; // uso M::operator+(int)
m1 = m2 + 3.14159; // uso M::operator+(double)
}
class X
{
friend X operator+(X&, X&);
public:
int value;
X(int i) { value = i; }
X& operator = (X&);
};
X operator+(X& a, X& b)
{
return X(a.value + b.value);
}
void main()
{
X g(2), h(5), i(3);
G = h +h +i;
}
En general, los operadores friend sobrecargados se comportan de manera muy similar a las
funciones miembro.
2.11 POLIMORFISMO
El origen del término polimorfismo es simple: proviene de las palabras griegas poly
(muchos) y morphos (forma) multiforme. El polimorfismo describe la capacidad del código
C++ de comportarse de diferentes maneras dependiendo de situaciones que se presenten al
momento de la ejecución.
Lo que hace especiales a las funciones virtual es que cuando se accede a una de ellas
utilizando un puntero de clase base señala a un objeto de clase derivada, C++ determina
qué función debe llamar en el momento de la ejecución, basándose en el tipo del objeto al
cual apunta. Por tanto, si apunta a diferentes objetos, se ejecutan versiones diferentes de la
función virtual.
#include<iostream.h>
class Base
{
public:
virtual void quien() { cout << “Base \n”; }
};
main(void)
{
Base obj_base;
Base *p;
Primera_deriv obj_primera;
Segunda_deriv obj_segunda;
p = &obj_base;
p->quien();
p = &obj_primera;
p->quien();
p = &obj_segunda;
p->quien();
return 0;
}
Base
Primera derivación
Segunda derivación.
Parte de la clave para aplicar con éxito el polimorfismo consiste en comprender que la base
y las clase derivadas forman una jerarquía, que va desde la mayor generalización a la menor.
Por tanto la clase base cuando se utiliza correctamente, proporciona todos los elementos
que puede utilizar directamente una clase derivada, mas aquellas funciones que la clase
derivada debe implementar por sí misma. Sin embargo dado que la forma de la interface
está determinada por la clase base todas las clases derivadas van a compartir esa interface .
El código siguiente utiliza la clase figura para derivar dos clase concretas llamadas cuadro y
triángulo.
#include<iostream.h>
class Figura
{
protected:
double x, y:
public:
void pon_dim(double I, double j) { x = I; y = j; }
virtual void mostrar_area() { cout << “función no implementada \n”;}
};
main (void )
{
Figura *p;
triangulo trian;
cuadrado cuad;
p = &trian;
p->pon_dim(10.0, 5.0);
p->mostrar_area();
p = &cuad;
p->pon_dim(10.0, 5.0);
p->mostrar_area();
return 0;
}
#include<iostream>
class calcular
{
double radio,area,longitud;
public:
void iniciar(void);
void entradaDatos(void);
void salidaDatos(void);
} calcular1;
int main(void);
{
calcular calcular2;
calcular1.iniciar();
calcular2.iniciar();
calcular1.entradaDatos();
calcular2.entradaDatos();
calcular1.salidaDatos();
M.Cs. Ing. Julio Cesar Bermudez Vargas Página 65
SIS 2210 “METODOLOGIA DE LA PROGRAMACION II
calcular2.salidaDatos();
return 0;
}
void calcular::iniciar(void)
{
cout <<"PROGRAMA QUE CALCULA EL AREA Y LONGITUD DE UNA
CIRCUNFERENCIA"<<"\n\n";
}
void calcular::entradaDatos(void)
{
cout<<"INTRODUZCA EL RADIO DE LA CIRCUNFERENCIA"<<"\n";
cin>>radio;
area=pi*radio*radio;
longitud=2*pi*radio;
}
void calcular::salidaDatos(void)
{
cout<<"AREA =\t\t" <<area <<"\n";
cout<<"LONGITUD =\t\t" <<area <<"\n\n";
}
int getc();
int mcd();
int producto(int,int);
};
#include <iostream.h>
#include <d:/lab1sis2210/ejemplo.h>
ejemplo::ejemplo(int d1,int d2)
{
a=d1;
b=d2;
};
void ejemplo::leer()
{
cout<<"\n valor para la variable a:";
cin>>a;
cout<<"\n valor para la variable b:";
cin>>b;
}
void ejemplo::imprimir()
{
cout<<"\n valor para la variable a="<<a;
cout<<"\n valor para la variable b="<<b;
};
void ejemplo::seta(int d)
{
a=d;
}
void ejemplo::setb(int d)
{
b=d;
}
void ejemplo::setc(int d)
{
c=d;
}
int ejemplo::geta()
{
return a;
}
int ejemplo::getb()
{
return b;
}
int ejemplo::getc()
{
return c;
}
int ejemplo::mcd()
{
int n,m;
n=a;m=b;
while (n>0)
if (n>=m)
n=n-m;
else
m=m-n;
return m;
}
int ejemplo::producto(int x,int y)
{
int prod=0,cont=0;
while (cont<y)
{
prod=prod+x;
cont++;
};
return prod;
}
e.setc(dat);
cout<<"\n valor de la variable c:"<<e.getc();
cout<<"\n el producto es:"<<e.producto(e.geta(),e.getc());
return 0;
}
#include <d:/lab2sis2210/herencia.h>
#include <iostream.h>
void persona::leer()
{
cout<<"\n introducir C.I.:";
cin>>ci;
cout<<"\n introducir Nombre:";
cin>>nombre;
cout<<"\n introducir Edad:";
cin>>edad;
}
void persona::impre()
{
cout<<"\n C.I.:"<<ci;
cout<<"\n Nombre:"<<nombre;
cout<<"\n Edad:"<<edad;
}
void estudiante::leer()
{
persona::leer();
cout<<"\n Introducir carrera:";
cin>>carrera;
};
void estudiante::impre()
{
persona::impre();
cout<<"\n Carrera:"<<carrera;
}
void docente::leer()
{
persona::leer();
cout<<"\n Introducir Salario:";
cin>>salario;
};
void docente::impre()
{
persona::impre();
cout<<"\n Salario:"<<salario;
}
#include <d:/lab2sis2210/herencia.cpp>
#include <iostream.h>
#include <conio.h>
void main()
{
persona p;
//cout<<"\n tamaño de un entero :"<<sizeof(int);
/*p.leer();
p.impre();
estudiante e;
e.leer();
e.impre();
docente d;
d.leer();
d.impre();*/
persona *a[5];
int i,tipo,n;
cout<<"\n cantidad de personas:";
cin>>n;
for (i=0;i<n;i++)
{
cout<<"\n tipo : ESTUDIANTE = 0 , DOCENTE = 1 ?:";
cin>>tipo;
if (tipo==0)
a[i]=new estudiante;
else
a[i]=new docente;
a[i]->leer();
};
cout<<"\n contenido del vector:";
for (i=0;i<n;i++)
a[i]->impre();
}
CAPITULO 3
Simples: Los tipos simples son cuando cada dato representa un único elemento
Estáticos: Ocupan un tamaño de memoria fijo, que se tiene que definir antes de
declararlo.
arrays, cadena, registros, ficheros, conjuntos
3.2. ARREGLOS.
Para referenciar un elemento de un array se emplea el nombre del array y entre corchetes [ ]
el índice que determina la posición de ese elemento en el array.
Nombre_array[numero_celda]
Tipo_dato nombre_arreglo[tamaño]
Ejemplo:
int a[100];
float b[30];
char nom[45];
Esta forma de almacenamiento es muy frecuente cuando se dese que los datos
almacenados en el vector siempre sean los mismos al empezar la ejecución de la
aplicación.
Ejemplo:
int a[10]={4,8,3,9};
Los cuatro valores inicializan las primeras cuatro celdas del vector. Las
celdas restantes del vector que no son inicializadas toman el valor 0 por
defecto:
o Cada vez que se ejecuta la aplicación, los datos que se almacena en el vector
pueden ser o no los mismos.
Ejemplo:
for (i=0;i<10;i++)
a[i]= random(20);
Los datos que almacenan en el vector, se introducen por el usuario desde el teclado durante
la ejecución del programa.
Ejemplo:
for (i=0;i<10;i++)
cin>>a[i];
Una vez que los datos están almacenados en el vector, estos se encuentran disponibles para
el procesamiento durante toda la ejecución del programa.
Dónde:
a[], es el nombre del vector que contiene la clase.
n, es la cantidad de celdas que almacenaran datos.
leer(), es la función que almacena datos en las ‘n’ primeras celdas
del vector.
imprimir(), es la función que muestra el contenido de las n primeras
celdas del vector.
buscar(), es la función que verifica si un vlor ‘clave’ esta o no en el
vector.
class Tvector
{
//private:
public:
int a[50];
int n;
public:
Tvector(int);
void setn(int);
int getn();
int geta(int);
void seta(int,int);
void leer();
void imprimir();
int buscar(int);
//otras funciones de la clase vector
void qsort(int[],int,int);
};
#include <d:/lab3sis2210/vector.h>
#include <iostream.h>
#include <stdlib.h>
Tvector::Tvector(int t)
{
n=t;
}
void Tvector::setn(int t)
{
n=t;
}
int Tvector::getn()
{
return n;
}
void Tvector::seta(int dat,int cel)
{
a[cel]=dat;
}
int Tvector::geta(int cel)
{
return a[cel];
}
void Tvector::leer()
{
int i;
randomize();
for (i=0;i<n;i++)
a[i]=random(20)+1;
}
void Tvector::imprimir()
{
int i;
for (i=0;i<n;i++)
cout<<a[i]<<" , ";
cout<<"\n";
}
int Tvector::buscar(int dat)
{
//BUSQUEDA SECUENCIAL O LINEAL
int i=0;
while (i<n && dat!=a[i])
i++;
if (i==n)
return (-1);
else
return i;
}
Void Tvector::qsort(int x[],int p,int q)
{
if(p<q)
{
int piv=x[p];
int i=p,j=q+1;
while (i<j)
{
do {i++;} while (x[i]<=piv && i<=q);
do {j--;} while (x[j]>piv && j>=p);
if (i<j)
{
int aux=x[i];
x[i]=x[j];
x[j]=aux;
};
};
x[p]=x[j];
x[j]=piv;
qsort(x,p,j-1);
qsort(x,j+1,q);
};
}
Se debe desarrollar otro programa ‘.cpp’ donde se crea objetos a partir de la clase
Tvector y se emplea las funciones del objeto creado para resolver un problema
determinado. En el ejemplo que se desarrolla el programa puede ser ‘vecprub.cpp’.
#include <d:/lab3sis2210/vector.cpp>
#include <stdlib.h>
#include <iostream.h>
int cant, resp;
main()
{
Tvector v(cant);
v.leer();
v.imprimir();
cout<<"\n tamaño del segundo vector:";
cin>>cant;
Tvector k(cant);
k.leer();
k.imprimir();
// mostrar los elemento del vecto v que se encuentran en el vector k
Tvector r(20);
int i,j=0;
for (i=0;i<v.getn();i++)
if (k.verificar(v.geta(i))>=0)
{
r.seta(v.geta(i),j);
j++;
};
r.setn(j);
cout<<"\n resultado de los elementos comunes\n";
r.imprimir();
v.qsort(v.a,0,v.getn()-1);
v.imprimir();
return 0;
}
El programa anterior, crea dos objetos ‘v’ y ‘k’ partir de la clase Tvector,
almacena cantidades de datos en cada vector de los objetos en forma
aleatoria, por medio de la función ‘random’ y crea un tercer objerto ‘r’ en
cuyo vector se tiene los valores comunes de los vectores de los objetos ‘v’ y
‘k’.
Tipo_dato nombre_arreglo[tamaño_filas][tamaño_columnas];
Ejemplo:
int a[10][10];
float b[30][5];
char lista[10][45];
Esta forma de almacenamiento es muy frecuente cuando se dese que los datos
almacenados en el vector siempre sean los mismos al empezar la ejecución de la
aplicación.
Ejemplo:
int b[10][10]={{4,8,3,9},{4,2,8,5},{9,4,2,7}};
Los tres grupos de valores inicializan las primeras tres filas de la matriz. Las
celdas restantes de la matriz que no son inicializadas toman el valor 0 por
defecto:
Ejemplo:
En las 100 celdas del vector ‘b, se almacenan valores aleatorios generados
por medio de la función ‘random’.
Los datos que almacenan en el vector, se introducen por el usuario desde el teclado durante
la ejecución del programa.
Ejemplo:
Una vez que los datos están almacenados en el vector, estos se encuentran disponibles para
el procesamiento durante toda la ejecución del programa.
Donde:
a[][], es el nombre del vector que contiene la clase.
n y m, es la cantidad de filas y columnas de la matriz.
leer(), es la función que almacena datos en la matriz.
imprimir(), es la función que muestra el contenido de la matriz.
verificar(), es la función que verifica si un valor ‘clave’ está o no en
la matriz.
class matriz
{
public:
int a[10][10];
int n,m;
public:
matriz(int,int);
void seta(int,int,int);
void setn(int);
int geta(int,int);
int getn();
void setm(int);
int getm();
void leer();
void imprimir();
int verificar(int);
};
#include <iostream.h>
#include <stdlib.h>
#include <d:\sis2210a\matrizoo\matriz.h>
matriz::matriz(int f,int c) //function constructora
{
n=f;
m=c;
}
void matriz::seta(int valor,int f,int c)
{
a[f][c]=valor;
}
void matriz::setn(int f)
{
n=f;
}
int matriz::geta(int f,int c)
{
return a[f][c];
}
int matriz::getn()
{
return n;
};
void matriz::setm(int c)
{
m=c;
}
int matriz::getm()
{
return m;
}
void matriz::leer()
{
int i,j;
cout<<"\n almacenamiento de datos en la matriz";
//randomize();
for (i=0;i<n;i++)
for (j=0;j<m;j++)
{
// cout<<"\nvalor para la celda a["<<i<<"]["<<j<<"]=";
// cin>>a[i][j];
a[i][j]=random(30)+5;
};
}
void matriz::imprimir()
{
int i,j;
cout<<"\n contenido de datos en la matriz\n\n";
for (i=0;i<n;i++)
{
for (j=0;j<m;j++)
cout<<a[i][j]<<"\t";
cout<<"\n";
};
}
Se debe desarrollar otro programa ‘.cpp’ donde se crea objetos a partir de la clase
‘matriz’ y se emplea las funciones del objeto creado para resolver un problema
determinado. En el ejemplo que se desarrolla el programa puede ser ‘ejemmat.cpp’.
#include <iostream.h>
#include <d:\sis2210a\matrizoo\matriz.cpp>
int main()
{
//int p,q,r,s;
matriz m1(3,3);
m1.leer();
m1.imprimir();
return 0;
}
CAPITULO 4
ESTRUCTURAS Y UNIONES
4.1. ESTRUCTURAS.
Todas las variables que se ha utilizado en los temas anteriores han sido de un único tipo,
incluso los arrays son variables con todos los elementos del mismo tipo. La finalidad de
una estructura es agrupar una o más variables, generalmente de diferentes tipos, bajo un
mismo nombre para hacer más fácil su manejo.
DEFINICION:
Una estructura es un conjunto finito de variables de diferente tipo o no
referenciadas por un nombre único y que permiten almacenar el valor en
cada variable simultáneamente.
El diseño del esquema de la estructura es una tarea no obligatoria pero resulta importante
en función al grado de complejidad en el proceso de descomposición en atributos del objeto
de interés.
Ejemplo:
Para crear una estructura hay que definir un nuevo tipo de datos y declarar una variable de
este tipo. La declaración de un tipo estructura, incluye tanto los elementos que la componen
como sus tipos. Cada elemento de una estructura recibe el nombre de miembro (campo del
registro). La sintaxis es la siguiente:
Ejemplo:
struct persona
{
int ci;
char nombre[30];
char direc[20];
float est;
char sexo;
};
Las reglas para utilizar el nuevo tipo son las mismas que las seguidas para los tipos
predefinidos como float, int y char, entre otros. Esto es, después de definir un tipo
estructura, podemos declarar una variable de ese tipo.
Esta manera, la mas usual en la programación, requiere que de manera previa la declaración
de un tipo de dato struct y es similar a la declaración de variables a partir de los tipos
predefinidos, se emplea el siguiente formato:
Ejemplo:
‘reg’ es una variable del tipo persona, es decir, contiene los mismos campos
especificados en el tipo de dato persona.
persona aux;
struct persona
{
int ci;
char nombre[30];
char direc[20];
float est;
char sexo;
} reg1, reg2;
struct
{
int ci;
char nombre[30];
char direc[20];
float est;
char sexo;
} per1, per2;
Una variable estructura hace referencia grupal al conjunto de miembros o campos que lo
conforman, sin embargo en el procesamiento de la información es necesario tener la
posibilidad de procesar de manera individual cada valor almacenado en cada campo.
El Operador ‘.’ (punto) permite accederé a cada campo de manera individual, donde se
utiliza la siguiente notación:
Varible_estructura.miembro
Ejemplo:
Persona reg;
…
…
Gráficamente reg tiene la siguiente forma:
reg.ci
reg.nombre
reg.direc
reg.est
reg.sexo
a) Asignación.
Dos variables estructura se pueden asignar únicamente si son estructuras del mismo tipo, es
decir, si son compatibles. La asignación supone una asignación equivalente a campo por
campo, el contenido de todos los campos de una variable son asighnadas a todos los
campos de otra variable.
Ejemplo:
persona reg, aux,
….
aux=reg;
b) Inicializacion.
Una variable estructura puede ser inicializada con valores en los diferentes campos en el
momento de su definición, esto es similar a la inicialización de los tipos predefinidos.
Ejemplo:
se tiene:
Ejemplo:
float est=reg.est;
Esta es una operación muy importante cuando se debe procesar la información contenida en
una estructura por medio de Punteros.
Ejemplo:
Persona *pt=®
‘lista’ es un vector de 50 celdas y cada celda contiene una estructura del tipo
persona.
tpersona curso[50];
‘curso’ es un vector de 50 celdas y cada celda contiene una estructura del tipo
persona.
Gráficamente:
Archivo personas.h
struct persona
{
int ci;
char nombre[30];
float est;
};
class personavec
{
//private:
public:
// int a[50]={5,7,8};
persona lista[50];
int n;
public:
personavec(int);
void leer();
void imprimir();
int verificar(int);
//otras funciones de la clase vector
void ordenar(persona[],int);
float promest();
//otras funciones de la clase pesonavec
};
Archivo personas.cpp
#include <d:/lab4sis2210/persona.h>
#include <iostream.h>
#include <string.h>
personavec::personavec(int t)
{
n=t;
lista[0].ci=123;
strcpy(lista[0].nombre,"luisa");
lista[0].est=1.7;
lista[1].ci=789;
strcpy(lista[1].nombre,"pedro");
lista[1].est=1.63;
lista[2].ci=765;
strcpy(lista[2].nombre,"ana");
lista[2].est=1.75;
lista[3].ci=4653;
strcpy(lista[3].nombre,"mario");
lista[3].est=1.85;
}
void personavec::leer()
{
int i;
for (i=0;i<n;i++)
{
cout<<"\n ingresar C.I.:";
cin>>lista[i].ci;
void personavec::imprimir()
{
int i;
cout<<"\n reporte completo del vector de personas";
for (i=0;i<n;i++)
{
cout<<"\n C.I.:"<<lista[i].ci;
cout<<"\n NOMBRE:"<<lista[i].nombre;
cout<<"\n ESTATURA:"<<lista[i].est;
};
}
float personavec::promest()
{
int i,suma=0;
float prom;
for (i=0;i<n;i++)
suma+=lista[i].est;//suma=suma+lista[i].est;
prom=suma/n;
return prom;
}
#include <d:/lab4sis2210/persona.cpp>
#include <iostream.h>
main()
{
personavec pv(4);
//pv.leer();
pv.imprimir();
int clave;
cout<<"\n\n C.I. de la persona a buscar en el vector:";
/*cin>>clave;
int resp=pv.verificar(clave);
if (resp!=-1)
cout<<"\n se encuentra en la celda :"<<resp;
else
cout<<"\n NO se encuentra en el vector.";*/
cout<<"\n\n ordenando el vector\n";
pv.ordenar(pv.lista,pv.n);
pv.imprimir();
return 0;
}
Declaración:
a) en un solo bloque.
struct persona
{
int ci;
struct // estructura anidad
{
char nom[15];
char paterno[15];
char materno[15];
} nombre;
char direc[20];
float est;
char sexo;
};
struct persona
{
int ci;
struct regnombre nombre; // estructura anidada
char direc[20];
float est;
char sexo;
};
4.2 UNIONES.
A diferencia de las estructuras, el tamaño de una Union es equivalente al tamaño del campo
más grande, mientras que el tamaño de una estructura es equivalente a la suma del tamaño
de todos sus campos.
Una unión es una variable que puede contener, en distintos instantes de la ejecución del
programa, datos de diferentes tipos. Esto permite manipular diferentes tipos de datos
utilizando una misma zona de memoria, la reservada para la variable unión.
La declaración de una unión tiene la misma forma que la declaración de una estructura,
excepto que en lugar de la palabra reservada struct se pone la palabra reservada union. Por
lo tanto, todo lo expuesto para las estructuras es aplicable a las uniones, con la excepción de
que de los miembros especificados, en un instante sólo está uno presente. La sintaxis para
declarar una unión es:
Ejemplo:
union bloque
{
int a;
float b;
char c;
};
M.Cs. Ing. Julio Cesar Bermudez Vargas Página 99
SIS 2210 “METODOLOGIA DE LA PROGRAMACION II
El tipo de dato ‘bloque’ contiene tres miembros a,b y c, el tamaño de este tipo de
dato es equivalente al tamaño del miembro ‘b’, es decir 4 bytes.
La variable ‘pb’ es del tipo unión bloque y puede almacenar uno de los tres valores
que corresponde a uno de los campos.
Solo la variable.
union
{
int a;
float b;
char c;
} pq;
La variable ‘pq’ es una variable unión con similares características a la variable ‘pb’.
El acceso a los campos de una ‘union’ es similar al de una estructura, sin embargo se debe
tomar en cuenta que en una ‘union’ solo se puede almacenar el valor de uno de los campos
en un momento dado.
CAPITULO 5
A diferencia de una variable normal (estática) que hace referencia directa al valor que
almacena, una variable puntero almacena la dirección de memoria del valor en la memoria,
es decir, realiza una referencia indirecta a los valores en la memoria. Los punteros sirven para
expresar direcciones de memoria y permiten modificar el contenido de la memoria a la que apuntan.
Cuando se trabaja con punteros son frecuentes los errores debidos a la creación de punteros
que apuntan a alguna parte inesperada, produciéndose una violación de memoria. Por lo
tanto, debe ponerse la máxima atención para que esto no ocurra, inicializando
adecuadamente cada uno de los punteros que utilicemos.
Para el trabajo o procesamiento de punteros existe dos operados especiales que permiten el
acceso a los valores almacenados en la memoria y la utilización de la dirección de memoria
donde se encuentran los valores.
Por ejemplo:
&p da como resultado la dirección de memoria de la variable p.
El resultado del operador ‘&’ se debe almacenar en una variable puntero o en una función
de salida.
Es un operador unitario que se aplica a un puntero. Dada una expresión de tipo puntero p, la
expresión ‘*p’ permite trabajar con la variable u objeto al que apunta p.
Por ejemplo:
El siguiente programa muestra el efecto resultante del uso de los operadores de dirección y
de indirección.
#include <stdio.h>
int void main()
{
//declaración de variables estáticas y dinamicas
int a = 5, b, *p;
p = &a; // asigna la dirección de a, a la variable p
// p apunta a la variable entera a
b = *p; // asigna a b el valor de la variable a (contenido de la
//variable p)
printf("en la dirección %.4X está el dato %d\n". p, b);
printf("En la dirección %.4X está el dato %d\n", p, *p);
return 0;
}
Tipo *var_puntero;
Donde Tipo especifica el tipo del objeto apuntado, puede ser cualquier tipo incluyendo
tipos definidos por el usuario y var_puntero es el identificador de la variable puntero.
Por ejemplo:
int a;
int *pt;
a) DIRECCIONES DE MEMORIA.
Por ejemplo:
int a=5,*p;
p=&a;
Sin importar el tipo de objeto al que apunta, a una variable puntero se le puede asignar la
constante NULL, esto significa que se tiene un puntero nulo, es decir, la variable puntero
no apunta a algo específico.
Por ejemplo:
int *pt;
float *pf;
pt=NULL;
pf=NULL;
En una variable puntero se puede almacenar el valor cero (0) y es equivalente a almacenar
la constante NULL. El almacenamiento del valor 0 es el único permitido de los valores
enteros que se puede almacenar en una variable puntero. El siguiente programa muestra
estas posibles formas de almacenamiento:
#include <stdio.h>
#include <iostream.h>
int a=5,*pt,*pt1,*pt2;
main()
{
pt=&a;
pt1=NULL;
pt2=0;
cout<<"direccion:"<<pt<<"\tvalor:"<<*pt<<endl;
cout<<"direccion:"<<pt1<<endl;
cout<<"direccion:"<<pt2<<endl;
return 0;
}
El trabajo con punteros permite realizar un conjunto de operaciones que exigen mucho
cuidado en la compatibilidad de los objetos a los que apuntan. Se pueden realizar las
siguientes operaciones:
5.3.1. ASIGNACION.
Dos variables puntero se pueden asignar solo si son punteros al mismo tipo de objeto, es
decir, las dos variables puntero deben ser compatibles.
Por ejemplo:
p=&a;
q=p;
int a=5,*pt;
float *pf;
pt=&a;
pf=pt; // error de incompatibilidad de tipo.
Como ‘pt’ y ‘pf’ son punteros a diferentes tipos de objetos, la asignación de ambas
variables produce un error.
Una Variable puntero, sea cualquiera el objeto al que apunta, se puede asignar con la
constante NULL (puntero nulo).
pt=NULL;
5.3.2. COMPARACION.
Dos variables puntero se pueden comparar solo sin son compatibles, es decir, si ambas
variables apuntan al mismo tipo de objeto. Cuando dos variables puntero se comparan solo
es útil el empleo de los operadores de relación igual que (==) y distinto que (!=), el empleo
de otros operadores no tiene sentido.
Por ejemplo:
int *p,*q;
…..
if (p==q)
……..
if (p!=q)
…….
Una Variable puntero, sea cualquiera el objeto al que apunta se puede comparar con la
constante NULL (puntero nulo).
if (p==NULL)
……..
if (p!=NULL)
……..
Las únicas operaciones aritméticas permitidas con punteros son el incremento o suma y el
decremento o resta. Tanto el incremento o decremento se realiza únicamente con valores
enteros. Se debe tomar en cuenta que la dirección de memoria almacenada en un puntero
está representada en sistema de numeración hexadecimal.
a) INCREMENTO O SUMA.
Por ejemplo:
int a=5,*pt;
pt=&a;
pt=pt+1; //pt apunta al en tero siguiente
pt=pt+3; //pt apunta al tercer entero más adelante
NOTA:
la operación de incremento es recomendable realizarla cuando se accede a
los valores de un arreglo unidimensional por medio de las direcciones de
memoria.
El incremento se realiza en sistema de numeración hexadecimal.
a) DECREMENTO O RESTA.
Por ejemplo:
int a=5,*pt;
pt=&a;
pt=pt-1; //pt apunta al entero anterior
pt=pt-3; //pt apunta al tercer entero más atrás
NOTA:
la operación de decremento es recomendable realizarla cuando se accede a
los valores de un arreglo unidimensional por medio de las direcciones de
memoria.
El decremento se realiza en sistema de numeración hexadecimal.
Existe una relación muy importante entre punteros y arreglos, por lo que es posible acceder
y procesar la información de un arreglo por medio de las direcciones de memoria del
arreglo sin necesidad de emplear la notación de sub índices.
Por ejemplo:
int a[5]={3,6,2,8,1};
int *pt;
pt=a; //pt apunta al primer elemento del arreglo ‘a’
cout<<*pt; //muestra el primer valor del arreglo ‘a’, 3
cout<<*(pt+1); // muestra el segundo valor del arreglo ‘a’, 6
cout<<*(pt+2); // muestra el tercer valor del arreglo ‘a’, 2
cout<<*(pt+3); // muestra el cuarto valor del arreglo ‘a’, 8
cout<<*(pt+4); // muestra el quinto valor del arreglo ‘a’, 1
#include <iostream.h>
int a[10]={5,2,8,1,4};
int a1[5][10]={{2,4,1},{8,1,3},{3,7,1}};
void imprimir(int x[],int y)
{
cout<<"\n CONTENIDO DEL VECTOR POR MEDIO DE DIR. DE MEMORIA";
int *pt;
pt=x;
int i;
for (i=0;i<y;i++)
cout<<"\nvalor="<<*(pt+i)<<"\tdir. de memoria="<<(pt+i);
}
void impmat(int x[][10],int f,int c)
{
int *pt;
int i,j;
cout<<"\n matriz por medio de dir. de memoria";
pt=x[0];
for (i=0;i<f;i++)
for (j=0;j<c;j++)
cout<<"\n valor="<<*((pt+i*10)+j)<<"\tdir. de memoria="<<((pt+i*10)+j);
}
main()
{
int i;
cout<<"\ncontenido del vector por medio de sub indices";
for (i=0;i<5;i++)
cout<<"\nvalor = " <<a[i];
imprimir(a,5);
impmat(a1,3,3);
int p=5,*pt;
float f=6.9,*pf;
char c='a',*pc;
cout<<"\ntamaño de un entero="<<sizeof(int);
cout<<"\ntamaño de un real="<<sizeof(float);
cout<<"\ntamaño de un caracter="<<sizeof(char);
cout<<"\ntamaño de un entero largo="<<sizeof(long);
cout<<"\ntamaño de un real doble="<<sizeof(double);
pt=&p;
pf=&f;
pc=&c;
cout<<"\nvalor de p="<<*pt<<"\t dir de memoeria="<<pt;
cout<<"\nvalor de f="<<*pf<<"\t dir de memoeria="<<pf;
cout<<"\nvalor de c="<<*pc<<"\t dir de memoeria="<<&pc;
pt=pt-2;
cout<<"\nnuevo valor de pt ="<<pt;
return 0;
}
NOTA: en el programa ejecutado, por la versión del IDE, el tamaño que ocupa un valor
entero en la memoria es de 2 bytes,.
La función ‘imprimir’ muestra el contenido del vector por medio de las direcciones de
memoria empleando una variable puntero.
Puesto que una cadena de caracteres es un array de caracteres, es correcto pensar que la
teoría expuesta anteriormente, es perfectamente aplicable a cadenas de caracteres.
char *cadena;
El identificador del array de caracteres es la dirección de comienzo del array y para que una
función manipuladora de una cadena de caracteres pueda saber dónde finaliza dicha cadena,
el compilador añade al final de la cadena el carácter ‘\0’.
#include <stdio.h>
int longcad(char *cad)
{
char *p = cad;
while (*p != '\0')
p++;
return (p - cad);
}
void main ( )
{
char *nombre = "abcd"; //el carácter de terminación '\0' se añade
automáticamente
printf(“\n la longitud de la cadena es: %d \n”,longcad(nombre));
}
El valor (p – cad) devuelto por la función longcad, da la longitud de la cadena. Esta función
realiza las siguientes operaciones:
1. Asigna a p la dirección del primer carácter de la cadena, que coincide con la dirección de
comienzo de la misma, e inicia la ejecución del bucle while.
2. Cuando se ejecuta la condición del bucle while, se compara el carácter *p apuntado por p
con el carácter nulo (\0). Si *p es el carácter nulo el bucle finaliza; si no, se incrementa el
valor de p en una unidad para que apunte al siguiente carácter, y se vuelve a evaluar la
condición.
Tipo_estructura *puntero;
struct persona
{
int ci;
char nombre[20];
float est;
};
Struct fecha
{
int dia;
int mes;
int anio;
};
El acceso a los miembros o campos de una estructura por medio del puntero se realiza
empleando el operador flecha (->).
Por ejemplo:
#include <iostream.h>
#include <stdlib.h>
struct fecha
{
int dd;
int mm;
int aa;
};
int main()
{
struct fecha *d;
d=(struct fecha *)malloc(sizeof(struct fecha));
leer(d);
imprimir(d);
return 0;
}
Notar que para usar un puntero se debe solicitar memoria dinámica por medio de la
función ‘malloc’.
Por ejemplo:
Definición:
Además:
Estructura de un nodo:
struct Nodo
{
int info;
Nodo *sig;
};
typedef Nodo *NNodo;
ADICION.
Se considera un nodo a insertar, con un puntero que apunte a él, y de una lista, en este
caso puede ser vacía o no vacía:
ELIMINACION.
RECORRIDO.
El recorrido es el acceso a todos los nodos sin afectar la estructura, se debe emplear una
variable puntero de apoyo ‘pt’.
BUSQUEDA.
struct Nodo
{
int info;
Nodo *sig;
};
typedef Nodo *NNodo;
class listasimple
{
Nodo *raiz;
public:
listasimple();
void crear();
int esvacia();
void adicion(int);
int eliminar();
NNodo buscar(int);
void recorrido();
};
#include <d:\sis2210a\listas\lisimple.h>
#include <iostream.h>
#include <stdlib.h>
#include <stdio.h>
//funcion constrauctora asigna NULL a la variable 'raiz'
// crea la lista vacia (raiz=NULL)
listasimple::listasimple()
{
raiz=NULL; //la lista se crea vacia
};
pt=raiz;
int dato;
dato=raiz->info;
raiz=raiz->sig;
free(pt);
return dato;
}
// la funcion buscar verifica si un valor esta o no en la lista
// retorna NULL si el valor no esta den la lista y retorna distinto a NULL
// si el valor esta en la lista.
NNodo listasimple::buscar(int valor)
{
Nodo *pt=raiz;
while (pt!=NULL && valor!=pt->info)
pt=pt->sig;
return pt;
}
//la funcion recorrido accede a todos los nodos de la lista
//con un proposito determinado sin modificar la lista.
void listasimple::recorrido()
{
Nodo *pt=raiz;
cout<<"\nCONTENIDO DE LA LISTA:\n";
while (pt!=NULL)
{
cout<<pt->info<<"--->";
pt=pt->sig;
};
cout<<"null\n";
}
#include <d:\sis2210a\listas\lisimple.cpp>
#include <iostream.h>
#include <stdlib.h>
#include <stdio.h>
main()
{
listasimple li;
li.crear();
li.adicion(11);
li.adicion(8);
li.adicion(17);
li.recorrido();
int dat=li.eliminar();
cout<<"\n valor eliminado :"<<dat;
li.recorrido();
return 0;
}
CAPITULO 6
ARCHIVOS
Todos los programas realizados hasta ahora obtenían los datos necesarios para su ejecución
de la entrada estándar (TECLADO) y visualizaban los resultados en la salida estándar
(PANTALLA). Por otra parte, el programa retiene los datos que manipula mientras esté en
ejecución; es decir, los datos introducidos se pierden cuando el programa finaliza.
La solución para hacer que los datos persistan de una ejecución para otra es almacenarlos
en un fichero o archivo en el disco, en vez de en un array en memoria. Entonces, cada vez
que se ejecute el programa que trabaja con esos datos, podrá leer del archivo los que
necesite y manipularlos. Nosotros procedemos de forma análoga en muchos aspectos de la
vida ordinaria; almacenamos los datos en fichas y guardamos el conjunto de fichas en 1o
que generalmente denominamos fichero o archivo.
6.1. DEFINICION.
NOMBRE.
EXTENSIÓN.
TAMAÑO.
LOCALIZACION O PATH.
PUNTERO DE LECTURA/ESCRITURA.
FIN DE ARCHIVO.
Al final del contenido de un archivo existe una marca de finalización EOF (End Of
File) que evita que se afecte otros sectores de memoria que no pertenecen al archivo.
a) CONSULTAS.
b) ACTUALIZACION.
Para poder escribir o leer sobre un fichero, primeramente hay que abrirlo. El fichero puede
ser abierto para leer, para escribir o para leer y escribir.
En C y en C++ abrir un fichero significa definir un stream (flujo) que permita el acceso al
fichero en el disco para leer o escribir. La definición del stream supone la definición de un
buffer (memoria intermedia) para conectar el stream con el fichero en el disco. Esto permite
referirse al stream como si fuera el fichero (más adelante veremos que un stream se define
como un puntero a una estructura de tipo FILE).
Para poder escribir o leer de un archivo, primeramente hay que abrirlo con las funciones
fopen o freopen. El fichero puede ser abierto para leer, para escribir o para leer y escribir.
Para la apertura de un archivo es necesario declarar un puntero a una estructura del tipo
FILE, este puntero se constituye en el stream que se emplea en el programa.
Por ejemplo:
FILE *arch;
fopen(“nombre-archivo”,”modo-aperttura”)
Por ejemplo:
el puntero ‘arch’ captura el resultado de la apertura del archivo ‘medicina.dat’ para el modo
de escritura (w) y en formato binario(b).
Los diferentes modos de apertura de un archivo o fichero son los que se ilustran en la
siguiente tabla:
La función fopen devuelve un puntero a una estructura de tipo FILE que define, entre otros
datos, el buffer asociado con el fichero abierto. Un puntero nulo indica un error en la
apertura. El puntero devuelto por fopen recibe el nombre de stream y es utilizado por las
funciones estándar de E/S para leer y escribir datos en un archivo. Por esta razon, antes de
invocar a la función fopen hay que definir un puntero que apunte a una estructura de tipo
FILE.
Por ejemplo:
FILE *arch;
arch = fopen("datos", "w");
if (arch== NULL)
printf("Error: el fichero no se puede abrir\n");
Por ejemplo:
fclose(arch);
El sistema incorpora una marca de fin de archivo EOF automáticamente cuando el archivo
es creado y si se intenta leer un archivo más allá de la marca de fin de archivo, entonces se
activa el indicador de fin de archivo.
La función ‘feof’ determina el estado del indicador de fin de archivo. La función feof
devuelve un valor distinto de 0 cuando se intenta leer un elemento del archivo y nos
encontramos con un eof (end of file - fin de fichero), en caso contrario devuelve un 0.
Por ejemplo:
while (!feof(arch)) // mientras no se llegue al final del archivo ‘arch’
….
Por ejemplo:
fseek(arch,0,SEEK_END);
Esta instrucción desplaza el puntero de L/E de ‘arch’ 0 bytes a partir del final del
archivo, es decir, lleva el puntero de L/E de ‘arch’ al final del archivo.
Si se desea conocer la posición actual del puntero de L/E del archivo se debe emplear la
función ‘ftell’. El valor o posición que retorna ‘ftell’ es un entero relativo al principio del
archivo.
Por ejemplo:
posi=ftell(arch);
Almacena en la variable ‘posi’ la posición actual del puntero de L/E del archivo
apuntado por arch.
Por ejemplo:
rewind(arch);
fseek(arch,0,SEEK_SET);
Generalmente los pasos 1 y 2 se realizan una vez pero los pasos 3, 4 y 5 se realizan cada
vez que se manipula la información del archivo.
a) CREACIÓN.
b) ADICION DE REGISTROS.
La adición de registros consiste en aumentar un registro a partir del final del archivo
y se debe considerar las siguientes acciones:
d) BUSQUE DE UN REGISTRO.
e) MODIFICACIÓN DE REGISTROS.
Esta operación se ejecuta muy pocas veces porque la información del registro
desaparece del archivo definitivamente. Regularmente solo se elimina físicamente
los registros que previamente han sido eliminados en forma lógica.
Un registro eliminado en forma lógica (inactivo) se puede restaurar para que este
activo nuevamente, esto significa que se debe almacenar 1 en el campo estado.
Código
Nombre
Precio
Procedencia
Estado
2. ESQUEMA DE LA CLASE
3. IMPLEMENTACION.
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <stdio.h>
#include <iostream.h>
#include <e:/jcbv/materia/sis2210/sis2210prog/archivosc++/farmacia.h>
//CREACION DE UN ARCHIVO
//EL ARCHIVO SE CREA VACIO.
void farmacia::crear()
{
arch=fopen("e:/jcbv/materia/sis2210/sis2210prog/archivosc++/medicina.dat","wb");
if (arch==NULL)
cout<<"\n ERROR EN LA APERTURA DEL ARCHIVO\n";
else
cout<<"\n ARCHIVO CREADO CON EXITO, FELICIDADES\n";
//ADICION DE REGISTRO.
//ADICIONA UN REGISTRO A PARTIR DEL FINAL DEL ARCHIVO
void farmacia::adicion()
{
producto reg;
arch=fopen("e:/jcbv/materia/sis2210/sis2210prog/archivosc++/medicina.dat","ab+");
if (arch==NULL)
cout<<"\n ERROR EN LA APERTURA DEL ARCHIVO\n";
else
{
clrscr();
cout<<"\n ADICION DE UN REGISTRO EN EL ARCHIVO\n";
cout<<"\n codigo del MEDICAMENTO:";
cin>>reg.cod;
cout<<"\n NOMBRE del MEDICAMENTO:";
cin>>reg.nombre;
cout<<"\n PRECIO del MEDICAMENTO:";
cin>>reg.precio;
cout<<"\n PROCEDENCIA del MEDICAMENTO 0:NACIONAL; 1: IMPORTADO:";
cin>>reg.proced;
reg.estado=1;
fwrite(®,sizeof(struct producto),1,arch);
cout<<"\nregistro grabado con exito\n";
fclose(arch);
getch();
};
}
getch();
fclose(arch);
};
}
};
cout<<"\n===================================================";
cout<<"\npresione cualquier tecla para continuar:";
getch();
fclose(arch);
};
}
}; return res;
}
else
{
clrscr();
cout<<"\n CONTENIDO ACTUAL DEL REGISTRO EN EL ARCHIVO\n";
fseek(arch,res,SEEK_SET);
fread(®,sizeof(producto),1,arch);
cout<<"\nCODIGO NOMBRE PRECIO PROCDEDENCIA ESTADO";
cout<<"\n===================================================";
cout<<"\n"<<reg.cod<<" "<<reg.nombre<<" "<<reg.precio<<" "<<reg.proced<<"
"<<reg.estado;
cout<<"\n===================================================";
cout<<"\npresione cualquier tecla para continuar:\n";
getch();
reg.estado=0;
fseek(arch,res,SEEK_SET);
fwrite(®,sizeof(struct producto),1,arch);
cout<<"\nregistro eliminado exito\n";
fclose(arch);
};
};
getch();
}
// eliminacion fisica de registros
// solo se elimina los registros eliminados en forma logica
void farmacia::elifisica()
{
producto reg;
FILE *ft;
arch=fopen("e:/jcbv/materia/sis2210/sis2210prog/archivosc++/medicina.dat","rb");
ft=fopen("e:/jcbv/materia/sis2210/sis2210prog/archivosc++/medicina.tmp","wb");
if (arch==NULL || ft==NULL)
cout<<"\n ERROR EN LA APERTURA DEL ARCHIVO\n";
else
{
clrscr();
cout<<"\n ELIMINACION FISICA DE REGISTROS\n";
fread(®,sizeof(struct producto),1,arch);
while (!feof(arch))
{
if (reg.estado==1)
fwrite(®,sizeof(struct producto),1,ft);
fread(®,sizeof(struct producto),1,arch);
};
cout<<"\nse han copiado satisfactoriamente los registros al archivo temporal\n";
cout<<"\npresione cualquier tecla para continuar:";
getch();
fclose(arch);
fclose(ft);
char cad[100],cad1[100];
strcpy(cad,"e:/jcbv/materia/sis2210/sis2210prog/archivosc++/medicina.dat");
strcpy(cad1,"e:/jcbv/materia/sis2210/sis2210prog/archivosc++/medicina.tmp");
remove(cad);
rename(cad1,cad);
};
}
fwrite(®,sizeof(struct producto),1,arch);
cout<<"\nregistro restaurado con exito\n";
fclose(arch);
};
};
getch();
}
#include <stdio.h>
#include <conio.h>
#include <iostream.h>
#include <e:/jcbv/materia/sis2210/sis2210prog/archivosc++/farmacia.cpp>
int main()
{
farmacia f;
int op,clave;
do {
clrscr();
cout<<"\n PROCEAMIENTO DE ARCHIVOS DIRECTOS";
cout<<"\n ======================================";
cout<<"\n 1: CREACION DEL ARCHIVO VACIO";
cout<<"\n 2: ADICION DE REGISTROS";
cout<<"\n 3: REPORTE GENERAL DE REGISTROS ACTIVOS";
cout<<"\n 4: REPORTE DE REGISTROS INACTIVOS";
cout<<"\n 5: REPORTE DE UN REGISTRO ESPECIFICO";
cout<<"\n 6: MODIFICACION DE REGISTROS";
cout<<"\n 7: ELIMINACION LOGICA";
cout<<"\n 8: ELIMINACION FISICA !!!!!";
cout<<"\n 9: RESTAURAR REGISTROS";
cout<<"\n 10: SALIR DE LA APLICACION";
cout<<"\n ======================================";
cout<<"\n ELIJA LA TAREA A REALIZAR : ";
cin>>op;
switch (op)
{
case 1: f.crear();break;
case 2: f.adicion();break;
case 3: f.reporte();break;
case 4: f.reportei();break;
case 5: clrscr();
cout<<"\ncodigo del medicamento:";
cin>>clave;
f.reporteesp(clave);
break;
case 6: clrscr();
cout<<"\ncodigo del medicamento a modificar:";
cin>>clave;
f.modificar(clave);
break;
case 7: clrscr();
cout<<"\ncodigo del medicamento a eliminar:";
cin>>clave;
f.elilogica(clave);
break;
case 8: clrscr();
f.elifisica();
break;
case 9: clrscr();
cout<<"\ncodigo del medicamento a RESTAURAR:";
cin>>clave;
f.restaurar(clave);
break;
};
} while (op<10);
return 0;
}
BIBLIOGRAFIA