Racional

Anuncio
Tema 2.
Programación basada
en objetos
Programación Avanzada
Ingeniería Técnica en
Informática de Gestión
Jorge Badenas
2.1. Objetivos

Estudiar los conceptos de clase y
objeto, y su implementación
mediante el lenguaje C++.

Constructores y destructores.

Sobrecarga de operadores.
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
2
2.2. Encapsulación de los
datos

Encapsulación de los datos
ListinTelefonico
cap
construirListin
insertarTelefono
buscarTelefono
Interfaz
destruirListin
main
insercion
Programa
busqueda
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
3
2.2. Encapsulación de los datos

El interfaz amortigua los efectos
de los cambios internos de la
estructura.

Si no es preciso cambiar el
interfaz, los cambios no afectarán
al programa.
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
4
2.3 Características beneficiosas
Mayor abstracción.
abstracción Entidades de
más alto nivel.
 Modularidad
Modularidad. Agrupación de
estructuras de datos con las
funciones que facilitan su
manipulación.
 Ocultación de la información.
información
Para usar algo no es preciso saber
cómo está hecho.
 Encapsulación de los datos.
datos El
acceso a los datos se hace a través
de un interfaz.

La encapsulación es la piedra
angular que hace que las otras tres
se cumplan.
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
5
2.4. Objetos


Necesitamos un mecanismo en los
lenguajes de programación que
obligue a los programas a manejar
los datos a través de la interfaz.
Los objetos son ese mecanismo
que permite la encapsulación.
Error de
compilación
Objeto
cap
construirListin
insertarTelefono
Acceder
directamente
a los datos
buscarTelefono
Interfaz
destruirListin
main
insercion
Programa
busqueda
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
6
2.5. Objetos y clases


Un objeto es una instancia de una
clase.
clase
Una clase contiene dos tipos de
elementos:



Dos tipos de elementos en una
clase según el tipo de acceso:




Datos
Funciones que manejan los datos.
Privados
Públicos
La interfaz de la clase lo forman las
funciones públicas.
El resto de las funciones del
programa que no pertenecen a la
clase están obligadas a usar los
datos a través de la interfaz.
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
7
2.6 Transformando un Tipo
de Dato en una Clase
struct Hora {
int minutos;
int horas;
};
void ponerEnHora( Hora& oh, int h, int m ) {
oh.minutos = m;
oh.horas = h;
if( m>59 )
normalizar( oh );
}
void normalizar( Hora& oh ) {
oh.horas += oh.minutos / 60;
oh.minutos = oh.minutos % 60;
}
void mostrarHora( const Hora &oh ) {
cout<<oh.horas<<“:”<<oh.minutos;
}
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
8
2.6 Transformando un Tipo
de Dato en una Clase
class Hora {
public:
void ponerEnHora( int h, int m);
void mostrar( );
private:
int horas;
int minutos;
void normalizar( );
};
Para definir una clase se pueden
usar dos palabras clave:





class
struct (elementos públicos)
Los datos serán privados.
Las funciones que permitían el
manejo de la estructura pasarán a
pertenecer a la clase.
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
9
2.6.1 Miembros de una clase.
Las funciones y datos que pertenecen a
la clase reciben el nombre de
miembros de la clase.
clase







