Вы находитесь на странице: 1из 19

Composición de Clases

Recordemos definiendo como se programa una clase:

class nombredelaclase {

public:

atributos ó metodos publicos;

private:

atributos ó metodos privados;

};

Veamos nuestra primera clase de ejemplo: La clase fracción, que representará una fracción
matemática. ¿Qué atributos tiene una fracción matemática? Pues un denominador y
un numerador

Composición de Clases
 Relación de pertenencia.

 Incluir objetos de una clase A como miembros de datos de otra clase B.

 Encapsulamiento

En la programación clásica (lenguaje C, p.e.) existen datos y procedimientos que actúan sobre
esos datos. No hay una relación aparente entre datos y procedimientos (funciones) y esta
relación se establece de manera más o menos precisa de acuerdo a la profesionalidad del
programador.

En un objeto podemos distinguir dos aspectos bien diferenciados:

• Estado -----------> Propiedades

• Comportamiento ---> Métodos

En P.O.O. los datos y los procedimientos que los gestionan están relacionados explícitamente y
se "encapsulan" en un objeto. La especificación de las propiedades de un objeto y los métodos
de acceso se realiza en la declaración de la clase de la que se instancia el objeto.
En la figura esquematizamos las propiedades y métodos que se van a asociar a los objetos de
la clase TObjGraf:

Clases
Private:

Por el concepto de encapsulación. No queremos que nadie desde fuera pueda tocar nuestro
numerador o nuestro denominador sin nuestro permiso. Como observan he colocado la clase
fracción dentro de un archivo llamado fracción.h, que irá separado de nuestro main. Vamos a
crear el main para que tengamos un programa que se pueda ejecutar:

Al ejecutar la consola muestra una apariencia negra. Si observáis hemos creado un objeto de la
clase fracción, concretamente el objeto unafraccion. Vamos a hacer algunos métodos para
empezar a interactuar con la clase:
Clase Mixto

class Mixto

{public:

Mixto();

Fraccion Equivalente();

float Equivalente();

void Listar();

private:

int ent;

Fraccion f;

};

Clase Curso

class Curso

{public:

Curso(int t=30);

void Inscribir(Alumno&);

void listar ();

double Promedio();

int Aprobados();

int Reprobados();
private:

int N;

char nom[25];

char cod[7];

Alumno v[50];

};

Plantillas de Funciones
Mecanismo para implementar funciones genéricas

template <classT>

tipo nombre(parámetros)

{sentencias}

Ejemplo

template <class T>

void Swap(T&a , T&b)

