Características de la POO en C++ - Security-Net

Anuncio
POO
2004
P
M
Introducción a la POO en C++
Algunas diferencias entre C y C++
Objetos, métodos, encapsulado.
T
e
m
a
Clases
Métodos
Constructores y Destructores
Sobrecarga
Amigos
Entrada y salida en C++
Espacios de nombres
Miembros estáticos
2
PM. 2. POO (1)
Características de la POO en C++
Respecto a la POO, las características más importantes
de C++ son:
•
•
•
•
•
•
Herencia múltiple
Sobrecarga de operadores y funciones
Derivación
Funciones virtuales
Plantillas
Gestión de excepciones
PM. 2. POO (2)
Pequeñas diferencias C-C++ (1)
Argumentos con valor por defecto
int a(int a=1, int b=2, int c)
NO
a(4,5) ??
int a(int c, int a=1, int b=2)
hace que a(1), ó a(1,2) ó a(1,2,3) no sean ambiguos
PM. 2. POO (3)
Pequeñas diferencias C-C++ (2)
Funciones inline frente a macros de preprocesador
supongamos definido
#define cuadrado(x) (x*x)
entonces
void f(double d, int i) {
r = cuadrado(d); // va bien
r = cuadrado(i++); // va mal: significa (i++*i++);
r = cuadrado(d+1); // va mal: significa (d+1*d+1);
//
que es, (d+d+1);
iría mejor
#define cuadrado(x) ((x)*(x))
// va mejor
PM. 2. POO (4)
Pequeñas difs. C-C++: funciones inline
Pero sigue sin resolver cuadrado(i++)
Así que C++ aporta una solución:
inline int icuadrado(int x) {
return x * x;
}
que expande C++ (no el procesador), sí resuelve el problema:
es como una función normal pero que se expande
entre las líneas
del código que la llama
PM. 2. POO (5)
Pequeñas difs. C-C++: funciones inline
La ventaja de las funciones inline es que no retrasan con
llamadas a función. Son expresiones que se repinten en
el código y no se quieren convertir en función para no
recargar el número de llamadas a función.
icuadrado(i++) // va bien
PM. 2. POO (6)
C no tiene parámetros por referencia
C++
C
void swap(int *pa, int *pb)
void swap(int & a, int & b)
{ int t = *pa; *pa=*pb; *pb=t; }
{ int t = a; a=b; b=t; }
main()
main()
{ swap(&x, &y);}
{ swap(x, y);}
El modificador & en los parámetros formales en C++ indica que
ese parámetro es pasado por referencia. Algo que en C es
imposible. En C no existía el paso por referencia, había que
pasar la dirección (operador &) de la variable.
PM. 2. POO (7)
Operadores new y delete
int *pint, *parr;
pint = new int;
parr = new int[10]
// …
delete pint;
delete[] parr;
Son operadores y por lo tanto el compilador interviene
"inteligentemente" decidiendo el tipo de solicitud de memoria
dinámica y el tamaño necesitado.
Esto es más adecuado en la creación de objetos
PM. 2. POO (8)
Operadores new y delete, ventajas
• No hace falta calcular los tamaños con sizeof():
pa = new int;
// malloc(sizeof(int))
• El propio delete se encarga de poner a 0 (NULL)
el puntero y new de poner a 0 la memoria
adquirida
delete pint;
// deja 0 modificando pint
// antes era: free(pint); pint=NULL;
PM. 2. POO (9)
Equivalencia entre new y malloc
truco en C
#define NEW(X) (X *) malloc(sizeof(X))
pa = NEW(X);
equivale en C++ a
pa = new X;
PM. 2. POO (10)
Classes
• La mayor diferencia entre C y C++ está en
soporte que este último ofrece para las clases
• El nombre original de C++ era "C with Classes"
• Esencialmente una clase es un tipo abstracto de
datos (TAD): colección de datos junto a operadores
para acceder y modificarlos
PM. 2. POO (11)
Clases, uso
Se puede definir una clase "Complejo" y variables
de tipo Complejo:
Complejo c1, c2, c3;
y entonces seguir operando no sólo con funciones
sino también con operadores:
c3 = c1 / c2; c3 = c1 + c2; c3++;…
en la que "/", "+", "++" se han sobredefinido para
operar con Complejo.
PM. 2. POO (12)
Clases, declaración
La declaración es muy parecida a la declaración
de estructuras, de la que son extensión:
class Complejo {
float parteReal;
float parteImaginaria;
};
PM. 2. POO (13)
Clases, declaración
La declaración es muy parecida a la declaración
de estructuras, de la que son extensión:
class Complejo {
float parteReal;
float parteImaginaria;
};
Los campos son ahora llamados
PM. 2. POO (13)
Clases, declaración
La declaración es muy parecida a la declaración
de estructuras, de la que son extensión:
class Complejo {
float parteReal;
float parteImaginaria;
};
Los campos son ahora llamados
atributos
PM. 2. POO (13)
Clases, declaración
La declaración es muy parecida a la declaración
de estructuras, de la que son extensión:
class Complejo {
float parteReal;
float parteImaginaria;
};
Los campos son ahora llamados
atributos
de la clase.
PM. 2. POO (13)
Clases, uso
Una vez declarada la clase, se pueden crear
objetos de esa clase:
Complejo c1, c2;
no es necesario decir Class Complejo c1…
Los objetos c1 y c2 son instancias de la clase
Complejo
Una instancia de cualquier clase se denomina
objeto
PM. 2. POO (14)
Los atributos están ocultos
Los miembros de una estructura son accedidos mediante
los operadores "." y "->"
En una clase, por otro lado, los miembros están ocultos,
mientras no se indique lo contrario, o sea, que por
defecto son inaccesibles, de manera que las siguientes
acciones son ilegales:
c1.parteReal = 0.0;
// ilegal, no accesible
im = c2.parteImaginaria; // ilegal, no accesible
PM. 2. POO (15)
Los atributos están ocultos
Los miembros de una estructura son accedidos mediante
los operadores "." y "->"
En una clase, por otro lado, los miembros están ocultos,
mientras no se indique lo contrario, o sea, que por
defecto son inaccesibles, de manera que las siguientes
acciones son ilegales:
c1.parteReal = 0.0;
// ilegal, no accesible
im = c2.parteImaginaria; // ilegal, no accesible
Decimos que parteReal y parteImaginaria son
miembros privados de la clase Complejo.
PM. 2. POO (15)
Atributos public
Si queremos permitir el
acceso desde fuera a
los atributos de una
clase hay que
declararlos públicos:
class Complejo {
public:
float parteReal;
float parteImaginaria;
};
Podemos combinar
partes públicas y
privadas de una
clase:
class Complejo {
public:
float parteReal;
private:
float parteImaginaria;
};
PM. 2. POO (16)
Miembros
Si no son visibles los miembros privados de una clase…
¿cómo se pueden modificar o ver sus valores?
La respuesta es simple:
PM. 2. POO (17)
Miembros
Si no son visibles los miembros privados de una clase…
¿cómo se pueden modificar o ver sus valores?
La respuesta es simple:
Las funciones que necesiten acceder a los
datos miembros de una clase deben estar
declarados
dentro de la misma clase
PM. 2. POO (17)
Miembros
Si no son visibles los miembros privados de una clase…
¿cómo se pueden modificar o ver sus valores?
La respuesta es simple:
Las funciones que necesiten acceder a los
datos miembros de una clase deben estar
declarados
dentro de la misma clase
Son las funciones miembro o métodos.
PM. 2. POO (17)
Diferencia con las estructuras
En resumen
• Para controlar el uso de la información que
determina el estado de un objeto se deben usar
métodos
• Un objeto tiene un estado interno sólo
reconocible por las respuestas a las llamadas a
sus métodos
• Para modificar el estado de un objeto se debe
llamar a sus métodos de acceso
PM. 2. POO (18)
Ejemplo de métodos
class Complejo {
public:
void crear(int preal, int pimag);
void print();
private:
float parteReal;
float parteImaginaria;
};
PM. 2. POO (19)
Métodos públicos y privados
Vemos que los métodos para pueden ser
declarados:
• en la parte pública: son la interfaz de uso
• en la parte privada: procedimientos internos
necesarios para el funcionamiento del
objeto.
PM. 2. POO (20)
Ejemplo
interfaz de uso
procedimientos
internos
atributos
class Fraccion {
public:
void crear(int num, int denom);
void print();
private:
void reduce();
int numerador, denominador;
}
PM. 2. POO (21)
Métodos especializados
• En la mayoría de las clases es necesario dar
valores iniciales a sus atributos:
– Constructor
• Liberar memoria, cerrar canales, etc. al
destruirlo:
– Destructor
PM. 2. POO (22)
Métodos especializados
• Constructor
– Sería el miembro público cuyo nombre es el mismo
que el de la clase, sin devolver nada.
– Puede llevar parámetros
• Destructor
– Sería el miembro público cuyo nombre es el mismo
que el de la clase con un ~ delante, sin devolver nada,
ni void
• Constructor de copia
– Sería el miembro público cuyo nombre es el mismo
que el de la clase y cuyo único argumento es un
objeto de la clase en modo const &
PM. 2. POO (23)
Constructor
• Constructor
– Sería el miembro público cuyo nombre es el mismo
que el de la clase, sin devolver nada.
– Puede llevar parámetros
– Puede haber varios con distintas signaturas.
– El Constructor de copia es uno de los posibles
pueden inicializar directamente los atributos con la
notación
: atrib1(parám1), atrib2(parám2)
que se pone tras la declaración.
PM. 2. POO (24)
Ejemplo: complejo.h
interfaz de uso
atributos
class Complejo {
public:
constructor automático
Complejo(int re=0, int im=1) :
equivale a
real(re), imag(im) { };
{real=re; imag=im;}
void print() const; // no modif. objeto
void pon(const int re=0, const int im=1);
private:
int real, imag;
};
Complejo c1(3,2), c2;
PM. 2. POO (25)
Constructor, casos
• Ejemplos de uso de Constructor
Según encajen pueden ser llamados un
constructor u otro.
Complejo c(10, -2), c2(5,4), c3[8];
nótese que al tenerse valores por defecto, cuando no se ponen
parámetros como en el caso de los 8 elementos del array de
Complejo c3, todos toman el valor, los valores por defecto.
PM. 2. POO (26)
Cuerpo de los métodos
La mayoría de los métodos no se desarrollan en
línea, como los constructores sino que el
desarrollo (los cuerpos de los métodos) se
desarrollan en el fichero pareja de la cabecera .h y
que sería .cpp
.h
.cpp
en él se accede a los miembros de la clase
mediante la notación de ámbito
nombreClase::
PM. 2. POO (27)
Cuerpo de los métodos
.h
class Nave {
public:
Nave(const int x=0, const int y=0) {X=x;Y=y;};
Mover(const int dx, const int dy);
...
}
.cpp
Nave:: Mover(const int dx, const int dy) {
X += dx;
Y += dy;
}
PM. 2. POO (28)
Ejemplo: complejo.cpp
#include <iostream.h>
#include "complejo.h" // siempre al final
Complejo::pon(const int re, const int im) {
real = re;
imag = im;
}
Complejo::print() {
cout << real <<"+ i "<<imag;
}
PM. 2. POO (29)
Uso de una clase
Para usar una clase bastará entonces importar su
descripción (la declaración de la clase que está en
la cabecera .h) y crear objetos, etcétera, en el
programa:
PM. 2. POO (30)
Uso de una clase
ojo: se importa clase.h tanto en
clase.cpp como en prueba.cpp
clase.h
#include "clase.h"
clase.cpp
#include "clase.h"
prueba.cpp
PM. 2. POO (31)
Ejemplo: prucomp.cpp
#include <iostream.h>
#include "complejo.h"
llamada a miembro
int main() {
del objeto
Complejo c1;
c1.print(); cout << endl;
c1.pon(2,-3);
c1.print(); cout << endl;
}
PM. 2. POO (32)
Métodos destructor
• Destructor
– Sería el miembro público cuyo nombre es el mismo
que el de la clase con un ~ delante, sin devolver nada,
ni void
el destructor no siempre hace falta, sólo cuando se
llame a rutinas que modifiquen el estado del
sistema y antes de destruir el objeto haya que
reponer ese estado
PM. 2. POO (33)
Ejemplo: destructor en matriz.h
class Matriz {
public:
Matriz(int n, int m) :
N(n), M(m), datos(new int[n*m]) { };
~Matriz(void) { delete[ ] datos; };
int ver(const int fila,const int col);
void pon(const int fila,const int col,const int x);
void dims(int &n, int &m) {n=N;m=M;};
private:
int N, M;
int *datos;
};
PM. 2. POO (34)
Destrucción implícita
La destrucción implícita se llama sóla,
automáticamente, cuando el objeto deja de estar
accesible, por ejemplo, al acabar el ámbito de su
visibilidad
La destrucción puede llamarse explícitamente llamando al método
de destrucción aunque esto último suele ser innecesario
PM. 2. POO (35)
Construcción por copia de objetos
• Constructor por copia
– Sería el miembro público cuyo nombre es el mismo
que el de la clase y cuyo único argumento es un
objeto de la clase en modo const clase&
No siempre hace falta.
Al igual que el destructor, el método de copia hace
falta cuando la creación solicita memoria al
sistema, abre ficheros o cosas así, en general,
modificando el estado externo del sistema.
PM. 2. POO (36)
Construcción por copia de objetos
La construcción por copia se "ejecuta" con la declaraci:
• implícita en la creación:
Matriz m2 = m1;
donde m2, obsérvese que no lleva parámetros (obligados) del
constructor, por hacerse mediante copia.
•
explícita en la creación:
Matriz m3(m1);
donde de nuevo, no se usan los parámetros del constructor
automático por llamarse explícitamente al método
constructor por copia
PM. 2. POO (37)
Ejemplo: matriz.h
class Matriz {
public:
Matriz(int n, int m) :
N(n), M(m), datos(new int[n*m]) { };
~Matriz(void) { delete[ ] datos; };
Matriz::Matriz(const Matriz& origen);
int ver(const int fila,const int col);
void pon(const int fila,const int col,const int x);
void dims(int &n, int &m) {n=N;m=M;};
private:
int N, M;
int *datos;
};
PM. 2. POO (38)
Ejemplo: matriz.cpp
Matriz::Matriz(const Matriz& origen) {
int i, j;
N = origen.N; M = origen.M;
datos = new int[N*M];
for (i=0;i<N;i++)
for (j=0;j<M;j++)
pon(i,j, origen.ver(i,j));
}
int Matriz::ver(const int fil,const int col) {
return datos[fil*M + col];
}
void Matriz::pon(const int fil,const int col,
const int x) {
datos[fil*M + col] = x;
}
PM. 2. POO (39)
Ejemplo: prumatriz.cpp
int main()
{
Matriz m(10, 20);
m.pon(2,2,1);
printf("en m 2,2: %d\n", m.ver(2,2));
Matriz mm = m; // actúa el constructor de copia implícito
printf("en mm 2,2: %d\n", mm.ver(2,2));
Matriz mmm(m); // constructor de copia explícita
printf("en mmm 2,2: %d\n", mmm.ver(2,2));
return 0;
}
PM. 2. POO (40)
Copia de objetos
La iniciación de objetos durante la construcción admite pues diversas
formas:
ejemplo:
class T {
public:
T(): dato(valor), ... {}; // Creador sin parámetros
T(tipo uno): dato(uno), ... {}; // Creador con un parámetro
T(const T& de): dato(de.dato), ... {}; // constructor por copia
....
pero una operación como:
...
t1 = t2;
...
hará sólo la copia de los atributos, y no será correcta en general
PM. 2. POO (41)
Peligro de la copia de objetos en parámetros
Peor aún, si hacemos una llamada a una función externa
como:
...
pinta(t1);
...
siendo
void pinta(const T t1)
los resultados serán desastrosos.
¿Por qué?
PM. 2. POO (42)
Peligro de la copia de objetos en parámetros
El motivo es que pinta(t1) construye una copia de t1 para el
parámetro local de pinta y no sólo hace esa copia sino
que al acabar pinta() destruye esa copia.
Una solución es hacer
void pinta(const T& t)
• sea, declarar el parámetro como referencia constante
– No se copia
– No se cambia
PM. 2. POO (43)
Copia de objetos en parámetros
Al recibirse el objeto por referencia no hay
destrucción automática al acabar el procedimiento
y no habrá sorpresas en los casos de memoria
dinámica enlazada.
void pinta(const T& t)
PM. 2. POO (44)
Copia por asignación de objetos en general
Pero el problema de la copia es más general: sólo
hemos visto como evitarlo durante la creación por
copia y durante el paso de parámetros.
Para que la operación = funcione correctamente con
nuestros objetos particulares hace falta hará falta
sobrecargar el operador asignación con nuestro
algoritmo particular de copiado.
T& operator= (const T& orig);
PM. 2. POO (45)
Copia por asignación de objetos en general
ejemplo:
class T {
public:
T(); // Creador sin parámetros
T(tipo uno); // Creador con un parámetro
T(const T& de); // constructor por copia
T& operator= (const T& de); // operador de copia
....
PM. 2. POO (46)
Miembros amigos
¿Puede ser posible que otra clase acceda a los miembros de
una clase?: Sí
permiso garantizado
class Una {
friend class Otra;
private:
int atri;
void metodo();
};
class Otra {
void metodo2(Una u) {
u.atri = 33;
u.metodo();
}
};
PM. 2. POO (47)
Sobrecarga de <<
Si se define la clase << como amiga
friend ostream& operator<<(ostream &os, const Text& orig);
y se desarrolla como por ejemplo:
ostream& operator<<(ostream &os, const Text& orig)
{
os << orig.s; // accede a su parte privada
return os;
}
PM. 2. POO (48)
Ejemplo +completo
class Text {
public:
Text(const char* txtPtr);
Text() : s(0) {};
~Text();
Text(const Text& orig);
bool operator== (const Text& derecho) const;
char operator[ ] (unsigned long indice) const;
unsigned long Longitud() const;
void pinta(const bool nl = true) const;
friend Text operator+ (const Text& ls, const Text& rs);
operator const char*() const;
Text& operator= (const Text& orig);
Text& operator= (const char *str);
friend ostream& operator<<(ostream &os, const Text& orig);
private:
char *s; // la cadena C
unsigned long l;
unsigned long strlen(const char *str) const;
void strasigna(const char *desde);
bool strcmp(const char *con) const;
Ejemplo +completo
};
class Text {
public:
Text(const char* txtPtr);
Text() : s(0) {};
~Text();
Text(const Text& orig);
bool operator== (const Text& derecho) const;
char operator[ ] (unsigned long indice) const;
unsigned long Longitud() const;
void pinta(const bool nl = true) const;
friend Text operator+ (const Text& ls, const Text& rs);
operator const char*() const;
Text& operator= (const Text& orig);
Text& operator= (const char *str);
friend ostream& operator<<(ostream &os, const Text& orig);
private:
char *s; // la cadena C
unsigned long l;
PM. 2. POO (49)
uso:
t1 == t2
t1[33]
t3 = t1 + t2
(char*) (t2)
t1 = t2
t1 = "abc"
cout << t1;
unsigned long strlen(const char *str) const;
void strasigna(const char *desde);
bool strcmp(const char *con) const;
};
PM. 2. POO (50)
Ejemplo +completo
#include <iostream.h>
#include "text.h"
// Constructor desde cadena C
Text::Text(const char* txtPtr) : s(0), l(0) {
if (txtPtr != 0)
strasigna(txtPtr);
}
// Constructor de copia
Text::Text(const Text& orig) : s(0), l(orig.l) {
if (orig.l != 0)
strasigna(orig.s);
}
// destructor
Text::~Text() {
delete[] s;
}
bool Text::operator== (const Text& derecho)
const {
if (derecho.l == 0 && 0 == l)
return true;
else
return strcmp(derecho.s);
}
char Text::operator[] (unsigned long indice) const
{
if (0 <= indice && indice < l)
return s[indice];
else
return '\0';
}
// averigua la longitud
unsigned long Text::Longitud() const {
return l;
}
// Adapta el tipo (cast) a cadena C
Text::operator const char*() const {
return s;
}
Ejemplo +completo
PM. 2. POO (51)
// Operador de asignación
Text& Text::operator= (const Text& orig) {
// evita a = a;
if (this == &orig)
return *this;
if (l != 0) {
delete[] s;
l = 0;
}
if (orig.l)
strasigna(orig.s);
return *this;
}
// Operador de asignación desde una str
Text& Text::operator= (const char *str) {
if (l != 0) {
delete[] s;
l = 0;
}
if (str != 0)
strasigna(str);
return *this;
}
unsigned long Text::strlen(const char *str) const {
unsigned long n = 0;
while (str[n])
n++;
return n;
}
void Text::strasigna(const char *desde) {
unsigned long i = 0;
l = strlen(desde);
s = new char[l+1];
while (s[i] = desde[i])
i++;
}
bool Text::strcmp(const char *con) const
{
unsigned long i = 0;
while (s[i] && con[i] && s[i] == con[i])
i++;
return s[i] == '\0' && con[i] == '\0';
}
PM. 2. POO (52)
Ejemplo +completo
// amigo que permite sobrecargar '+' para concatenar Texts
Text operator+ (const Text& ls, const Text& rs) {
Text ret;
unsigned long i=0, ii=0;
ret.l = ls.l + rs.l;
ret.s = new char[ret.l+1];
ret.s[0] = '\0';
if (ls.s != NULL) {
while (ret.s[i] = ls.s[i])
i++;
ii = i-1;
i = 0;
}
if (rs.s != NULL)
while (ret.s[ii] = rs.s[i])
i++; ii++;
return ret;
}
ostream& operator<<(ostream &os, const Text& orig) {
os << orig.s; return os; }
PM. 2. POO (53)
espacios de nombres
Una declaración accesible en todo el programa es
una declaración global.
La colección de todas las declaraciones se
denomina espacio de nombres global.
PM. 2. POO (54)
using namespace
C++, sin embargo, proporciona a los programadores la
posibilidad de situar las declaraciones en espacios de
nombres definidos por el programa.
namespace nombre {
void f() { cout << desde f en nombre<<endl;
};
la llamada hay que hacerla:
nombre::f();
excepto que se ponga antes:
using namespace nombre;
PM. 2. POO (55)
librerías con namespace
Las cabeceras que utilizan el espacio de nombres
global suelen acabarse como clásicamente: .h
Las librerías que sitúan sus declaraciones dentro
de espacios de nombres (no globales) no usan
extensión.
Por ejemplo: <iostream> es un archivo de
cabecera cuyas declaraciones están en el
espacio de nombres std
PM. 2. POO (56)
librerías con namespace
Por eso se suele hacer:
#include <iostream>
#include <iomanip>
using namespace std;
int main () {
cout << setw (10);
cout << 77 << endl;
return 0;
}
PM. 2. POO (57)
Miembros estáticos
PM. 2. POO (58)
Miembros estáticos
Se pueden definir miembros (atributos o métodos) con el
atributo static:
PM. 2. POO (58)
Miembros estáticos
Se pueden definir miembros (atributos o métodos) con el
atributo static:
• Tal miembro es común a todos los objetos de tal clase.
PM. 2. POO (58)
Miembros estáticos
Se pueden definir miembros (atributos o métodos) con el
atributo static:
• Tal miembro es común a todos los objetos de tal clase.
• Tales miembros existen aunque no haya ningún objeto
de tal clase
PM. 2. POO (58)
Miembros estáticos
Se pueden definir miembros (atributos o métodos) con el
atributo static:
• Tal miembro es común a todos los objetos de tal clase.
• Tales miembros existen aunque no haya ningún objeto
de tal clase
• Para llamarlos se puede usar un objeto cualquiera de
los de la clase o usar el especificador de ámbito
Clase::metod()
PM. 2. POO (58)
Miembros estáticos
Se pueden definir miembros (atributos o métodos) con el
atributo static:
• Tal miembro es común a todos los objetos de tal clase.
• Tales miembros existen aunque no haya ningún objeto
de tal clase
• Para llamarlos se puede usar un objeto cualquiera de
los de la clase o usar el especificador de ámbito
Clase::metod()
• Pueden servir, por ejemplo, para mantener información
global, como la cuenta de todos los objetos. No son
datos globales, ya que mantienen los criterios de
visibilidad.
PM. 2. POO (58)
FIN
PM. 2. POO (59)
Descargar