Métodos  Funciones
Atributos  Datos
Los miembros privados son
inaccesibles desde las funciones que
no pertenecen a la clase
(Encapsulación
Encapsulación).
Las funciones públicas son la interfaz
de la clase.
Los datos de una clase son
equivalentes a los campos de un tipo de
dato struct de C.
Las funciones de una clase están
incrustadas en ella, por lo tanto no
necesitan recibir el objeto, YA LO
TIENEN.
TIENEN
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
10
2.6.2. Declaración y uso de las
clases
// Sin clases
int main( ) {
Hora h;
ponerEnHora( h, 6, 50 );
mostrar( h );
normalizar( h );
h.minutos = 80;//Contenido incorrecto
return 0;
}
// Con clases
int main( ) {
Hora h;
h.ponerEnHora( 6, 50 );
h.mostrar( );
h.normalizar( ); // ERROR
h.minutos = 80; // ERROR
return 0;
}
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
11
2.6.3. Definición de
funciones miembro
Se debe poner el nombre de la
clase
void Hora::mostrar( ) {
cout<< horas << “:” <<minutos;
}
void Hora::normalizar( ) {
horas += minutos / 60;
minutos = minutos % 60;
}
Cuando estoy en
la clase se
sobreentiende
que hablo de
Hora::minutos
void Hora::ponerEnHora( int h, int m ) {
minutos = m;
horas = h;
if( m>59 )
normalizar( );
}
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
12
Convertir en una clase:
/* Archivo Cuenta.h */
struct Cuenta{
char titular[30];
int numero;
float saldo;
};
void asignarSaldo ( Cuenta &, float = 0.0);
void inicializar ( Cuenta &, char *, int ,
float = 0.0);
/*Archivo Cuenta.cpp */
#include <iostream>
using namespace std;
#include “Cuenta.h
Cuenta.h””
void asignarSaldo (Cuenta &c, float s) {
if (s<0.0) { cerr<<"SALDO NEGATIVO";
s=0.0;}
c.saldo=s;
}
void inicializar (Cuenta &c, char *t, int n,
float s) {
strcpy (c.titular, t);
c.numero=n;
asignarSaldoCuenta (c, s);
} Programación Avanzada - Ingeniería Técnica en Informática de Gestión
13
…viene de la página anterior
/* Archivo Principal.cpp */
int main ( ) {
Cuenta c1, c2;
inicializar (c1, "Luis López", 35006);
inicializar(c2, "Jesús García", 35007,
1000);
asignarSaldo (c1, 500.0);
asignarSaldo ( c2 );
return 0;
}
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
14
2.7. Funciones inline


Si una función es breve, se puede
escribir su código dentro de la
estructura class.
Entonces es una función inline.
class Hora {
public:
…
int leerMinutos( ) { return minutos; }
int leerHoras( ) { return horas; }
…
};
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
15
2.8. Funciones const

Cuando una función no modifica al
objeto propietario de la función es
conveniente definirla como const.
class Hora {
public:
…
int leerMinutos( ) const { return minutos; }
int leerHoras( ) const { return horas; }
…
};
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
16
2.9. Constructores



Constructor Función que se
Constructor:
ejecuta automáticamente al crearse
un objeto.
Propósito: Inicializar el objeto.
Características:



Su nombre es el de la clase.
No devuelve nada.
Puede haber varios, por ejemplo:



Los dos anteriores, el compilador
los proporciona de forma implícita:



Uno sin parámetros: por defecto.
Uno con un parámetro de la misma
clase: copia.
Si declaro un constructor, pierdo el
implícito por defecto.
Cualquiera de los dos puedo declararlo
para redefinirlo a mi gusto.
Crear un objeto siempre supone
usar un constructor... y ha de
decidirse cuál en cada caso.
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
17
2.10. Destructores


Destructor Función que se
Destructor:
ejecuta automáticamente cuando
un objeto deja de existir.
Propósito: Liberar los recursos
que el objeto tuviera ocupados
(memoria, ficheros, etc.).
class Lista {
Nodo *cap;
public:
~Lista( );
…
};
Lista::~Lista( ) {
Nodo *nt;
while( cap != NULL ) {
nt = cap;
cap = cap->sig;
delete nt;
}
}

Si no, hay un destructor implícito.
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
18
2.11. El constructor copia




Construye un objeto a partir de otro
de la misma clase.
Hay uno implícito que copia
miembro a miembro.
Si el constructor copia implícito no
es adecuado, debo reescribirlo.
Dada una clase A, el constructor
copia se define como:
class A {
…
A( const A&origen);
…
};

Se trata de inicializar un objeto de
la clase A a partir de otro objeto de
la clase A (origen).
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
19
2.12. Constructores y arrays.


