Programación Orientada A Objetos Con C++
Programación Orientada A Objetos Con C++
PROGRAMACIÓN ORIENTADA A
OBJETOS (POO) CON C++
1.2. Encapsulación
Este concepto permite tener un control de acceso selectivo tanto a los miembros
como a los métodos, de tal forma que desde fuera del objeto sólo se pueda acceder a los
métodos e identificadores que permita el creador del objeto.
1.3. Herencia
Permite la reutilización y la extensión del código. Permite diseñar nuevas clases
a partir de otras ya existentes, pudiendose además extender sus métodos (cambiar su
semántica en la clase que hereda).
Ej. Pilas y Colas.
1
Programación orientada a objetos con C++
1.4. Polimorfismo
Permite tratar de forma genérica objetos de distintas clases, ahorrando así código
y proporcionando simplicidad. Por tanto, trata de forma genérica objetos de distintos
tipos derivados de una misma clase de objetos.
class Nombre_de_la_Clase
{
Definición_de_Datos;
Prototipos_y_métodos;
};
Cuando deseemos crear un objeto de una clase definida lo que haremos será lo
siguiente:
Nombre de la Clase Nombre del Objeto;
2
Programación orientada a objetos con C++
Se puede acceder a los datos y métodos de una clase de la misma forma que se
accede a un campo de una estructura, es decir, con . o con ->, seguido del nombre del
elemento a usar.
3
Programación orientada a objetos con C++
class Celdilla
{
char Caracter, Atributo; //Miembros privados
public:
void FijaCeldilla(char C, char A)
{
Caracter=C; Atributo=A;
}
void ObtenCeldilla(char &C, char &A)
{
C=Caracter; A=Atributo;
}
//&C es una referencia a la variable C
};
Figura 1. Ejemplo de la definición de una clase con métodos definidos dentro de la clase.
class Celdilla
{
char Caracter, Atributo;
public:
void FijaCeldilla(char C, char A);
void ObtenCeldilla(char &C, char &A);
};
void Celdilla::FijaCeldilla(char C, char A)
{
Caracter=C;Atributo=A;
}
void Celdilla::ObtenCeldilla(char &C, char &A)
{
C=Caracter;A=Atributo;
}
4
Programación orientada a objetos con C++
3. SOBRECARGA
Consiste en la redefinición de un método. Por tanto, un método sobrecargado es
un método que consta de varias definiciones distintas, aunque todas ellas llamadas de
igual forma. Lo que va a permitir distinguir un método de otro que se llame de igual
forma van a ser el tipo y el número de parámetros que recibe.
#include <iostream.h>
class Celdilla
{ char Caracter, Atributo;
public:
void FijaCeldilla(char C, char A)
{
Caracter=C;
Atributo=A;
}
void FijaCeldilla(unsigned CA)
{
Caracter=CA & 0xff;
Atributo=CA >> 8;
}
void ObtenCeldilla(char &C, char &A)
{
C=Caracter;
A=Atributo;
}
unsigned ObtenCeldilla()
{
return Atributo<<8 | Caracter;
}
};
void main()
{ Celdilla X, Y;
unsigned Casilla;
char C, A;
5
Programación orientada a objetos con C++
4. CONSTRUCTORES Y DESTRUCTORES
Cada vez que se define una variable de un tipo básico, el programa ejecuta un
procedimiento que se encarga de asignar la memoria necesaria, y si es necesario,
también realizará las inicializaciones pertinentes.
De forma complementaria cuando una variable queda fuera de ámbito se llama a
un procedimiento que libera el espacio que estaba ocupando dicha variable.
El método al que se llama cuando creamos una variable es el constructor, y al
que se llama cuando se destruye una variable es el destructor.
Una clase puede tener uno o varios constructores, pero un sólo destructor. El
constructor debe tener el mismo nombre que la clase a la que pertenece, mientras que el
destructor también debe llamarse de igual forma pero precedido por el carácter ∼.
Estos métodos no van a tener parámetros de salida, ni siquiera puede usarse void.
Sin embargo, el constructor sí podría tomar parámetros de entrada, cosa que no puede
hacer el destructor.
#include <string.h>
class Cadena
{ char *pc;
unsigned longitud;
public:
Cadena( char * Str);
∼Cadena() { delete pc; }
Cadena Operator+(Cadena c);
int Operator==(Cadena c)
{
return ! strcmp(pc, c.pc);
}
unsigned strlen()
{
return longitud;
}
};
Cadena::Cadena(char * Str)
{ longitud= ::strlen( Str);
pc=new char[longitud+1];
strcpy(pc, Str);
}
Cadena Cadena::operator+(Cadena c)
{ char Resultado[ 1024 ];
strcpy(Resultado, pc);
strcat(Resultado, c.pc);
Cadena Temp(Resultado);
return Temp;
}
void main()
{ Cadena C1(“Pis Pas”), C2(“Pas Pis”); }
Figura 4. Ejemplo de una clase con constructor y destructor.
6
Programación orientada a objetos con C++
include <string.h>
class Cadena
{ char *pc;
unsigned longitud;
public:
Cadena (){ longitud=0; pc=0;}
Cadena (char * Str);
Cadena (Cadena &c);
∼Cadena (){ if (longitud) delete pc;}
Cadena operator+ (Cadena c);
void operator= (Cadena c);
int operator==(Cadena c)
{
return ! strcmp(pc, c.pc);
}
unsigned strlen()
{
return longitud;
}
};
Cadena::Cadena(char * Str)
{
longitud= ::strlen( Str);
pc=new char[longitud+1];
strcpy(pc, Str);
}
Cadena Cadena::operator+(Cadena c)
{
char Resultado[ 1024 ];
strcpy(Resultado, pc);
strcat(Resultado, c.pc);
Cadena Temp(Resultado);
return Temp;
}
Cadena Cadena::operator=(Cadena &c)
{
longitud=c.longitud;
pc=new char[longitud+1];
strcpy(pc,c.pc);
}
void main()
{
Cadena C1(“Pis Pas”), C2(“Pas Pis”);
Cadena C3(C1), C4= C1+C2;
C3=C2;
}
Figura 5. Ejemplo de una clase con constructores de copia y asignación, y con destructor.
7
Programación orientada a objetos con C++
8
Programación orientada a objetos con C++
4.3. Destructores
El destructor, como se mencionó antes, se distingue del resto de miembros de
una clase porque va precedido por el carácter ∼ . Cada vez que un objeto deja de ser
útil, se llamará al destructor del objeto, que realizará sobre éste las últimas operaciones
que sean necesarias. Si no se provee de un destructor se creará uno por defecto, que
destruirá el objeto, sin tener en cuenta posibles liberaciones de memoria.
Cualquier destructor no puede aceptar parámetros ni de entrada ni de salida, y
además sólo puede aparecer a lo sumo uno en cada clase.
9
Programación orientada a objetos con C++
void ControlErroresdeMemoria()
{
cout<<“Error en asignación dinámica \n”;
exit(-1);
}
void main()
{
set_new_handler(ControlErroresdeMemoria);
Cadena *Q=new Cadena[32000];
}
5. Herencia
La herencia es una de las características fundamentales de la Programación
Orientada a Objetos por la que, tomando como base una clase ya existente, es posible
derivar una nueva, que heredará todos sus miembros, teniendo posibilidad de
sobrecargarlos, crear unos nuevos o utilizarlos. La idea básica por tanto, es reutilizar las
clases existentes y extender o modificar su semántica.
10
Programación orientada a objetos con C++
Para crear una clase derivada, tras el nombre de la clase, se pondrán dos puntos y
el nombre de la clase que se quiere tomar como base. Además, deberemos especificar el
tipo de herencia, lo cual se especificará delante del nombre de la clase base.
Por ejemplo, supongamos que a partir de la clase Ventana, que permite
gestionar ventanas en modo texto, queremos crear una clase Ventanagrafica
que las gestiona en modo gráfico. Para ello empleamos el siguiente código:
11
Programación orientada a objetos con C++
#include <iostream>
class Base
{
int Entero; // Miembro privado por defecto
protected:
void FijaEntero( int N)
{
Entero=N;
}
public:
void ObtenEntero(void)
{
return Entero;
}
};
class Derivada : public Base
{
public:
void ImprimeEntero(void)
{
cout<<Entero; // Error: Entero no está
// accesible aquí
}
void ActivaBase(int N)
{
FijaEntero(N); // Correcto: acceso a
// miembro protegido
}
void MuestraEntero(void)
{
cout << ObtenEntero();
// Correcto: acceso a miembro público
}
};
void main(void)
{
Derivada A;
A.ActivaBase(5); // Se accede a un miembro público
cout << A.ObtenEntero(); // Correcto, al ser un miembro
// público
A.FijaEntero(10); //Error, FijaEntero está protegido,
} //y por tanto, no accesible desde
//fuera
12
Programación orientada a objetos con C++
#include <iostream.h>
int Entero; // Variable global
class Base
{
protected:
int Entero; // Miembro protegido
};
class Derivada : public Base
{
int Entero;
public:
void ImprimeValores(void);
};
void Derivada::ImprimeValores(void)
{
cout << Entero; // Imprime el Entero de la clase derivada
cout << Base::Entero; // Imprime el Entero de la clase Base
cout << ::Entero; // Imprime el Entero varible global
}
class Base
{
int Entero1;
};
class Derivada : public Base
{
int Entero2; // Se hereda Entero1
};
void main(void)
{
Base A;
Derivada B;
Figura 9. Ejemplo de problemas que pueden surgir con la conversión de una clase derivada a base.
13
Programación orientada a objetos con C++
#include <iostream.h>
class Base
{
int Entero1;
public:
Base()
{
Entero1=0;
cout <<(“Constructor Base \n”);
}
Base(int N)
{
Entero1=N;
}
};
class Derivada : public Base
{
// Se hereda Entero1
int Entero2;
public:
Derivada()
{
Entero2=0;
cout <<(“Constructor Derivado\n”);
}
Derivada(int N1, int N2) : Base (N1)
{
Entero2=N2;
}
};
void main(void)
{
Base A(5);
Derivada B(3,6);
}
Figura 10. Ejemplo de herencia en constructores.
14
Programación orientada a objetos con C++
En cambio, con los destructores ocurre todo lo contrario, puesto que se destruirá
primero el objeto de la clase heredada y después se irán llamando a los destructores de
objetos de las clases bases. Además, una ventaja que tiene es que como los destructores
no tienen parámetros, no es necesario llamar al destructor de la clase base, sino que la
llamada se hará de forma automática.
#include <iostream>
class Base
{
protected:
int Entero1;
public:
Base()
{
Entero1=0;
cout <<(“Constructor Base\n”);
}
Base(int N)
{
Entero1=N;
}
∼Base()
{
cout <<(“Destructor Base\n”);
}
};
class Derivada : public Base
{
int Entero2; // Se hereda Entero1
public:
Derivada()
{
Entero2=0;
cout <<(“Constructor Derivado\n”);
}
Derivada(int N1, int N2) : Base(N1)
{
Entero2=N2;
}
∼Derivada()
{
cout << “Destructor Derivado\n”;
}
}
void main(void)
{
Base A(5);
Derivada B(3,6);
}
15
Programación orientada a objetos con C++
__________:private......,public......,public.....
6. POLIMORFISMO
Esta característica de C++ permite que un objeto tome distintas formas, gracias
al enlace en tiempo de ejecución (vinculación dinámica) o late binding.
7. AMISTAD
Esta característica permite que una función que no pertenezca a una clase pueda
hacer uso de todos sus miembros.
16
Programación orientada a objetos con C++
public:
void FijaEntero(int N);
friend void FuncionAmiga(Clase &X,int N);
};
void FuncionAmiga(Clase &X,int N)
{
X.EnteroPrivado=N;//Acceso al miembro privado
}
// Si no fuese friend EnteroPrivado no estaría accesible desde
// fuera de la clase
class ClaseAmiga;
class Clase
{
int EnteroPrivado;
friend ClaseAmiga;
};
8. PLANTILLAS
Un problema con el que nos podemos encontrar es que tengamos dos clases que
hacen uso de los mismos métodos pero estos están aplicados a distintos tipos de datos;
Por ejemplo, listas con enteros, listas con cadenas, etc...
17
Programación orientada a objetos con C++
18
Programación orientada a objetos con C++
9. OTROS CONCEPTOS
9.1. Miembros Static
Los miembros y métodos de una clase pueden declararse estáticos anteponiendo
la palabra static.
Un dato declarado estático en una clase es un dato de una sóla instancia, se crea
al definir la clase y sólo existirá uno, independientemente del número de objetos que se
creen. Esto es bueno cuando todos los objetos van a compartir una misma variable. Para
inicializar la variable, lo que haremos será preceder a la variable del nombre de la clase.
class Clase
{
static int Dato;
.....
};
En cuanto a los métodos declarados como estáticos, debemos decir que no han
de estar relacionados con objeto alguno de dicha clase. Un método estático no puede ser
nunca virtual.
19
Programación orientada a objetos con C++
#include <iostream.h>
class Clase
{ const int Objeto; // Un miembro const privado
public:
Clase(int N=0) : Objeto(N){ }// Inicializa el
// objeto
// El método Imprime es capaz de manejar objeto const
void Imprime(void)
const { const cout << Objeto;}
};
void main(void)
{ const Clase X(15); // Un objeto const de valor inicial 15
X.Imprime();
}
class Clase
{ enum Enumeracion1 {Negro, Blanco};
public:
enum Enumeracion2 {Abierto, Cerrado};
struct {
char Caracter;
int Numero;
} Datos;
};
void main(void)
{ int N;
Clase Objeto;
20
Programación orientada a objetos con C++
#define Une(x,y)
// Macro para crear clase de manejo de matrices
#define ClaseMatriz(tipo)
#include <iostream.h>
void main(void)
{ Matrizint Objetoint(10);
Matrizdouble Objetodouble(10);
Objetoint[0]=10;
Objetodouble[5]=24.5;
cout << Objetoint[0] << ‘\n’ << Objetodouble[5];
}
Figura 17. Ejemplo de una macro.
21
Programación orientada a objetos con C++
10.2. Realización
En cada uno de los archivos que necesiten hacer uso de una clase será necesario
incluir los archivos cabecera en los cuales están definidas las clases que van a ser
usadas. Sin embargo, normalmente estas bibliotecas son compiladas obteniendo ficheros
.OBJ que serán enlazados junto a las aplicaciones.
En el caso de que tengamos varios ficheros se puede, o bien generar un código
objeto por cada uno de los archivos fuente, o bien varios archivos .OBJ, o bien unirlos
todos en una biblioteca .LIB.
En definitiva, el proceso de creación y utilización de una biblioteca de funciones
podría simplificarse a:
extern”c”{
#include “cabecera.h”
}
22
Programación orientada a objetos con C++
11. STREAMS
Un Stream es un flujo de datos, y para que exista es necesario que haya un origen
abierto conteniendo datos, y un destino abierto y preparado para recibir datos. Entre el
origen y el destino debe haber una conexión por donde puedan fluir los datos. Estas
conexiones se llevan a cabo a través de operadores y métodos de C++.
Los streams se usan de forma genérica, independientemente de cuales sean el
origen y el destino.
23
Programación orientada a objetos con C++
11.4. Anchura
Por defecto, los datos que entran y salen tienen una anchura que se corresponde
con el espacio necesario para almacenar ese dato.
Para fijar la anchura, tanto se puede usar el método width() como el
manipulador setw(). Si se usa width() sin parámetro devolverá la anchura fijada,
que por defecto es 0. Esto significa que se tomen los carácteres necesarios para la
representación del dato en el stream de salida. Si le pasamos un parámetro se fijará la
anchura a ese número de carácteres.
11.6. Precisión
Para números en punto flotante, a la hora de la salida se representa con todos sus
dígitos, pero esto es alterable, o bien mediante el método precision(), o bien
mediante el manipulador setprecision(), poniendo el número de dígitos a
presentar en la salida como parámetro.
El método sin parámetros devolverá el valor fijado anteriormente.
24
Programación orientada a objetos con C++
#include <iostream.h>
#include <iomanip.h>
void main(void)
{
unsigned long Dato;
25
Programación orientada a objetos con C++
Métodos Acción
ios::Skipws Cuando está activo ignora los espacios en blanco iniciales
ios::left Activa la justificación izquierda en la salida de datos
ios::right Activa la justificación derecha en la salida de datos
ios::internal Permite que en las salidas el signo o indicador de base, se muestre
a la izquierda del relleno como primer carácter
ios::dec Activa conversión decimal
ios::oct Activa conversión octal
ios::hex Activa conversión hexadecimal
ios::showbase Mostrará el indicador de base en las salidas
ios::showpoint Muestra el punto decimal y los dígitos decimales necesarios para
completar la salida
ios::uppercase Se usan letras mayúsculas en la salida
ios::showpos Está desactivado por defecto y lo que hace es mostrar el signo en
las salidas numéricas
ios::scientific Realiza las salidas de punto flotante con notación científica
ios::unitbuf Vuelca los caracteres de E/S en el dispositivo origen o destino
ios::fixed Realiza la notación decimal
26
Programación orientada a objetos con C++
27
Programación orientada a objetos con C++
#include <iostream.h>
#include <iomanip.h>
void main(void)
{
char Cadena[20];
int N;
28
Programación orientada a objetos con C++
Métodos Acción
in_avail() Devuelve un entero indicando el número de caracteres que hay
actualmente en el buffer de entrada, tomados desde el stream de
lectura
sbumpc() Lee un carácter del buffer de entrada, lo devuelve y avanza una
posición
snextc() Avanza una posción, lee un carácter del buffer y lo devuelve
sgetc() Lee el carácter actual apuntado en el buffer de entrada y lo devuelve,
pero no avanza
stossc() Avanza al siguiente carácter
sgetn() Este método necesita dos parámetros, un puntero a carácter y un
entero. Lee el número de caracteres indicado del buffer de entrada y lo
almacena en la dirección indicada por el puntero
sputbackc() Devuelve un carácter al buffer de entrada, especificándoselo como
parámetro
Métodos Acción
out_waiting() Devuelve un entero indicando el número de caracteres que hay en el
buffer de salida esperando a escribirse en el stream
sputc() Permite escribir un carácter al buffer de salida, carácter que habrá
que pasar como parámetro
sputn() Permite escribir en el buffer de salida un determinado número de
caracteres. Toma dos parámetros: Puntero a carácter y un entero
29
Programación orientada a objetos con C++
Métodos Acción
seekpos() Posiciona el puntero en una posición absoluta dada por un entero largo,
el cual se pasa como parámetro. El segundo parámetro indica el puntero
que se va a fijar, el de entrada o el de salida
seekoff() Toma tres parámetros y la posición del puntero de forma relativa según
el segundo parámetro que puede ser:
ios::beg, ios::cur, ios::end, según se desee desplazar
desde el principio, desde la posición actual o desde el final. El tercer
parámetro es el indicador del puntero a desplazar ios::in,
ios::out. Por defecto, ios::in/ios::out
Bibliografía
[1] Programación Orientada a Objetos con Borland C++
Francisco Charte Ojeda
Anaya Multimedia, 1993
[2] El Lenguaje de Programación C++ (2ª Edición)
Bjarne Strouptrup
Addison-Wesley, 1993
30