{T aux;

aux= a;

a= b;

b= aux;

Plantillas de Funciones
void ()

{ int V[5];

Crear(V,5);

Listar(V,5);

float X[4];

Crear(X,4);

Listar(X,4);
char C[6];

Crear(C,6);

Listar(C,6);

Plantillas de Funciones
template <class T>

void Crear(T v[], int n)

{for (int i=0;i<n;i++)

cin>>v[i];

template <class T>

void Listar(T v[], int n)

{for (int i=0;i<n;i++)

cout<<v[i];

Plantillas de clases
Mecanismo para implementar Clases genéricas

template<class T>

class nombre

{private:

Métodos

Variables

public:

Métodos

Variables

Métodos
template <class T>

Stack<T>::Stack(int s)

{largo = s;
top = -1;

p=new T[largo];

template <class T>

bool Stack<T>::Push(T e)

{ if (!Full())

{ p[++top]=e;

return true;

return false;

Uso de clases genéricas


void main()

{ Stack<int> S,S2;

Poblar(S);

Listar(S);

S2=S;

Listar(S2);

Stack<float> S3;

Poblar(S3);

Listar(S3);

Invertir(S3);

Ordenar(S1,S2)

CONSTRUCTORES Y DESTRUCTORES
Son métodos que permiten establecer el estado inicial y final de un objeto. Los constructores
se pueden definir con un conjunto de argumentos arbitrario, pero no pueden devolver nada. Y
los destructores no pueden recibir ni devolver ningún valor.

El constructor debe llamarse igual que la clase, y el destructor el nombre de la clase precedido
del carácter ~
Un constructor se ejecuta cuando se crea un nuevo objeto: 1) por declaración, ó 2) cuando se
crea dinámicamente con el operador new. Un destructor se ejecuta cuando el objeto deja de
existir: 1) porque su ámbito acaba, ó 2) cuando se libera explícitamente con el operador
delete.

Herencia
Cuando una clase hereda de otra, la clase derivada incorpora todos los miembros de la clase
base además de los suyos propios.

La herencia es una herramienta muy importante en muchos aspectos del desarrollo de


aplicaciones:

Organización del diseño.

Reusabilidad de clases (propias o no).

Mejora del mantenimiento.

Ejemplo: De una clase TObjGraf se van a construir dos nuevas clases, TCirculo y TCuadrado,
que derivan de TObjGraf. Esto significa que los objetos de estas clases tienen asociados las
propiedades y métodos de la clase base, TObjGraf, además de los suyos propios. En la figura
5.3 esquematizamos el mecanismo de herencia para las nuevas clases y las nuevas
propiedades que se asocian a los objetos de las clases derivadas.

//*************************************************/

// Definicion de la clase derivada TCirculo

// Deriva de la clase base TObjGraf

//*************************************************/

class TCirculo : public TObjGraf {

public:

int Radio; // Propiedad exclusiva de TCirculo

};

//*************************************************/

// Definicion de la clase derivada TCuadrado.

// Deriva de la clase base TObjGraf

//*************************************************/
class TCuadrado : public TObjGraf {

public:

int Lado; // Propiedad exclusiva de TCuadrado

};

Antes del nombre de la clase base hay que poner public, esto es así porque C++ permite
también la herencia private. Pero ésta no se suele usar, por lo que nosotros supondremos que
sólo existe la public.

CONSTRUCTOR
Es un método que sirve para inicializar los atributos de una clase al valor que se quiera cuando
ésta es creada. Los constructores son un tanto especiales, por varios motivos:

Tienen el mismo nombre que la clase a la que pertenecen

No retornan ningún valor

Deben ser públicos siempre. No tiene sentido un constructor privado

¿Y por qué hacer un constructor?. Para mantener nuestra clase consistente. Cuando se
programa una clase es importante que desde su creación hasta su destrucción sea consistente,
es decir, que no ocasione errores ni estados no posibles. Nuestra clase fracción es
inconsistente en éste momento porque tiene un denominador 0, y si construimos un método
que devuelva la división de los dos atributos, provocará un error. Con el constructor
inicializaremos nuestra clase a 1 de la siguiente manera:
Constructor Alumno
Alumno::Alumno()

{k=0; t=0;}

Alumno::Alumno(char *n, char *r, int m, int c)

{strcpy(nom,n);

strcpy(rut,r);

mat=m;

carrera=c;

k=0;

t=0;

Constructor Curso Objeto anónimo

Invocación explícita al constructor

Curso::Curso(int t)

{N=t;

cin.getline(nom,25);

cin.getline(cod,7);

char x, y;

int z,c;

for (int i=0;i<N;i++)

cout<<"NOMBRE: "; cin.getline(x,20);

cout<<"RUT : "; cin>>y;

cout<<"MAT : "; cin>>z;

cout<<"Carr : "; cin>>c;

Alumno a(x,y,z,c);

v[i]= a; }}
Listar

void Curso::Listar()

{Curso C;

C.Listar();

void main()

{Curso C;

C.Listar();

Composición de clases
objetos de clase Fecha

class Fecha

{private:

int dia;

int mes;

int año;

int ValidaDia();

public:

Fecha(int,int,int);

void Imprimir();

class Empleado

{private:

char nom;

char app;

float sueldo;

Fecha fnac;

Fecha fcontr;

public:

Empleado(char, char, float, int, int, int, int, int, int);

void Imprimir();
}

Composición de clases
 Los constructores inicializan miembros de datos.

 Éstos se invocan en el constructor de la clase contenedora

 Por lo tanto, ANTES de inicializar los datos de la clase contenedora (Mixto, Curso,
Empleado) deberán inicializarse aquellos datos que sean objetos de otras clases, por
medio, de sus constructores.

 PARAMETROS, AMBITO, SOBRECARGA

 INLINE

 El camino de c++ es largo, pero se sigue avanzando. Veamos las funciones inline, un
recurso interesante para mejorar el rendimiento.

 inline double Calcula (double a, double b);

 //Log : saca un mensaje por pantalla

 void Log(char *mensaje);

 //Variables globales

 long variable = 666;

 char*PROGRAMA = "Globales> ";

 int main () {

 //Sacamos por salida standar un mensaje

 Log("Vamos a probar los operadores");

 unsigned int test = 0;

 double a = 23, b = 21, c = 34;

 //Tomamos el valor a

 Log("Dame valores. \na=");

 cin >> a;

 //Tomamos el valor b

 cout << "b=";

 cin >> b;

 cout << "Y ahora son estos: b=" << b << " a=" << a << " global:" << variable << "Y el";

 //Probamos la funcion

 Log("Venga va vamos");

 return 0;
 }

 DOUBLE

 Calcula parámetros: double a, double b devuelve doublé. En la implementación


no hace falta volver a poner INLINE.

 double Calcula (double a, double b) {

 a *= 35462;

 b *=32546 + a;

 return (a / b) * variable;

 }

 /**

 Log

 parametros: char *mensaje

 devuelve void

 */

 void Log (char *mensaje) {

 cout << PROGRAMA << mensaje << endl;

 }

Sobrecarga de operadores
Esto se utiliza para hacer que un operador (+, -, *, /, etc.) haga mas cosas que sumar números
enteros, decimales, etc. Podemos hacer que nos sume matrices, vectores, etc. Si definimos el
operador + para que sume matrices, no dejará de hacer lo que hacia antes (sumar enteros,
decimales, etc.).

La forma para definir la función sobrecargada es:

tipo_a_devolver nombre_clase::operator(parámetros);

Sobrecarga de operadores

Esto se utiliza para hacer que un operador (+, -, *, /, etc.) haga mas cosas que sumar números
enteros, decimales, etc. Podemos hacer que nos sume matrices, vectores, etc. Si definimos el
operador + para que sume matrices, no dejará de hacer lo que hacia antes (sumar enteros,
decimales, etc.).
La forma para definir la función sobrecargada es:

tipo_a_devolver nombre_clase::operator(parámetros);

La sobrecarga de OPERADORES o FUNCIONES es otro concepto básico en la POO.

Sobrecarga de operadores

 Operaciones aritméticas para:

 Fracciones f=f1 + f2

 Complejos c=c1 + c2

 Vectores, etc. v=v+k

 Operadores de flujo:

 Fracciones cout<<f;

 Complejos cin>>c;

 Vectores, etc. Cout<<v;

Restricciones
 No es posible sobrecargar:

 Operador punto (.)

 If aritmético (? :)

 Operador sizeof

 Operador de resolución de alcance (::)

 Puntero a un miembro de un objeto (*.)

Restricciones
 Se puede modificar la definición de un operador, pero NO su gramática (número de
operandos, precedencia y asociatividad)

 Se requiere que, al menos, UN operando sea un objeto de la clase, en la que se ha


definido.

Es el tipo de operandos lo que determina qué operador utilizar

El operador sobrecargado

1. Operador Miembro

 Operador que modifica el operando implícito (izquierda del operador)


 Requiere que el primer operando de la función sea un objeto de la clase (izquierda del
operador)

 Contiene sólo UN parámetro

a) f1 + f2

b) f5 * f3

Ejemplo
class Fraccion

{public:

Fraccion(int=0 ,int=1 ); // Por defecto

void Imprimir();

void SetNum(int);

void SetDen(int);

int Numerador();

int Denominador();

void Simplificar();

Fraccion operator+(const Fraccion&);

private:

int num;

int den;

int mcd();

};

Ejemplo
Fraccion operator+(const Fraccion&);

Fraccion Fraccion::operator+(const Fraccion &f)

{ Fraccion g;

g.num=f.num*den + num*f.den;

g.den= den * f.den;

g.Simplificar();

return g;

}
Ejemplo
class Fraccion

{public:

Fraccion(int=0 ,int=1 ); // Por defecto

void Imprimir();

void SetNum(int);

void SetDen(int);

int Numerador();

int Denominador();

void Simplificar();

Fraccion& operator+(const Fraccion&);

private:

int num;

int den;

int MCD();

};

Ejemplo
Fraccion& operator+ (const Fraccion&);

Fraccion& Fraccion::operator+(const Fraccion& y)

{ num = num * y.den + den * y.num;

den = den * y.den;

Simplificar();

return *this;

};

El operador sobrecargado

2. Operador Friend
 Función "Amiga" de la clase

 Operador actúa sobre varios objetos SIN modificarlos

 Primer parámetro (objeto de la izquierda). Ahora explícito


 a) f1 + f2

 b) f5 * f3

Ejemplo
class Fraccion

{public:

Fraccion(int=0 ,int=1 ); // Por defecto

void Imprimir();

void SetNum(int);

void SetDen(int);

int Numerador();

int Denominador();

void Simplificar();

friend Fraccion operator+ (const Fraccion&,const Fraccion&);

private:

int num;

int den;

int MCD();

};

Ejemplo
friend Fraccion operator+ (const Fraccion&,const Fraccion&);

Fraccion operator+(const Fraccion &f, const Fraccion &g)

{Fraccion h;

h.num= g.num*f.den+ f.num*g.den

h.den= g.den*f.den

return h;

}
SOBRECARGA DE
OPERADORES DE
RELACIÓN
Ejemplo: Sobrecargar ==

bool operator==(const fraccion&);

bool fraccion::operator==(const fraccion &f)

{return (num*f.den==den*f.num);

a) if (f1==f2)

b) x= (f1==f2);

SOBRECARGA DEL
OPERADOR DE
ASIGNACIÓN
Ejemplo: Sobrecargar =

Fraccion& operator=(const Fraccion&);

Fraccion& operator=(const Fraccion &f)

{ if (this!=&f)

{num= f.num;

den= f.den;

return *this;

f1= f2;

Diferencia

a) El constructor de Copia inicializa memoria no inicializada.


Fraccion::Fraccion(const Fraccion& k)

{num=k.num;

den=k.den;

}
Fraccion f(3,4);

Fraccion g(f);

Diferencia

b) El operador de asignación
 Protege contra la "auto-asignación"

 Elimina posibles elementos antiguos

 Inicializa y copia los nuevos elementos

 Fraccion& operator=(const Fraccion &f)

 { if (this!=&f)

 {num= f.num;

 den= f.den;

 }

 return *this;

 }

 SOBRECARGA DE

 OPERADORES

 << - >>

 cout <<f;

 cout<<"la fracción es:"<<f;

 cout<<"La suma de"<<f<<" y" <<g<<"es:"<<h;

 cin>>f;

Sobrecarga: << - >>


 Sobrecargar operadores de flujos, requiere:

a) Que el primer operando sea un objeto de la clase del:

 Flujo de entrada: istream

 Flujo de salida : ostream.

b) Que el método retorne la dirección del objeto para que se pueda utilizar varias veces en una
expresión

Ejemplo: Sobrecargar <<

friend ostream& operator<< (ostream&,const Fraccion&);

ostream& operator <<(ostream &sal, const Fraccion &f)


{sal << f.num << " / " <<f.den;

return sal;

cout <<f;

cout<<"la fracción es:"<<f;

cout<<"La suma de"<<f<<" y" <<g<<"es:"<<h;

Вам также может понравиться