Los objetos de un array también se
construyen y se destruyen.
Paso de parámetros a constructores
de arrays:
class Complejo {
public:
Complejo(float r=0.0, float i=0.0) {...}
...
};
int main() {
Complejo vc1[10];
Complejo vc2[3]= {1.0, 2.0, 3.0};
Complejo vc3[2]= {Complejo(1.0, 1.0),
Complejo(2.0, 2.0)};
Complejo * pc, * pvc;
pc = new Complejo(1.1, 2.2);
pvc = new Complejo[5];
...
delete pc; delete[] pvc;
...
}
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
20
2.13. Funciones y clases
amigas (friend)



Una función amiga es una función qué
aunque no pertenece a una clase recibe
el privilegio de poder acceder a la parte
privada.
Para hacer una función amiga de una
clase se ha de poner en la clase la
palabra friend seguida del prototipo de la
función.
Si se quiere que todas las funciones de
una clase A puedan acceder a la parte
privada de una clase B, se definirá la
clase A como amiga de B.
class B {
…
friend class A;
…
};
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
21
2.14. this

this es la dirección (inmutable) del
objeto sobre el que se está
ejecutando una función miembro.
class A {
A* direccion( ) { return this; }
A objeto( ) { return *this; }
A& objetoReferencia( ) { return *this; }
void escrX( int v) { this->x = v; }
private:
int x;
};
…
// En alguna función
A a, b;
A* pa = a.direccion( ); // pa = &a
A* pb = b.direccion( ); // pb = &b
b = a.objeto( );
// b=a
a.objetoReferencia( ).escrX( 10 );
// Lo mismo que a.escrX( 10 )
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
22
2.15. Sobrecarga de
operadores

La sobrecarga de operadores
consiste en asociar funciones a
operadores.


Podemos sobrecargar los
operadores que existen, pero no
podemos:



El operador es una abreviatura que
implica la ejecución de una función.
Inventar operadores nuevos.
Cambiar el número de operandos
que requiere un operador.
Sobrecargar el operador + consiste
en escribir una función llamada
operator+. Lo mismo para el resto
de operadores.
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
23
2.15 Sobrecarga de operadores
class Racional {
public:
Racional( int n, int d = 1):
num( n ), den( d ) { }
Racional( ) { }
private:
int num; // Numerador
int den; // Denominador
};

Queremos que funcione:
Racional a, b, c;
a = b + c;
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
24
2.15 Sobrecarga de operadores



En a
= b + c
hay dos operadores:

b + c  Racional + Racional

a=Resultado(b+c)  Rac = Rac
Para sobrecargar b + c debemos
escribir una función llamada
operator+.
operator+ puede tener dos formas:

Miembro de la clase Racional:
b.operator+( c )

No miembro:
operator+( b, c )


Podemos escoger la forma que
queramos.
Según la que elijamos tendremos
distinto número de parámetros.
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
25
2.15 Sobrecarga de operadores

¿Qué debe devolver b + c?
Racional + Racional  Racional

¿Qué debe hacer b


+ c?
Sumar b más c y devolver el
resultado
¿Modificar a los objetos b o c?
No es lo que ocurre con los tipos de
datos del lenguaje (int, float,
etc.)
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
26
2.15 Sobrecarga de operadores

Como no miembro, la función sería:
Racional operator+( const Racional& b,
const Racional& c ) {
Racional tmp;
tmp.num = b.num*c.den + b.den*c.num;
tmp.den = b.den*c.den;
return tmp;
}

Si accede a miembros privados desde una
función no miembro, la función debe ser
friend:
class Racional {
friend Racional operator+(const Racional& b,
const Racional& c
);
… // Resto del class
};
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
27
2.15 Sobrecarga de operadores

Falta el operator=
Racional = Racional

No es necesario implementarlo,
porque todas las clases tienen una
versión implícita de ese operador.
Racional& operator=(const Racional&);



