Clases y objetos

Anuncio
Clases y objetos
Álvaro Sánchez Miralles
José Porras
(Fidel Fernández)
Índice
• Conceptos iniciales
• Clases y objetos
– Ejemplo para romper el hielo
– Definiciones
– Vida de un objeto
• Funciones miembro
–
–
–
–
–
–
Constructor de un objeto
Destructor de un objeto
Sobrecarga de operadores
Paso de parámetros por referencia
Sobrecarga de constructor de copia
Mejora del ejemplo
• Diseño básico en C++
Clases y objetos - 2
Conceptos iniciales
Modificador de tipo const
• Indica que un tipo permanece constante
– Sólo vale para evitar errores en tiempo de desarrollo
const int LON=5;
LON = 6;
// ERROR
int vi[LON];
const int *pi = vi;
vi[0]=0;
pi++;
*pi=1; // ERROR
int * const pii = vi;
pii[0]=1;
pii++;
// ERROR
const int * const piii = vi;
piii++;
// ERROR
piii[1]=3;
// ERROR
Clases y objetos - 4
Memoria dinámica I
• new
– Reserva memoria
• delete
– Libera memoria
main () {
int *pi;
pi = new int;
double *pd=new double;
delete pd;
delete pi;
int *pi=new int[10];
delete [] pi;
}
• Todo new tiene un delete asociado
Clases y objetos - 5
Memoria dinámica II
• ¿Qué está mal?
main() {
int *pi;
pi = get_vector_discreto(1, 4);
}
/** función que devuelve un vector con los valores enteros
* comprendidos entre min y max*/
int* get_vector_discreto(int min, int max){
int i, v[max-min];
for (i=0; i<max-min; i++)
v[i] = i;
return v;
v es local !!!!
}
Clases y objetos - 6
Memoria dinámica III
• Solución con memoria dinámica
#define MAX 50
main() {
int* pi;
pi = get_vector_discreto(1, 4);
}
¿Qué está
mal?
int* get_vector_discreto(int min, int max){
int i;
int *pi = new int[max-min];
for (i=0; i<max-min; i++)
pi[i] = i;
return pi;
}
Clases y objetos - 7
Memoria dinámica IV
• Solución con memoria dinámica
main() {
int* pi;
pi = get_vector_discreto(1, 4);
delete [] pi;
}
int* get_vector_discreto(int min, int max){
int i;
int *pi = new int[max-min];
for (i=0; i<max-min; i++)
pi[i] = i;
return pi;
}
Clases y objetos - 8
Clases y objetos
Ejemplo CComplejo I
#ifndef CCOMPLEJO_H
#define CCOMPLEJO_H
CComplejo.h
/**
* Descripcion: clase que representa a los números complejos
*/
class CComplejo {
private:
double m_d_real; /** parte real*/
double m_d_imag; /** parte imaginaria*/
public:
// constructores y destructor
CComplejo();
CComplejo(double r, double i);
~CComplejo();
// funciones de acceso a miembros
double getReal();
double getImag();
};
#endif
Clases y objetos - 10
Ejemplo CComplejo II
#include "CComplejo.h"
#include <iostream>
CComplejo.cpp
using namespace std;
// ---------- constructores
CComplejo::CComplejo(){
m_d_imag = 0; m_d_real = 0;
cout << "Me han creado por defecto" << endl;
}
CComplejo::CComplejo(double r, double i){
m_d_imag = i;
m_d_real = r;
cout << "Me han creado con r = " << m_d_real
<< " e i = " << m_d_imag << endl;
}
Clases y objetos - 11
Ejemplo CComplejo III
CComplejo::~CComplejo(){
cout << "Me han eliminado (" << m_d_real
<< "," << m_d_imag << ")" << endl;
}
// ---------- funciones de acceso a miembros
double CComplejo::getReal(){
return m_d_real;
}
CComplejo.cpp
double CComplejo::getImag(){
return m_d_imag;
}
Clases y objetos - 12
Ejemplo CComplejo IV
#include "CComplejo.h"
main.cpp
void pruComplejos(){
CComplejo C1,C2;
cout << "Dentro de pruComplejos" << endl;
Me han creado por defecto
}
Antes de llamar a pruComplejos
Me han creado por defecto
void main(void)
Me han creado por defecto
{
Dentro de pruComplejos
CComplejo C1;
Me han eliminado
cout << "Antes de llamar a pruComplejos"
<< (0,0)
endl;
Me han eliminado (0,0)
pruComplejos();
Fuera
pruComplejos
cout << "Fuera de pruComplejos"
<< de
endl;
Me han creado con r = 0 e i = 3
CComplejo C2(0,3);
Antes del final
cout << "Antes del final" << endl;
Me han eliminado (0,3)
}
Me han eliminado (0,0)
Clases y objetos - 13
Ejemplo CComplejo V
• ¿Y el main con memoria dinámica?
void main(void)
¿Qué está
{
mal?
CComplejo *C1 = new CComplejo();
cout << "Antes de llamar a pruComplejos" << endl;
pruComplejos();
cout << "Fuera de pruComplejos" << endl;
CComplejo *C2 = new CComplejo(0,3);
cout << "Antes del final" << endl;
}
Clases y objetos - 14
Ejemplo CComplejo VI
• ¿Y el main con memoria dinámica?
void main(void)
{
CComplejo *C1 = new CComplejo();
cout << "Antes de llamar a pruComplejos" << endl;
pruComplejos();
cout << "Fuera de pruComplejos" << endl;
CComplejo *C2 = new CComplejo(0,3);
cout << "Antes del final" << endl;
delete C1;
delete C2;
}
Clases y objetos - 15
Definición de una clase I
• Una clase es un tipo de datos que tiene cuatro
atributos asociados:
– Variables miembro
– Funciones miembro
– Niveles de acceso tanto a las variables miembro como a
las funciones miembro.
• private: no se puede acceder desde fuera de la
clase. Oculta la información.
• public: se puede acceder desde fuera
• protected: sólo las clases hijas de esta clase pueden
acceder a la información (se verá más adelante).
– Un nombre como etiqueta que sirve para identificar al
nuevo tipo definido por la clase
Clases y objetos - 16
Definición de una clase II
class CClase {
// Privada por defecto
public:
// Accesible desde fuera
protected:
// Acceso restringido
private:
// Inaccesible desde fuera
};
Clases y objetos - 17
Definición de una clase III
• Elementos de una clase:
– Variables miembro (Atributos). En el nombre se añade al
principio m_, como por ejemplo double m_d_real.
– Funciones miembro (Métodos).
• Un objeto es un elemento de una determinada clase.
– Clase número complejo CComplejo.
– C1, C2 son objetos de clase CComplejo.
• Estado del objeto
– Lo definen las variables miembro
– No se debe poder acceder desde fuera del objeto
• Comportamiento del objeto
– Lo definen las funciones miembro
• Objeto es a clase como variable es a tipo.
Clases y objetos - 18
Vida de un objeto
• Igual que un tipo básico
• Creación de un objeto
– Se reserva en memoria espacio para los datos internos del
objeto.
– Se llama a una función para inicializar estos datos:
constructor del objeto.
• Acceso al objeto a través de las funciones miembro
visibles.
• Destrucción de un objeto
– Al igual que después de reservar espacio se llama al
constructor del objeto, antes de destruirlo se llama al
destructor del objeto.
Clases y objetos - 19
Funciones miembro de una clase
Constructores de un objeto
• Son funciones miembro cuyo nombre es el nombre
de la clase.
– No se puede llamar directamente
• Cada vez que se crea un objeto de una clase, se
crea espacio de forma automática para las
variables miembro y a continuación se llama al
constructor para que termine de inicializarlas
• Siempre hay dos constructores por defecto:
– Sin parámetros: no hace nada.
– Desde otro objeto: inicializa las variables miembros con el
valor que tienen en el otro objeto.
• Se pueden definir constructores adicionales con
parámetros, que a su vez tengan valores por
defecto.
Clases y objetos - 21
Destructor de un objeto
• Es una función miembro de la clase cuyo nombre
es el nombre de la clase acompañado ~.
– No se puede llamar directamente
• Cuando un objeto va a ser eliminado de un
programa, antes de liberar la memoria asignada a
sus variables miembro, se llama al destructor.
• Hay un destructor por defecto que no hace nada.
• El destructor se puede sobrecargar:
– Esto es muy útil en el caso de variables que utilicen
memoria dinámica para poderla liberar.
• El destructor no lleva parámetros.
• Sólo puede haber un destructor
Clases y objetos - 22
El operador de asignación = (I)
• Al igual que en los tipos básicos (int, double, etc) las
clases tienen automáticamente asociadas un operador
de asignación (=) entre objetos de una misma clase.
• Por defecto, al asignar un objeto a otro de la misma
clase hay una asignación o copia miembro a miembro.
• Se puede redefinir esta asignación (sobrecarga del
operador =)
Clases y objetos - 23
El operador de asignación = (II)
void main(void)
¡OJO! Acceso a
{
miembro
CComplejo C1(0,1), C2;
C2 = C1;
cout << "C2 = (" << C2.getReal() << ","
<< C2.getImag() << ")" << endl;
}
Me
Me
C2
Me
Me
han creado con r = 0 e i = 1
han creado por defecto
= (0,1)
han eliminado (0,1)
han eliminado (0,1)
Clases y objetos - 24
El operador de asignación = (III)
• ¿Y el main con memoria dinámica?
void main(void)
¡OJO! Acceso a
{
miembro
CComplejo *C1, *C2;
C1 = new CComplejo(0,1);
*C2 = *C1;
cout << "C2 = (" << C2->getReal() << ","
<< C2->getImag() << ")" << endl;
delete C1;
delete C2;
}
Clases y objetos - 25
Sobrecarga de operador = (I)
class CComplejo {
private:
double m_d_real; /** parte real*/
double m_d_imag; /** parte imaginaria*/
public:
// constructores y destructor
CComplejo();
CComplejo(double r, double i);
~CComplejo();
// funciones de acceso a miembros
double getReal();
double getImag();
void operator=(CComplejo c);
};
Clases y objetos - 26
Sobrecarga de operador = (II)
• Es importante si la clase tiene miembros que son
memoria dinámica
void CComplejo::operator=(CComplejo c){
m_d_real=c.m_d_real;
m_d_imag=c.m_d_imag;
cout << "Operador igualdad" << endl;
}
¡OJO! Acceso a
zona privada
Clases y objetos - 27
Sobrecarga de operador = (III)
void main(void)
{
CComplejo C1(0,1), C2;
C2 = C1;
cout << "C2 = (" << C2.getReal() << ","
<< C2.getImag() << ")" << endl;
}
He creado dos objetos
y ¡ se eliminan tres !!
Me han creado con r = 0 e i = 1
Me han creado por defecto
Operador igualdad
Me han eliminado (0,1)
C2 = (0,1)
Me han eliminado (0,1)
Me han eliminado (0,1)
Clases y objetos - 28
Paso por referencia (I)
class CComplejo {
private:
double m_d_real; /** parte real*/
double m_d_imag; /** parte imaginaria*/
public:
// constructores y destructor
CComplejo();
CComplejo(double r, double i);
~CComplejo();
// funciones de acceso a miembros
double getReal();
double getImag();
void operator=(const CComplejo& c);
};
Clases y objetos - 29
Paso por referencia (II)
void main(void)
{
CComplejo C1(0,1), C2;
¡¡Uhm, qué
C2 = C1;
raro!!
cout << "C2 = (" << C2.getReal() << ","
<< C2.getImag() << ")" << endl;
}
void CComplejo::operator=(const CComplejo& c){
m_d_real=c.m_d_real;
m_d_imag=c.m_d_imag;
cout << "Operador igualdad" << endl;
}
Me han creado con r = 0 e i = 1
Me han creado por defecto
Operador igualdad
C2 = (0,1)
Me han eliminado (0,1)
Me han eliminado (0,1)
Clases y objetos - 30
Sobrecarga del constructor de copia (I)
class CComplejo {
private:
double m_d_real; /** parte real*/
double m_d_imag; /** parte imaginaria*/
public:
// constructores y destructor
CComplejo();
CComplejo(double r, double i);
CComplejo(const CComplejo& c);
~CComplejo();
// funciones de acceso a miembros
double getReal();
double getImag();
void operator=(const CComplejo& c);
};
Clases y objetos - 31
Sobrecarga de constructor de copia (II)
void copia(CComplejo c){
cout << "Dentro de copia (" << c.getReal() << ","
<< c.getImag() << ")" << endl;
}
void main(void)
{
CComplejo C1(0,1);
copia(C1);
}
¿Se puede quitar
la referencia?
CComplejo::CComplejo(const CComplejo& c){
m_d_real=c.m_d_real;
m_d_imag=c.m_d_imag;
cout << "Me han creado por copia de (" << c.m_d_real
<< "," << c.m_d_imag << ")" << endl;
}
Clases y objetos - 32
Sobrecarga de constructor de copia (III)
void copia(CComplejo c){
cout << "Dentro de copia (" << c.getReal() << ","
<< c.getImag() << ")" << endl;
}
void main(void)
{
CComplejo C1(0,1);
copia(C1);
}
Me han
Me han
Dentro
Me han
Me han
¿Cómo se evita la
copia?
creado con r = 0 e i = 1
creado por copia de (0,1)
de copia (0,1)
eliminado (0,1)
eliminado (0,1)
Clases y objetos - 33
Definición del operador << (I)
#include <iostream>
class CComplejo {
private:
double m_d_real; /** parte real*/
double m_d_imag; /** parte imaginaria*/
public:
// constructores y destructor
...
void operator=(const CComplejo& c);
friend std::ostream& operator<<(std::ostream &o,
const CComplejo& c);
};
Es una función que no es de la clase, pero que puede
acceder a la parte privada de la misma
Clases y objetos - 34
Definición del operador << (II)
ostream& operator<<(ostream &o, const CComplejo& c){
o << "(" << c.m_d_real << "," << c.m_d_imag << ")";
return o;
}
¡OJO! Acceso a
zona privada
void main(void)
{
CComplejo C1(0,1);
cout << "C1: " << C1 << endl;
}
Me han creado con r = 0 e i = 1
C1: (0,1)
Me han eliminado (0,1)
Clases y objetos - 35
Mejora de la clase CComplejo (I)
• ¿Que pasa si en vez de querer sacar por pantalla el
complejo en formato (r,i) se quiere en [r,i]?
CComplejo::CComplejo(const CComplejo& c){
m_d_real=c.m_d_real;
m_d_imag=c.m_d_imag;
cout << "Me han creado por copia de " << c << endl;
}
CComplejo::CComplejo(double r, double i){
m_d_imag = i;
m_d_real = r;
cout << "Me han creado con " << *this << endl;
}
CComplejo::~CComplejo(){
cout << "Me han eliminado " << *this << endl;
}
this es un puntero al objeto en ejecución
Clases y objetos - 36
Mejora de la clase CComplejo (II)
• Quitar más código duplicado
CComplejo::CComplejo(const CComplejo& c){
*this = c;
cout << "Me han creado por copia de " << c << endl;
}
Clases y objetos - 37
Funciones miembro de una clase
• Representan los métodos a través de los cuales se
establece relación con el objeto.
• El nombre de la función representa la acción a
realizar por el objeto.
• Para mantener la integridad del objeto, toda
comunicación con el objeto debería ser a través de la
funciones miembro:
• Si una función externa a la clase quiere acceder a
información interna como es el caso de >> o << de
iostream debe ser declarada como amiga en la
zona pública (friend).
• Se pueden sobrecargar los nombres de las funciones.
Clases y objetos - 38
Resumen hasta ahora
• Toda clase debe tener
–
–
–
–
–
Destructor
Constructor de copia que llama al operador =
Operador = sobrecargado
Constructor por defecto
Funciones de acceso a miembros
• Siempre que se pueda se pasan los objetos por
referencia
• No olvidarse del uso de const
Clases y objetos - 39
Definición del operador + (I)
CComplejo CComplejo::operator+(const CComplejo c){
CComplejo t;
t.m_d_real=m_d_real+c.m_d_real;
t.m_d_imag=m_d_imag+c.m_d_imag;
cout << "Suma de complejos" << endl;
return t;
}
Clases y objetos - 40
Definición del operador + (II)
void main(void)
{
CComplejo C1(0,1), C2(2,3), C3;
C3 = C1 + C2;
cout << "C3: " << C3 << endl;
Me han creado con (0,1)
}
Inicialmente
eran 3
objetos y he
necesitado 6
Me han creado con (2,3)
Me han creado por defecto
Operador igualdad
Me han creado por copia de (2,3)
Me han creado por defecto
Suma de complejos
Operador igualdad
Me han creado por copia de (2,4)
Me han eliminado (2,4)
Me han eliminado (2,3)
Operador igualdad
Me han eliminado (2,4)
C3: (2,4)
Me han eliminado (2,4)
Me han eliminado (2,3)
Me han eliminado (0,1) Clases y objetos - 41
Definición del operador + (III)
CComplejo CComplejo::operator+(const CComplejo& c){
CComplejo t;
t.m_d_real=m_d_real+c.m_d_real;
t.m_d_imag=m_d_imag+c.m_d_imag;
cout << "Suma de complejos" << endl;
return t;
}
Clases y objetos - 42
Definición del operador + (IV)
void main(void)
{
CComplejo C1(0,1), C2(2,3), C3;
C3 = C1 + C2;
cout << "C3: " << C3 << endl;
}
Me han creado con (0,1)
Me han creado con (2,3)
Sigue
Me han creado por defecto
habiendo
Me han creado por defecto
muchos
Suma de complejos
objetos
Operador igualdad
Me han creado por copia de (2,4)
Me han eliminado (2,4)
Operador igualdad
Me han eliminado (2,4)
C3: (2,4)
Me han eliminado (2,4)
Me han eliminado (2,3)
Me han eliminado (0,1)
Clases y objetos - 43
Definición del operador + (V)
CComplejo& CComplejo::operator+(const CComplejo& c){
CComplejo t;
t.m_d_real = m_d_real+c.m_d_real;
t.m_d_imag = m_d_imag+c.m_d_imag;
cout << "Suma de complejos" << endl;
return t;
}
CComplejo& CComplejo::operator+(const CComplejo& c){
m_d_real+=c.m_d_real;
m_d_imag+=c.m_d_imag;
cout << "Suma de complejos" << endl;
return *this;
}
Clases y objetos - 44
Definición del operador + (VI)
void main(void)
{
CComplejo C1(0,1), C2(2,3), C3;
C3 = C1 + C2;
cout << "C3: " << C3 << endl;
}
¡Aquí hay
algo raro!
Me han creado con (0,1)
Me han creado con (2,3)
Me han creado por defecto
Suma de complejos
Operador igualdad
C3: (2,4)
Me han eliminado (2,4)
Me han eliminado (2,3)
Me han eliminado (2,4)
Clases y objetos - 45
Definición del operador + (VII)
void CComplejo::operator+=(const CComplejo& c){
m_d_real+=c.m_d_real;
m_d_imag+=c.m_d_imag;
cout << "Suma monaria de complejos" << endl;
}
Clases y objetos - 46
Definición del operador + (VIII)
void main(void)
{
CComplejo C1(0,1), C2(2,3);
C1 += C2;
cout << "C1: " << C1 << endl;
}
Me han creado con (0,1)
Me han creado con (2,3)
Suma monaria de complejos
C1: (2,4)
Me han eliminado (2,3)
Me han eliminado (2,4)
Clases y objetos - 47
Definición del operador + (IX)
• Mejora del operador +
CComplejo CComplejo::operator+(const CComplejo& c){
CComplejo t = *this;
t += c;
return t;
}
Clases y objetos - 48
Sobrecarga de operadores
• Los operadores de C++ funcionan exactamente igual
que una función:
CComplejo C1,C2,C3;
C1=C2+C3;
C1.operator=(C2.operator+(C3));
• Siempre hay definido por defecto: = y & para cualquier
clase (por supuesto, se pueden sobrecargar).
• Permiten definir un álgebra de los objetos de una clase.
• No se debe abusar. Si a partir del operador no se sabe
claramente la acción a realizar es mejor utilizar
funciones.
Clases y objetos - 49
Ejercicios con operadores
• Definir el operador > de CComplejo
• Definir el operador+ pero que sea friend de
CComplejo
• Definir el operador + para trabajar con punteros
de CComplejo
En C++ las funciones pueden tener el mismo
nombre, si los parámetros son distintos
Clases y objetos - 50
Diseño básico en C++
C++. Un nuevo estilo de programación
• Programación estructurada
– La solución del problema se consigue construyendo un
programa formado por funciones, cada una especializada en
resolver una parte del problema.
– Los datos son intercambiados entre funciones bajo control del
programa principal.
• Programación orientada a objetos
– La solución del problema es un conjunto de objetos que
interactúan entre sí, a través de sus interfaces.
– Ninguno sabe como funciona el otro por dentro.
Objeto2
Programa principal
Objeto1
Datos
Objeto3
Función1
Función2
Función3
Objeto4
Clases y objetos - 52
Descargar