Los programas estructurados se basan en estructuras de control bien definidas, bloques de código, subrutinas independientes que soportan recursividad y variables locales. La esencia de la programación estructurada es la reducción de un programa a sus elementos constituidos.
La programación orientada a objetos permite descomponer un problema en subgrupos relacionados. Cada subgrupo pasa a ser un objeto autocontenido que contiene sus propias instrucciones y datos que le relacionan con ese objeto. Todos los lenguajes POO comparten tres características: Encapsulación, Polimorfismo y Herencia.
ENCAPSULACIÓN: Es el mecanismo que agrupa el código y los datos que maneja. Los mantienen protegidos frente a cualquier interferencia y mal uso. Cuando el código y los datos están enlazados de esta manera se ha creado un objeto. Ese código y datos pueden ser privados para ese objeto o públicos para otras partes del programa.
POLIMORFISMO: Es la cualidad que permite que un nombre se utilice para dos o más propósitos relacionados pero técnicamente diferentes. El propósito es poder usar un nombre para especificar una clase general de acciones. Por ejemplo en C tenemos tres funciones distintas para devolver el valor absoluto. Sin embargo en C++ incorpora Polimorfismo y a cada función se puede llamar abs(). El Polimorfismo se puede aplicar tanto a funciones como a operadores.
HERENCIA: Proceso mediante el cual un objeto puede adquirir las propiedades de otro objeto. La información se hace manejable gracias a la clasificación jerárquica.
OBJETO: Conjunto de variables y funciones pertenecientes a una clase encapsulados. A este encapsulamiento es al que se denomina objeto. Por tanto la clase es quien define las características y funcionamiento del objeto.
2. ALGUNAS DIFERENCIAS ENTRE C Y C++
Aunque C++ es un superconjunto de C, existen algunas diferencias entre los dos. En primer lugar, en C cuando una función no toma parámetros, su prototipo tiene la palabra void. Sin embargo en C++ void no es necesario(opcional).
Prototipo en C: char f1(void);
Prototipo en C++: char f1();
Otra diferencia entre C y C++ es que en un programa de C++ todas las funciones deben estar en forma de prototipo, en C los prototipos se recomiendan, pero son opcionales. También si una función de C++ es declarada para devolver un valor obligatoriamente la sentencia return debe devolver un valor, en C no es necesario que se devuelva.
Otra diferencia es el lugar donde se declaran las variables locales. En C, deben ser declaradas solo al principio del bloque, mientras que en C++ las variables se pueden declarar en cualquier punto. Aunque es conveniente realizarlo siempre al comienzo de la función.
3. CREACIÓN DE CLASES Y OBJETOS
La base del encapsulamiento es la clase, a partir de ellas se le dan las características y comportamiento a los objetos. Lo primero es crear la clase y después en la función main que sigue siendo la principal crearemos los objetos de cada una de las clases. Las variables y funciones de una clase pueden ser publicas, privadas o protegidas. Por defecto si no se indica nada son privadas.
Estos modificadores nos indican en que partes de un programa
podemos utilizar las funciones y variables.
private: Solo tendrán acceso los de la misma clase donde estén
definidos.
public: Se pude hacer referencia desde cualquier parte del
programa.
protected: Se puede hacer referencia desde la misma clase y las subclases.
CREACIÓN DE UNA CLASE:
class nomb_clase{
funciones y variables privadas;
public:
funciones y variables publicas;
}
CREACION DEL OBJETO:
nomb_clase
nombre_objeto1;
nomb_clase nombre_objeto2;
LLAMADAS A LAS FUNCIONES DE UNA CLASE:
nombre_objeto.nomb_funcion(parámetros);
DESARROLLO DE FUNCIONES MIEMBRO:
val_devuelto
nomb_clase::nomb_funcion(parametros){
cuerpo;
}
EJEMPLO: Declaramos una clase con una variable privada y dos funciones públicas. La clase recibe el nombre de miclase.
class miclase{
int a;
public:
void funcion1(int num);
int funcion2();
}
4. PRIMER PROGRAMA
Como ejemplo a lo anterior crearemos el primer programa utilizando objetos y clases para ver la teoría llevada a la práctica. Seguiremos utilizando las mismas sentencias que usábamos en C, más adelante los programas tomarán la estructura exclusiva de C++.
#include <stdio.h>
#include <conio.h>
class miclase{
int a;
public:
void pasar_a(int
num);
int mostrar_a();
};
void miclase::pasar_a(int num)
{
a=num;
}
int miclase::mostrar_a()
{
return a;
}
void main()
{
miclase
obj1, obj2;
clrscr();
obj1.pasar_a(10);
obj2.pasar_a(99);
printf(“%d\n”,obj1.mostrar_a());
printf(“%d\n”,obj2.mostrar_a());
getch();
}
5. E/S POR CONSOLA EN C++
En C++ se pueden seguir utilizando las mismas sentencias
para mostrar información por pantalla o pedirla mediante teclado. Pero a estas
antiguas se añaden 2 nuevas de la misma potencia y mayor facilidad de uso. La
cabecera que utilizan estas dos sentencias es iostream.h.
MOSTRAR POR PANTALLA:
cout <<
expresión;
PEDIR POR TECLADO:
cin >>
variable; La variable pude ser de cualquier tipo.
EJEMPLO:
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
void
main()
{
int i,j;
double
d;
clrscr();
i=10;
j=15;
cout <<”Introducir valor: “;
cin>>d;
cout << “Estos son los
valores: “;
cout << i << “ “<< j << “ “<< d;
getch();
}
INDICADORES DE FORMATO: Tres funciones miembro (width, precision y fill) que fijan formato de anchura, precisión y carácter de
relleno. Es necesario fijar la anchura, precisión y carácter de relleno antes
de cada sentencia de escritura.
ANCHURA: cout.width(ancho);
DECIMALES: cout.precision(nº digitos);
RELLENO:
cout.fill(‘carácter’);
EJEMPLO:
#include<iostream.h>
#include<stdio.h>
#include<conio.h>
void main()
{
double numero=123.1234567;
clrscr();
cout<< "hola" <<"\n";
cout.width(15);
cout<< "hola" <<"\n";
cout.width(15);
cout.fill('*');
cout<< "hola"<<"\n";
cout<<numero
<<"\n";
cout.precision(4);
cout<<numero
<<"\n";
cout.precision(10);
cout<<numero;
getch();
}
MODIFICADORES DE LA CLASE IOS: Estos modificadores son pertenecientes a la clase ios. Cuando se activan su valor se mantiene, es decir hay que
desactivarlos para volver al formato de salida original.
FIJAR INDICADOR:
cout.setf(ios::identificador|ios::identificador2);
ANULAR IDENTIFICADOR:
cout.unsetf(ios::identificador|ios::identificador2);
IDENTIFICADOR |
DESCRIPCIÓN |
oct |
Devuelve
un entero en octal. |
hex |
Devuelve
un entero en hexadecimal. |
scientific |
Devuelve
un número en formato científico. |
showpoint |
Muestra
6 decimales aunque no sea necesario |
showpos |
Muestra
el signo + en los valores positivos |
left |
Ajusta
la salida a la izquierda. |
skipws |
Omite
los espacios a la izquierda de la salida. |
uppercase |
Muestra
el texto en mayúsculas. |
EJEMPLO:
#include <stdio.h>
#include <conio.h>
#include <iostream.h>
void main()
{
float
num=200.0;
int
num1=200;
clrscr();
cout<<num <<"\n";
cout.setf(ios::showpos|ios::showpoint);
cout<<num
<<"\n";
cout.setf(ios::scientific);
cout<<num
<<"\n";
cout.unsetf(ios::scientific|ios::showpoint|ios::showpos);
cout<<num <<"\n";
cout.setf(ios::hex);
cout<<num1 <<"\n";
getch();
}
6.
FUNCIONES CONSTRUCTORAS Y DESTRUCTORAS
En los programas hay partes que requieren inicialización.
Esta necesidad de inicialización es incluso más común cuando se está trabajando
con objetos. Para tratar esta situación, C++ permite incluir una función
constructora. A estas funciones se las llama automáticamente cada vez que se
crea un objeto de esa clase.
La función constructora debe tener el mismo nombre que la
clase de la que es parte, no tienen tipo devuelto, es ilegal que un constructor
tenga un tipo devuelto. Pero si es posible pasarle valores a modo de
parámetros.
PROTOTIPO DE LA
FUNCIÓN:
nombre_fucion(parámetros);
DESARROLLO DE LA FUNCIÓN:
nombre_calse::nombre_funcion(parámetros){
cuerpo;
}
EJEMPLO:
#include
<iostream.h>
#include
<stdio.h>
#include
<conio.h>
class miclase{
int a;
public:
miclase();
void show();
};
miclase::miclase()
{
a=100;
}
void miclase::show()
{
cout << a;
}
void main()
{
clrscr();
miclase obj;
obj.show();
getch();
}
El complemento de un constructor es la función destructora. A esta función se la llama automáticamente
cuando se destruye el objeto. El nombre de las funciones destructoras debe ser el mismo que el de la clase a la
que pertenece precedido del carácter ~
(alt+126). Los objetos de destruyen
cuando se salen de ámbito cuando son locales y al salir del programa si son
globales. Las funciones destructoras no devuelve tipo y tampoco pueden recibir
parámetros.
Técnicamente un constructor y un destructor se utilizan para
inicializar y destruir los objetos, pero también se pueden utilizar para
realizar cualquier otra operación. Sin embargo esto se considera un estilo de programación
pobre.
PORTOTIPO DE LA
FUNCIÓN:
~nombre_funcion(parámetros);
DESARROLLO DE LA
FUNCION:
nombre_clase::nombre_funcion(){
cuerpo;
}
EJEMPLO: Mismo programa de antes añadiendo una función destructora.
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class miclase{
int a;
public:
miclase();
~miclase();
void
show();
};
miclase::miclase()
{
a=100;
}
miclase::~miclase()
{
cout <<
"Destruyendo...\n";
getch();
}
void miclase::show()
{
cout << a;
}
void main()
{
clrscr();
miclase obj;
obj.show();
getch();
}
CONSTRUCTORES CON PARAMETROS: Es posible pasar argumentos a una función constructora. Para
permitir esto, simplemente añada los parámetros a la declaración y definición
de la función constructora. Después, cuando declare un objeto, especifique los
parámetros como argumentos.
EJEMPLO:
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class miclase{
int
a;
public:
miclase(int x);
void mostrar();
};
miclase::miclase(int
x)
{
cout << “Constructor”;
a=x;
}
void
miclase::miclase()
{
cout <<”El valor de a es: “;
cout << a;
}
void main()
{
miclase
objeto(4);
ob.show();
getch();
}
7.
FUNCIONES INLINE Y AUTOMÁTICAS
La ventaja de las funciones
insertadas es que se pueden ejecutar más rápidamente que las funciones
normales. La llamada y vuelta de una función normal tardan tiempo y si tienen
parámetros incluso más. Para declarar este tipo de funciones simplemente hay
que preceder la definición de la función con el especificador inline.
inline
valor_devuelto nombre_función(parámetros)
{
cuerpo;
}
Las llamadas a las funciones insertadas se realiza de la
misma manera que cualquier función. Uno de los requisitos es que se tiene que
definir antes de llamarla, es decir definir y desarrollar antes de la función main.
Si el compilador no es capaz de cumplir la petición, la
función se compila como una función normal y la solicitud inline se ignora. Las restricciones son cuatro, no puede contener
variables de tipo static, una sentencia de bucle,
un switch o un goto.
EJEMPLO:
En este programa utilizamos una función inline pasando valores. No usa clases
ni objetos.
#include
<iostream.h>
#include
<stdio.h>
#include <conio.h>
inline int
valor(int x) { return ¡!(X%2);}
void main()
{
int a;
cout <<”Introducir
valor: “;
cin >> a;
if (valor(a))
cout << “Es par ”;
else
cout << “Es impar”;
}
La característica principal de las funciones automáticas es que su definición es lo suficientemente
corta y puede incluirse dentro de la declaración de la clase. La palabra inline no es necesaria. Las restricciones que se aplican a las
funciones inline se aplican también para este tipo. El
uso más común de las funciones automáticas es para funciones constructoras.
valor_devuelto
nombre_funcion(parametros){cuerpo;}
EJEMPLO:
Mismo programa anterior pero sin utilizar inline.
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class ejemplo{
public:
int valor(int x) { return ¡!(X%2);}
};
void main()
{
int a;
cout <<”Introducir
valor: “;
cin >> a;
if (valor(a))
cout << “Es par ”;
else
cout << “Es impar”;
}
8.
UTILIZACIÓN DE ESTRUCTURAS COMO CLASES
En C++, la definición de una estructura se ha ampliado para
que pueda también incluir funciones miembro, incluyendo funciones constructoras
y destructoras como una clase. De hecho, la única diferencia entre una
estructura y una clase es que, por omisión, los miembros de una clase son
privados y los miembros de una estructura son públicos.
struct nombre{
variables y funciones publicas;
private:
variables y funciones privadas;
};
Aunque las estructuras tienen las mismas capacidades que las
clases, se reserva el uso de struct para objetos
que no tienen funciones miembro. Una de las razones de la existencia de las
estructuras es mantener compatibilidad con los programas hechos C.
EJEMPLO:
#include
<iostream.h>
#include
<stdio.h>
#include
<string.h>
#include
<conio.h>
struct tipo{
tipo(double b, char *n);
void mostrar();
private:
double balance;
char nombre[40];
};
tipo::tipo(double b, char *n)
{
balance=b;
strcpy(nombre,n);
}
void tipo::mostrar()
{
cout << "Nombre: " << nombre;
cout
<< ": $" << balance;
if (balance<0.0) cout <<
"****";
cout
<< "\n";
}
void main()
{
clrscr();
tipo acc1(100.12,"Ricardo");
tipo
acc2(-12.34,"Antonio");
acc1.mostrar();
getch();
clrscr();
acc2.mostrar();
getch();
}
9.
OPERACIONES CON OBJETOS
ASIGNACIÓN DE OBJETOS: Se puede asignar un objeto a otro a condición de que ambos
objetos sean del mismo tipo (misma clase). Cuando un objeto se asigna a otro se
hace una copia a nivel de bits de todos los miembros, es decir se copian los
contenidos de todos los datos. Los objetos continúan siendo independientes.
objeto_destino=objeto_origen;
EJEMPLO:
#include
<iostream.h>
#include
<stdio.h>
#include
<conio.h>
class miclase{
int
a,b;
public:
void obtener(int i,
int j){a=i;b=j;}
void mostrar(){cout << a
<< “ “<< b << “\n”;}
};
void main()
{
miclase o1,o2;
o1.obtener(10,4);
o2=o1;
o1.show();
o2.show();
getch();
}
ARRAY DE OBJETOS:
Los objetos son variables y tienen las mismas capacidades y atributos que
cualquier tipo de variables, por tanto es posible disponer objetos en un array.
La sintaxis es exactamente igual a la utilizada para declarar y acceder al
array. También disponemos de arrays bidimensionales.
DECLARACIÓN:
nombre_clase
nombre_objeto[nº elementos];
nombre_clase nombre_objeto[nº elementos]={elementos};
INICIALIZACIÓN:
nombre_objeto[índice].función(valores);
EJEMPLO: Unidimensional.
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class ejemplo{
int a;
public:
void pasar(int x){a=x;}
int mostrar()
{return a;}
};
void main()
{
ejemplo ob[4];
int indice;
clrscr();
for(indice=0;indice<4;indice++)
ob[indice].pasar(indice);
for(indice=0;indice<4;indice++)
{
cout << ob[indice].mostrar();
cout << “\n”;
}
getch();
}
EJEMPLO: Bidimensional.
#include
<iostream.h>
#include
<stdio.h>
#include
<conio.h>
class bidi{
int a,b;
public:
bidi(int n, int
m){a=n;b=m;}
int pasa_a(){return
a;}
int pasa_b(){return
b;}
};
void main()
{
clrscr();
int fil,col;
bidi objeto[3][2]={
bidi(1,2),bidi(3,4),
bidi(5,6),bidi(7,8),
bidi(9,10),bidi(11,12)};
for(fil=0;fil<3;fil++)
{
for(col=0;col<2;col++)
{
cout << objeto[fil][col].pasa_a();
cout << “ “;
cout << objeto[fil][col].pasa_b();
cout << “\n”;
}
}
getch();
}
PASO DE OBJETOS A FUNCIONES: Los objetos se pueden pasar a funciones como argumentos de
la misma manera que se pasan otros tipos de datos. Hay que declarar el
parámetro como un tipo de clase y después usar un objeto de esa clase como
argumento cuando se llama a la función. Cuando se pasa un objeto a una función
se hace una copia de ese objeto.
Cuando
se crea una copia de un objeto porque se usa como argumento para una función,
no se llama a la función constructora. Sin embargo, cuando la copia se destruye
(al salir de ámbito), se llama a la función destructora.
PROTOTIPO
DE FUNCIÓN:
tipo_devuelto
nombre(nombre_clase nombre_objeto){
cuerpo;
}
LLAMADA
A LA FUNCIÓN:
nombre_funcion(objeto);
EJEMPLO:
#include <iostream.h>
#include
<stdio.h>
#include
<conio.h>
class objetos{
int i;
public:
objetos(int n){i=n;}
int devol(){return i;}
};
int sqr(objetos o)
{
return o.devol()*o.devol();
}
void main()
{
objetos a(10), b(2);
cout
<< sqr(a);
cout << sqr(b);
getch();
}
OBJETOS DEVUELTOS POR FUCIONES: Al igual que se pueden pasar objetos, las funciones pueden
devolver objetos. Primero hay que declarar la función para que devuelva un tipo
de clase. Segundo hay que devolver un objeto de ese tipo usando la sentencia return.
Cuando
un objeto es devuelto por una función, se crea automáticamente un objeto
temporal que guarda el valor devuelto. Este es el objeto que realmente devuelve
la función. Después el objeto se destruye, esto puede causar efectos laterales
inesperados.
EJEMPLO:
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
#include <string.h>
class ejemplo{
char
cadena[80];
public:
void
muestra(){cout<<cadena<<"\n";}
void copia(char
*cad){strcpy(cadena,cad);}
};
ejemplo entrada()
{
char cadena[80];
ejemplo str;
cout<<"Introducir
cadena: ";
cin>>cadena;
str.copia(cadena);
return str;
}
void main()
{
ejemplo ob;
ob=entrada();
ob.muestra();
getch();
}
PUNTEROS A OBJETOS: Hasta ahora se ha accedido a miembros de un objeto usando el
operador punto. Es posible acceder a un miembro de un objeto a través de un
puntero a ese objeto. Cuando sea este el caso, se emplea el operador de flecha
(->) en vez del operador punto. Para obtener la dirección de un
objeto, se precede al objeto con el operador &. Se trabaja de igual forma que los punteros a otros tipos.
EJEMPLO:
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class miclase{
int a;
public:
miclase(int x);
int get();
};
miclase::miclase(int x)
{
a=x;
}
int miclase::get()
{
return a;
}
void main()
{
clrscr();
miclase obj(200);
miclase *p;
p=&obj;
cout << "El valor
del Objeto es " << obj.get();
cout << "El valor
del Puntero es " << p->get();
getch();
}
10. FUNCIONES AMIGAS
Habrá momentos en los que se quiera que una función tenga
acceso a los miembros privados de una clase sin que esa función sea realmente
un miembro de esa clase. De cara a esto están las funciones amigas. Son útiles para la sobrecarga de operadores y
la creación de ciertos tipos de
funciones E/S.
El prototipo de esta funciones viene precedido por la palabra clave friend, cuando se desarrolla la función no es necesario incluir friend. Una función amiga no es miembro y no se puede calificar
mediante un nombre de objeto. Estas funciones no se heredan y pueden ser amigas
de más de una clase.
PROTOTIPO:
friend
tipo_devuelto nombre(parametros);
DESARROLLO:
tipo_devuelto
nombre(parametros){
cuerpo;
}
EJEMPLO:
#include <iostream.h>
#include
<stdio.h>
#include
<conio.h>
class miclase{
int n,d;
public:
miclase(int i, int
j){n=i;d=j;}
friend int factor(miclase ob);
};
int
factor(miclase ob)
{
if (!(ob.n%ob.d))
return 1;
else
return 0;
}
void main()
{
miclase obj1(10,2), obj2(3,2);
if(factor(obj1))
cout << "es factor";
else
cout << "no es factor";
getch();
}
11.
THIS, NEW Y DELETE
This es un puntero que se pasa automáticamente a cualquier
miembro cuando se invoca. Es un puntero al objeto que genera la llamada, por
tanto la función recibe automáticamente un puntero al objeto. A este puntero se
referencia como this y solo se pasa a los miembros punteros this.
objeto.funcion(); // a la función recibe automáticamente el puntero this.
EJEMPLO:
A la izquierda sin puntero this. En la derecha utilizando el puntero this.
#include
<iostream.h> #include
<stdio.h> #include
<conio.h> #include
<string.h> class
stock{ char item[20]; double coste; public: stock(char *i,double c) { strcpy(this->item,i); this->coste=c; } void muestra(); }; void
stock::muestra() { cout<<this->item <<
"\n"; cout<<"PVP: " <<
this->coste; } void
main() { clrscr(); stock obj("tuerca",5.94); obj.muestra(); getch(); }
#include
<iostream.h>
#include
<stdio.h>
#include
<conio.h>
#include
<string.h>
class
stock{
char item[20];
double coste;
public:
stock(char *i,double c)
{
strcpy(item,i);
coste=c;
}
void muestra();
};
void stock::muestra()
{
cout<<item
<< "\n";
cout<<"PVP:
" << coste;
}
void
main()
{
clrscr();
stock obj("tuerca",5.94);
obj.muestra();
getch();
}
Hasta ahora si se necesitaba asignar memoria dinámica, se
hacía con malloc y para liberar se utilizaba free. En C++ se puede asignar memoria utilizando new y liberarse mediante delete. Estas operadores no se pueden combinar unas con otras, es
decir debe llamarse a delete solo con un
puntero obtenido mediante new. Los objetos también
se les puede pasar un valor inicial con la sentencia new.
SINTAXIS:
puntero=new
tipo;
delete puntero;
puntero=new
tipo(valor_inicial);
También se pueden crear arrays asignados dinámicamente,
estos arrays pueden utilizar la sentencia new. La sintaxis general es:
DECLARACION DEL ARRAY:
puntero=new
tipo[tamaño];
EJEMPLO:
#include<iostream.h>
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
class cosas{
int i,j;
public:
void obten(int
a,int b){i=a;j=b;}
int muestra(){return
i*j;}
};
void main()
{
clrscr();
int *p_var;
p_var=new int;
//p_var=new int(9); se asigna un valor inicial.
cosas *p;
p=new
cosas;
if(!p || !p_var)
{
cout<<"Error
de asignacion\n";
exit(1);
}
*p_var=1000;
p->obten(4,5);
cout<<"El entero
en p_var es: " <<*p_var;
cout<<"\nTotal:
" <<p->muestra();
getch();
}
EJEMPLO:
Array asignado dinámicamente.
#include<iostream.h>
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
void main(void)
{
int *p;
int i;
p=new
int[5];
clrscr();
if(!p)
{
cout<<"Error de asignacion\n";
exit(1);
}
for(i=0;i<5;i++)
p[i]=i+1;
for(i=0;i<5;i++)
{
cout<<"Este es el entero
en p["<<i<<"]:";
cout<<p[i]<<"\n";
}
delete[] p;
getch();
}
12.
REFERENCIAS
C++ consta de una particularidad relacionada con los
punteros, denominada referencia. Una referencia es un puntero implícito que se comporta como
una variable normal siendo un puntero. Existen tres modos de utilizar una
referencia. Se puede pasar a una función, ser devuelta de una función y crearse
como una referencia independiente. Lo
que apunta una referencia no puede
ser modificado. El caso de las referencias independientes es muy poco común y
casi nunca se utilizan, en este manual no se hace referencia a ellas.
En el ejemplo siguiente se compara un programa que utiliza
un puntero normal y otro programa que realiza las mismas operaciones utilizando
una referencia que se pasa a una función.
EJEMPLO:
Utilizando
punteros normal. Utilizando
referencias.
#include <iostream.h> #include <stdio.h> #include <conio.h> void f(int &n); void main() { int
i=0; f(i); cout<<"valor
i:"<< i; getch(); } void f(int &n) { n=100; }
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
void f(int *n);
void main()
{
int i=0;
f(&i);
cout<<"valor i:" << i;
getch();
}
void f(int *n)
{
*n=100;
}
En el caso de las referencias devueltas
por una función se puede poner el nombre de la función en el lado izquierdo de
la expresión. Es como asignar un valor a una variable. Hay que tener en cuenta
el ámbito de la variable que se comporta como una referencia.
EJEMPLO:
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
int &f();
int x;
void main()
{
clrscr();
f()=100;
cout<<"Valor
de x: " <<x;
getch();
}
int &f()
{
return x;
}
13.
HERENCIA
Para empezar, es necesario definir dos términos normalmente
usados al tratar la herencia. Cuando una clase hereda otra, la clase que se
hereda se llama clase base. La clase
que hereda se llama clase derivada.
La clase base define todas las cualidades que serán comunes
a cualquier clase derivada. Otro
punto importante es el acceso a la clase base. El acceso a la clase base pude
tomar 3 valores, public, private y protected.
Si el acceso es public,
todos los atributos de la clase base son públicos para la derivada.
Si el acceso es private,
los datos son privados para la clase base la derivada no tiene acceso.
Si el acceso es protected,
datos privados para la base y derivada tiene acceso, el resto sin acceso.
EJEMPLO:
para comprobar los distintos tipos de acceso.
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class miclase{
int a;
protected:
int b;
public:
int c;
miclase(int n,int m){a=n;b=m;}
int obten_a(){return
a;}
int obten_b(){return
b;}
};
void main()
{
miclase objeto(10,20);
clrscr();
objeto.c=30;
// objeto.b=30; error,sin acceso.
// objeto.a=30; error,sin acceso.
cout<<objeto.obten_a()
<<"\n";
cout<<objeto.obten_b()
<<"\n";
cout<<objeto.c;
getch();
}
FORMATO DE LA CLASE
DERIVADA:
class
nombre_derivada:acceso nombre_base{
cuerpo;
};
EJEMPLO: Herencia pública.
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class base{
int x;
public:
void obten_x(int
a){x=a;}
void muestra_x(){cout<< x;}
};
class derivada:public base{
int y;
public:
void obten_y(int
b){y=b;}
void muestra_y(){cout<<y;}
};
void main()
{
derivada obj;
clrscr();
obj.obten_x(10);
obj.obten_y(20);
obj.muestra_x();
cout<<"\n";
obj.muestra_y();
getch();
}
EJEMPLO:
Herencia con acceso privado.
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class base{
int x;
public:
void obten_x(int
a){x=a;}
void muestra_x(){cout<<x <<"\n";}
};
class derivada:private base{
int y;
public:
void obten_xy(int
a,int b){obten_x(a);y=b;}
void muestra_xy(){muestra_x();cout<<y<<"\n";}
};
void main()
{
clrscr();
derivada ob;
ob.obten_xy(10,20);
ob.muestra_xy();
// ob.obten_x(10); error,sin
acceso.
// ob.muestra_x(); error,sin
acceso.
getch();
}
HERENCIA MULTIPLE:
Existen dos métodos en los que una clase derivada puede heredar más de una
clase base. El primero, en el que una clase derivada puede ser usada como la
clase base de otra clase derivada, creándose una jerarquía de clases. El segundo, es que una clase derivada puede
heredar directamente más de una clase base. En esta situación se combinan dos o
más clases base para facilitar la creación de la clase derivada.
SINTAXIS:
Para construir la derivada mediante varias clases base.
class
derivada:acceso nomb_base1,nomb_base2,nomb_baseN{
cuerpo;
};
SINTAXIS: Para crear
herencia múltiple de modo jerárquico.
class
derivada1:acceso base{
cuerpo;
};
class derivada2:acceso
derivada1{
cuerpo;
};
class derivadaN:acceso
derivada2{
cuerpo;
};
EJEMPLO:
Herencia de tipo jerárquica.
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class base_a{
int a;
public:
base_a(int x){a=x;}
int ver_a(){return
a;}
};
class deriva_b:public base_a{
int b;
public:
deriva_b(int x, int
y):base_a(x){b=y;}
int ver_b(){return b;}
};
class deriva_c:public deriva_b{
int c;
public:
deriva_c(int x,int
y,int z):deriva_b(x,y){c=z;}
void ver_todo()
{
cout<<ver_a()<<"
"<<ver_b()<<" "<<c;
}
};
void main()
{
clrscr();
deriva_c
ob(1,2,3);
ob.ver_todo();
cout<<"\n";
cout<<ob.ver_a()<<"
"<<ob.ver_b();
getch();
}
El
caso de los constructores es un poco
especial. Se ejecutan en orden descendente, es decir primero se realiza el
constructor de la clase base y luego el de las derivadas. En las destructoras ocurre en orden inverso,
primero el de las derivadas y luego el de la base.
EJEMPLO:
Múltiple heredando varias clases base.
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class B1{
int a;
public:
B1(int x){a=x;}
int obten_a(){return
a;}
};
class B2{
int b;
public:
B2(int x){b=x;}
int obten_b(){return
b;}
};
class C1:public B1,public B2{
int c;
public:
C1(int x,int
y,int z):B1(z),B2(y)
{
c=x;
}
void muestra()
{
cout<<obten_a()<<"
"<<obten_b()<<" ";
cout<<c<<"\n";
}
};
void main()
{
clrscr();
C1 objeto(1,2,3);
objeto.muestra();
getch();
}
14.
FUNCIONES VIRTUALES
Una función virtual es miembro de una clase que se declara
dentro de una clase base y se redefine en una clase derivada. Para crear una función virutal hay que preceder a la
declaración de la función la palabra clave virtual. Debe tener el mismo tipo y numero de parametros y devolver
el mismo tipo.
Cada redefinición de la función
virtual en una clase derivada expresa el funcionamiento especifico de la
misma con respecto a esa clase derivada. Cuando se redefine una función virtual en una clase derivada NO
es necesaria la palabra virtual.
EJEMPLO:
#include<iostream.h>
#include<stdio.h>
#include<conio.h>
class base{
public:
int i;
base(int x){i=x;}
virtual void func(){cout<<i<<"\n";}
};
class derivada1:public base{
public:
derivada1(int x):base(x){};
void func(){ cout
<<i*i<<"\n";}
};
class derivada2:public base{
public:
derivada2(int x):base(x){};
void func(){cout<<i+i;}
};
void main()
{
base obj1(10);
derivada1 obj2(10);
derivada2 obj3(10);
obj1.func();
obj2.func();
obj3.func();
getch();
}
15.
SOBRECARGA DE
FUNCIONES Y OPERADORES
En C++ dos o más funciones pueden compartir el mismo nombre
en tanto en cuanto difiera el tipo de sus argumentos o el número de sus
argumentos o ambos. Cuando comparten el mismo nombre y realizan operaciones
distintas se dice que están sobrecargadas.
Para conseguir la sobrecarga simplemente
hay que declarar y definir todas las versiones requeridas.
También es posible y es muy común sobrecargar las funciones
constructoras. Hay 3 razones por las que sobrecargar las funciones
constructoras. Primero ganar flexibilidad, permitir arrays y construir copias
de constructores.
EJEMPLO:
#include <iostream.h>
#include <conio.h>
#include <stdio.h>
int abs(int numero);
long abs(long numero);
double abs(double numero);
void main()
{
clrscr();
cout
<<"Valor absoluto de -10 "<< abs(-10)
<<"\n";
cout
<<"Valor absoluto de -10L "<< abs(-10L)
<<"\n";
cout
<<"Valor absoluto de -10.01 "<< abs(-10.01)
<<"\n";
getch();
}
int abs(int numero)
{
return numero<0 ? -numero:numero;
}
long abs(long numero)
{
return numero<0 ? -numero:numero;
}
double abs(double numero)
{
return numero<0 ? -numero:numero;
}
EJEMPLO:
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
void fecha(char *fecha);
void fecha(int anno, int mes,
int dia);
void main()
{
clrscr();
fecha("23/8/98");
fecha(98,8,23);
getch();
}
void fecha(char *fecha)
{
cout<<"Fecha:
"<<fecha<<"\n";
}
void fecha(int anno,int mes,int dia)
{
cout<<"Fecha:
"<<dia<<"/"<<mes<<"/"<<anno;
}
ARGUMENTOS IMPLICITOS: Otra característica relacionada con la sobrecarga es la
utilización de argumentos implícitos que permite dar un valor a un parámetro
cuando no se especifica el argumento correspondiente en la llamada a la
función.
PROTOTIPO:
tipo_devuelto(var1=valor,var2=valor,varN=valor);
EJEMPLO:
#include<iostream.h>
#include<stdio.h>
#include<conio.h>
void funcion(int a=0, int b=0)
{
cout<<"a:
"<< a <<" b: "<< b <<”\n”;
}
void main()
{
clrscr();
funcion();
funcion(10);
funcion(20,30);
getch();
}
Es muy similar a la sobrecarga de funciones, un operador
siempre se sobrecarga con relación a una
clase. Cuando se sobrecarga un operador no pierde su contenido original, gana
un contenido relacionado con la clase. Para sobrecargar un operador se crea una
función operadora que normalmente será una función amiga a la clase.
PROTOTIPO:
tipo_devuelto
nombre_clase::operator operador(parametros)
{
cuerpo;
}
Se pueden realizar cualquier actividad al sobrecargar los
operadores pero es mejor que las acciones de un operador sobrecargado se
ajusten al uso normal de ese operador. La sobrecarga tiene dos restricciones,
no puede cambiar la precedencia del operador y que el numero de operadores no
puede modificarse. También hay operadores que no pueden sobrecargarse.
OPERADORES |
. |
:: |
¿?? |
.* |
Existen 3 tipos de sobrecarga de operadores. Operadores
binarios, operadores logicos-relacionales y operadores unarios. Cada uno de
ellos deben tratarse de una manera especifica para cada uno de ellos.
BINARIOS:
La función solo tendrá un parametro. Este parámetro contendrá al objeto que
este en el lado derecho del operador. El objeto del lado izquierdo es el que
genera la llamada a la función operadora y se pasa implícitamente a través de
this.
EJEMPLO:
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class opera{
int x, y;
public:
opera() {x=0;y=0;}
opera(int i,
int j) {x=i; y=j;}
void obtenxy(int
&i,int &j) {i=x;
j=y;}
opera operator+(opera obj);
};
opera opera::operator+(opera obj)
{
opera temp;
temp.x=x+obj.x;
temp.y=y+obj.y;
return temp;
}
void main()
{
opera obj1(10,10), obj2(5,3),obj3;
int x,y;
obj3=obj1+obj2;
obj3.obtenxy(x,y);
cout << "Suma de
obj1 mas obj2\n ";
cout << "Valor de
x: "<< x << "
Valor de y: " << y;
getch();
}
LÓGICOS Y RELACIONALES: Cuando se sobrecargan dichos operadores no se deseará que
las funciones operadoras devuelvan un objeto, en lugar de ello, devolverán un
entero que indique verdadero o falso. Esto permite que los operadores se
integren en expresiones lógicas y relacionales más extensas que admitan otros
tipos de datos.
EJEMPLO:
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class opera{
int x,y;
public:
opera() {x=0;y=0;}
opera(int i,int
j) {x=i; y=j;}
void obtenerxy(int
&i, int &j) {i=x; j=y;}
int operator==(opera obj);
};
int opera::operator==(opera obj)
{
if(x==obj.x && y==obj.y)
return 1;
else
return 0;
}
void main()
{
clrscr();
opera
obj1(10,10), obj2(5,3);
if(obj1==obj2)
cout <<
"Objeto 1 y Objeto 2 son iguales";
else
cout << "
Objeto 1 y objeto 2 son diferentes";
getch();
}
UNARIOS:
El miembro no tiene parámetro. Es el operando el que genera la llamada a la
función operadora. Los operadores unarios pueden preceder o seguir a su
operando, con lo que hay que tener en cuenta como se realiza la llamada.
EJEMPLO:
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class opera{
int x, y;
public:
opera() {x=0;y=0;}
opera(int i,
int j) {x=i;y=j;}
void obtenxy(int
&i, int &j) {i=x; j=y;}
opera
operator++();
};
opera opera::operator++()
{
x++;
y++;
}
void main()
{
clrscr();
opera objeto(10,7);
int x,y;
objeto++;
objeto.obtenxy(x,y);
cout<< "Valor de
x: " << x <<"
Valor de y: "<< y << "\n";
getch();
}
16.
FICHEROS
Para realizar E/S en archivos debe incluirse en el programa
el archivo cabecera fstream.h. Un
archivo se abre mediante el enlace a un flujo. Tenemos 3 tipos de flujo: de entrada, de salida o de entrada-salida.
Antes de abrir un fichero debe obtenerse el flujo. Los 3 flujos tienen
funciones constructoras que abren el archivo automáticamente. Una vez
realizadas las operaciones con los ficheros debemos cerrar el fichero mediante
la función close( ).
FLUJO |
DESCRIPCIÓN |
ofstream
out |
De
salida. |
ofstream
in |
De
entrada. |
fstream
io |
De
salida-entrada. |
En C++ podemos trabajar con 3 tipos de ficheros: secuencial,
binario sin formato y acceso aleatorio. Todos comparten el método de apertura,
pero cada uno de ellos tienen métodos propios para ir escribiendo y leyendo.
SINTAXIS:
flujo(“nombre_fichero.extension”);
EJEMPLO:
Fichero secuencial.
#include <iostream.h>
#include <fstream.h>
#include <stdio.h>
#include <stdlib.h>
void main()
{
ofstream out("fichero.tex");
if (!out)
{
cout << "El
archivo no puede abrirse";
exit(1);
}
char cad[80];
cout << "Escritura
de cadenas. Salir dejar en blanco\n";
do
{
cout<<": ";
gets(cad);
out << cad << endl;
}while(*cad);
out.close();
}
BINARIOS SIN FORMATO: Las funciones E/S son read()y write(). La función read() lee num bytes del
flujo asociado y los coloca en la variable. La función write() escribe num bytes
de la variable en el flujo asociado.
PROTOTIPOS:
in.read(variable,num_bytes);
out.write(variable,longitud_cadena);
EJEMPLO: Fichero binario. Escritura.
#include <iostream.h>
#include <fstream.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main()
{
ofstream out("texto.tex");
if (!out)
{
cout
<< "El archivo no puede abrirse";
exit(1);
}
char cad[80];
cout << "Escritura
de cadenas. Salir dejar en blanco\n";
do
{
cout<<": ";
gets(cad);
out.write(cad,strlen(cad));
}while(strlen(cad));
out.close();
}
EJEMPLO: Fichero binario. Lectura.
#include <iostream.h>
#include <fstream.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
void main()
{
clrscr();
ifstream
in("texto.tex");
if (!in)
{
cout << "El
archivo no puede abrirse";
exit(1);
}
char cad[80];
cout << "Lectura
de cadenas\n";
in.read(cad,80);
cout << cad;
in.close();
getch();
}
ALEATORIOS:
También podemos realizar el acceso aleatorio. Las funciones que se utilizan son
seekg() y seekp() para
posicionarnos y las funciones get() y put() para leer y escribir en el fichero. Las funciones de
posicionamiento y leer-escribir van emparejadas.
PROTOTIPOS:
out.seekp(posicion,lugar_de_comienzo);
out.put(‘char’);
in.seekg(posicion,lugar_de_comienzo);
in.get(var_char);
LUGAR |
DESCRIPCIÓN |
ios::beg |
Desde
el principio. |
ios::end |
Desde
el final. |
ios::cur |
Posición
actual. |
EJEMPLO:
Fichero aleatorio. Escritura.
#include <iostream.h>
#include <fstream.h>
#include <stdlib.h>
void main()
{
fstream
out("texto1.txt",ios::in|ios::out);
if (!out)
{
cout << "El
archivo no se puede abrir";
exit(1);
}
out.seekp(4,ios::beg);
out.put('z');
out.close();
}
17.
EXCEPCIONES
Es un mecanismo de gestión de errores incorporado. Permite
gestionar y responder a los errores en tiempo de ejecución. Las excepciones están
construidas a partir de tres palabras clave: try, catch y throw.
Cualquier sentencia que provoque una
excepción debe haber sido ejecutada desde un bloque try o desde una función que este dentro del bloque try.
Cualquier excepción debe ser capturada por una sentencia
cath que sigue a la sentencia try, causante de la excepción.
SINTAXIS:
try{
cuerpo;
}
catch(tipo1 arg){
bloque catch;
}
catch(tipo2 arg){
bloque catch;
}
catch(tipoN arg){
bloque catch;
}
EJEMPLO:
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
void main()
{
try{
cout<<"Dentro del bloque try\n";
throw 10;
cout<<"Esto se
ejecuta si no hay problemas";
}
catch(int i){
cout<<"Capturado el error "<< i;
cout<<"\n";
}
cout << "fin";
getch();
}