Realiza la asignación de cada uno
de los datos miembros de la clase.
Lo deberemos redefinir cuando
necesitemos que este operador
haga algo diferente.
Pregunta: ¿funcionará?:
a = b + c + d;
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
28
2.16. Racional += Racional

Haremos que la función sea
miembro de la clase Racional



a += b
 a.operator+=(
b )
Un operador no es normal que
retorne void, así que haremos que
devuelva Racional.
La función debe modificar el primer
operando racional.
class Racional {
Racional& operator+=( const Racional& );
…
};
Racional& Racional::operator+=(
const Racional& b ) {
num = num * b.den + den * b.num;
den *= b.den;
return *this;
}
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
29
2.17. Racional == Racional


¿Qué debe devolver a == b? bool.
Mejor como función externa:
a == b

operator==(a, b)
class Racional {
...
friend bool operator==(const Racional&,
const Racional&);
...
};
...
bool operator==(const Racional& a,
const Racional& b) {
return a.num/a.den == b.num/b.den;
}


Problema con los tipos de datos:
4/5 == 1/3  ¿true?
¿Sobrecargamos también !=?
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
30
2.18. -Racional

Uso:
Racional q, p;
p = -q; // p = q.operator-( );

Declaración:
class Racional {
Racional operator-( ) const;
…
};

Código de la función:
// Versión 1
Racional Racional::operator-( ) const {
Racional aux;
aux.num = -num;
aux.den = den;
return aux;
}
// Versión 2
Racional Racional::operator-( ) const {
return Racional( -num, den );
}
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
31
2.19. Operaciones entre
clases distintas
Si se quiere poder sumar
Complejo y Racional con el
operador +, hay dos opciones:



Sobrecargar operator+ para
Complejo y Racional.
Suponiendo que ya se dispone de
la suma de complejos, especificar
la conversión de Racional en
Complejo. De esta forma, en
Complejo + Racional:
1. Se convierte automáticamente el
Racional en un Complejo.
2. Se efectúa la suma con la sobrecarga
del operator+ para dos complejos.
2.20. Complejo + Racional
Complejo c1, c2;
Racional q;
c1 = c2 + q;

Declaración en las clases:
class Complejo {
Complejo operator+( const Racional&)const;
…
};
class Racional {
friend Complejo Complejo::operator+(
const Racional& ) const;
…
};

Código de la función:
Complejo Complejo::operator+(
const Racional& q)const {
float rac =
static_cast<float>( q.num ) / q.den ;
return Complejo( real + rac,imaginaria );
}
2.21. Racional + Complejo
Complejo c1, c2;
Racional q;
c1 = q + c2;

Declaración en las clases:
class Complejo {
friend Complejo operator+( const Racional&,
const Complejo& );
…
};
class Racional {
friend Complejo operator+(const Racional&,
const Complejo& );
…
};

Código de la función:
Complejo operator+( const Racional& q,
const Complejo& c) {
float rac =
static_cast<float>( q.num ) / q.den ;
return Complejo(c.real+rac,c.imaginaria );
}
2.22. Conversión mediante
constructores

El constructor permite convertir un
objeto a otra clase diferente.
class Complejo {
public:
Complejo(const Racional&);
...
friend Complejo operator+(const Complejo&,
const Complejo&);
private:
float real, imaginaria;
};
Complejo::Complejo(const Racional& q) {
real = static_cast<float>(q.num) / q.den;
imaginaria = 0.0;
}

Se puede evitar que se produzcan
conversiones implícitas (coerciones),
poniendo la palabra explicit
delante del constructor.
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
35
2.22. Conversión mediante
constructores
Racional r;
Complejo c1, c2;
c1 = c2 + r;
C1 = r + c2;

El objeto r primero se convierte en
un Complejo mediante el
constructor, después se llama a la
sobrecarga del operador + que
suma dos complejos.
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
36
2.22 ++Racional y Racional++
class Racional {
Racional& operator++( );
Racional operator++( int );
…
};
Racional& Racional::operator++( ) {
num += den;
return *this;
}
Racional Racional::operator++( int ) {
Racional tmp = *this;
num += den;
return tmp;
}

El parámetro entero es artificial,
sólo sirve para diferenciar el
operador posfijo del prefijo.
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
37
2.23. Operadores de E/S

Se trata de sobrecargar los
operadores >> y <<.
Racional q;
cout << “Dame un racional... ”;
cin >> q;
cout << “q vale ” << q << endl;


cout pertenece a la clase ostream,
mientras que cin pertenece a
istream.
Estos operadores no pueden ser
funciones miembro, porque el
operando de la izquierda no es el
de mi clase.
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
38
2.23. Operadores de E/S

El operador de salida es <<:
Racional q(2, 3);
cout << q; // operator<<(cout, q);

Se debe devolver por referencia el
operando izquierdo (un ostream)
para poder ordenar más salidas en la
misma expresión:
cout << q1 << “ y ” << q2 << endl;

Posible implementación:
ostream& operator<<(ostream& os,
const Racional& q) {
os << q.num << ‘/’ << q.den;
return os;
}

Si se accede a datos privados, la
función tendrá que ser friend.
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
39
2.23. Operadores de E/S

El operador de entrada es >>:
Racional q;
cin >> q; // operator>>(cin, q);


El formato que lee operator>>
debe ser similar al que escribe
operator<<.
Posible implementación:
istream& operator>>(istream& is,
Racional& q) {
char c; // para leer ‘/’
is >> q.num >> c >> q.den;
return is;
}

Consideraciones análogas al caso
anterior, pero:


en vez de ostream.
Operando derecho, por referencia
no const.
istream
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
40
2.24. El operador =

Dada una clase C, su cabecera es:
C& C::operator=(const C&);



Todas las clases disponen de un
operator= implícito, que debe
redefinirse si no es adecuado.
El operator= implícito asigna los
datos de un objeto al otro, miembro
a miembro.
No se debe confundir el constructor
copia con el operator=. Al
constructor sólo se le puede llamar
cuando se está inicializando un
objeto que es nuevo.


Pero puede ser útil definirlo a partir
de la asignación, hecha sobre *this.
Es importante prever casos como
a=a; (autoasignación).
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
41
2.25. Operador de indexación

Para acceder a un elemento de un
objeto contenedor mediante su
índice (no siempre int):
obj[ind]  obj.operator[](ind)
// acceso al elemento ind de obj

Por ejemplo, para que funcione:
VectorFloat v(10); // Diez flotantes
float x;
...
x = v[5]; // Lectura
v[6] = x; // Escritura


Para poder escribir, operator[] no
puede devolver una copia del
elemento, sino una referencia a él.
Para poder leer incluso si el
contenedor es const, necesitamos
una versión que devuelva copia o
referencia const.
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
42
2.25. Operador de indexación
class VectorFloat{
public:
// Para lectura y escritura (Op1)
float& operator[](int i)
{ return elementos[i]; }
// Para sólo lectura (Op2)
float operator[](int i) const
{ return elementos[i]; }
...
private:
int nelems; // Número de elementos
int *elementos;
};
void raro(VectorFloat& vvble,
const VectorFloat& vcte) {
float x;
...
x = vvble[0];
// Versión Op1
x += vcte[1];
// Versión Op2
vvble[2] = 2*x; // Versión Op1
}
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
43
2.26. Operador de llamada

Para utilizar un objeto como si fuera
una función:
obj(arg1,... argn)

obj.operator()(arg1,... argn)


Puede definirse para el número de
argumentos que se desee.
Por ejemplo, para acceso a
elementos de una matriz:
class MatrizFloat{
public:
float& operator()(int i, int j)
{ return elementos[i*ncols+j]; }
float operator()(int i, int j) const
{ return elementos[i*ncols+j]; }
...
private:
int nfils, ncols;
int *elementos;
};
Programación Avanzada - Ingeniería Técnica en Informática de Gestión
44
Descargar