Anuncios

Hola, a todos bienvenidos a mi nuevo post. Hoy trataremos sobre sobre una forma mas avanzada de herencia, hasta ahora solamente la hemos visto de forma simple y directa, es decir hasta ahora vimos una clase base maestra, y sus multiples herencias y tambien sobrecargas de las mismas, hoy pasaremos a ver mas formas de trabajar con las herencias.

Anuncios

Nuestra primera herencia que veremos es llamada contencion, en una forma muy resumida es cuando una clase externa «contiene» a una clase interma, por ejemplo nosotros podemos tener una clase Empleado y esta a su vez puede necesitar contener distintos tipos de clases, tales como cadena (para almacenar el nombre) o de tipo entera (para almacenar el salario) y asi sucesivamente, para ahondar mas en estos ejemplos primero vamos a crear una libreria la cual se va a encargar de manejar los objetos de tipo cadena, pasemos a ver el codigo:

advher.hpp

# include <iostream>
# include <string.h>

using namespace std;

class Cadena
{
public:
 	Cadena();
 	Cadena(const char * const);
 	Cadena(const Cadena &);
 	~Cadena();
 	char & operator[](int desplazamiento);
 	char operator[](int desplazamiento) const;
 	Cadena operator+(const Cadena &);
 	void operator+=(const Cadena &);
 	Cadena & operator= (const Cadena &);
 	int ObtenerLongitud() const { return suLongitud; }
 	const char * ObtenerCadena() const { return suCadena; }
private:
 	Cadena(int);
 	char *suCadena;
 	unsigned short suLongitud;
};

Cadena::Cadena()
{
 	suCadena = new char[ 1 ];
 	suCadena[ 0 ] = '\0';
 	suLongitud = 0;
}

Cadena::Cadena(int longitud)
{
 	suCadena = new char[ longitud+1 ];
 	for(int i = 0; i <= longitud; i++)
 		suCadena = '\0';
 	suLongitud = longitud;
}

Cadena::Cadena(const char * const cCadena)
{
 	suLongitud = strlen(cCadena);
 	suCadena = new char[ suLongitud + 1 ];
 	for (int i = 0; i < suLongitud; i++ )
 		suCadena[ i ]=cCadena[ i ];
 	suCadena[ suLongitud ] = '\0';
}

Cadena::Cadena (const Cadena & rhs)
{
 	suLongitud = rhs.ObtenerLongitud();
 	suCadena = new char[ suLongitud+1 ];
 	for(int i = 0; i < suLongitud; i++ )
 		suCadena[ i ] = rhs[ i ];
 	suCadena[ suLongitud ] = '\0';
}

Cadena::~Cadena()
{
 	delete [] suCadena;
 	suLongitud = 0;
}

Cadena & Cadena::operator=(const Cadena & rhs)
{
 	if (this == &rhs)
 		return *this;
 	delete [] suCadena;
 	suLongitud = rhs.ObtenerLongitud();
 	suCadena = new char[ suLongitud + 1 ];
 	for(int i = 0; i < suLongitud; i++)
 		suCadena[ i ] = rhs[ i ];
 	suCadena[ suLongitud ] = '\0';
 	return *this;
}

char & Cadena::operator[](int desplazamiento)
{
 	if (desplazamiento > suLongitud)
 		return suCadena[ suLongitud - 1 ];
 	else
 		return suCadena[ desplazamiento ];
}

char Cadena::operator[](int desplazamiento) const
{
 	if (desplazamiento > suLongitud)
 		return suCadena[ suLongitud - 1 ];
 	else
 		return suCadena[ desplazamiento ];
}

Cadena Cadena::operator+(const Cadena & rhs)
{
 	int longitudTotal = suLongitud + rhs.ObtenerLongitud();
 	Cadena temp(longitudTotal);
 	int i, j;
	for(i = 0; i < suLongitud; i++)
 		temp[ i ] = suCadena[ i ];
 	for(j = 0; j < rhs.ObtenerLongitud(); j++, i++)
 		temp[ i ] = rhs[ j ];
 	temp[ longitudTotal ] = '\0';
 	return temp;
}

void Cadena::operator+=(const Cadena & rhs)
{
 	unsigned short rhsLong = rhs.ObtenerLongitud();
 	unsigned short longitudTotal = suLongitud + rhsLong;
 	Cadena temp(longitudTotal);
 	int i, j;
	for(i = 0; i < suLongitud; i++)
 		temp[ i ] = suCadena[ i ];
 	for(j = 0; j < rhs.ObtenerLongitud(); j++, i++)
 		temp[ i ] = rhs[ i - suLongitud ];
 	temp[ longitudTotal ] = '\0';
 	*this = temp;
}
Anuncios

Este codigo, como podran ver no produce ninguna salida simplemente va a contener la clase Cadena, la cual se encargara de manipular los datos de tipo cadena que reciba, para eso en esta ocasion vamos a incluir la libreria string.h, la cual se encarga de habilitarnos las variables tipo char y el comando strlen, para saber el tamaño de cadena de texto, en la clase cadena, vamos a tener nuestros prototipo de constructores:

Cadena();
Cadena(const char * const);
Cadena(const Caddena &);
Anuncios

Luego nuestro destructor, despues pasaremos a la seccion de operadores sobrecargados, tambien declarados en plan de prototipos:

char & operator[](int desplazamiento);
char operator[](int desplazamiento) const; 
Cadena operator+(const Cadena &);
void operator+=(const Cadena &);
Cadena & operator= (const Cadena &);

Y por ultimo, tendremos nuestros metodos de acceso:

int ObtenerLongitud() const { return suLongitud; } 
const char * ObtenerCadena() const { return suCadena; }
Anuncios

Hasta aqui la parte publica, pasemos a la parte privada donde vamos a tener dos tipos de variables: una tipo char y otra de tipo short, la primera almacenara la cadena de texto a procesar y la segunda la longitud de la misma y por ultimo esta linea:

Cadena(int);

Este es un constructor de tipo privado, luego explicaremos su funcionamiento por ahora es solo un prototipo en la parte privada. Pasamos a ver la definicion de cada uno de los prototipos.

Anuncios

En el primer caso: Cadena::Cadena(), este es nuestro constructor predeterminado, esta se va a encargar una nueva cadena de 0 bytes, observen como primero crea la variable de tipo con un array de una sola posicion, luego asigna a esa posicion el valor terminal de los char y por ultimo a suLongitud le asigna cero.

Anuncios

En la siguiente definicion: Cadena::Cadena(int longitud), esta es una clase auxiliar (por eso esta en privado) donde solo la utilizaran las clases para crear una nueva cadena del tamaño requerido, se completa de caracteres nulos. Como pueden ver recibe una variable donde define el tamaño de la misma, luego a asignar a suCadena el tamaño para la variable de tipo char el cual sera igual al informado mas uno, esto es para poder almacenar el caracter nulo que lo identifica como tal, luego usaremos un bucle for donde llenaremos cada una de las posiciones con el valor nulo y por ultimo asignaremos a suLongitud el valor de longitud.

Anuncios

En el siguiente, Cadena::Cadena(const char * const cCadena), este se encargara de convertir el array de caracteres (char) en tipo cadena (string). Primero vamos a asignar el tamaño de la cadena a suLongitud con el comando strlen sobre la variable informada cCadena, luego a suCadena le definiremos de nuevo el tamaño de char con suLongitud mas uno, para luego utilizar un bucle for, donde llenaremos  cada una de las posiciones de suCadena con las posiciones provenientes de cCadena, una vez finalizado, al final de suCadena le agregaremos el caracter nulo para finalizar la conversion en base al valor de suLongitud.

Anuncios

Luego pasamos a este: Cadena::Cadena(const Cadena & rhs), este es un constructor de copia, como pueden ver primero asigna a suLongitud el valor de rhs (right hand side) por medio del metodo ObtenerLongitud(), en este post hablo un poco mas de constructores de copia y rhs, a suCadena le asignamos un nuevo valor de longitud mas uno en base a la variable antes definida, despues hay un bucle for, el cual asignara a cada posicion de suCadena, la posicion del rhs, despues para terminar la copia le asignaremos el valor nulo.

El proximo bloque, Cadena::~Cadena(), este el destructor y su unica funcion es liberar la memoria asignada, pueden verlo en las dos lineas dentro del bloque.

Siguiendo: Cadena& Cadena::operator=(const Cadena & rhs), nuestra primer linea es un condicional if, este chequea si el this es igual a rhs, en caso afirmativo retorna el apuntador this. en caso contrario libera la memoria, vuelve a copiarlo como en la funcion anterior, y una vez finalizado retorna al apuntador this.

Anuncios

El siguiente bloque, char & Cadena::opertator[](int desplazamiento), es el encargado del operador de desplazamiento no constante, regresa una referencia a un caracter para que se pueda cambiar, el condicional if verifica si desplazamiento (valor informado a la funcion) es mayor a suLongitud, en caso de ser verdad procede a devolver el caracter de la posicion del array suCadena y la posicion esta definida por suLongitud – 1, de lo contrario retorna el caracter de la posicion de suCadena por el informado en desplazamiento,

Anuncios

El bloque siguiente: char Cadena::operator[](int desplazamiento) const, es igual al anterior bloque pero en este caso es constante y esta para ser utilizados para objetos de tipo constante.

Anuncios

Ahora en este bloque: Cadena Cadena::operator+(const Cadena & rhs), este bloque se encarga de crear una cadena nueva, agregando la cadena actual a rhs, primero crearemos la variable de la longitud total de la nueva cadena, longitudTotal, la cual es la suma de suLongitud mas el valor obtenido por rhs.ObtenerLongitud(), creamos un objeto del tipo Cadena y le pasamos el valor antes creado, y por ultimo las dos variables para los siguientes bucles for. en el primer bucle for, incrementaremos a i y procederemos a llenar a temp con suCadena, en ambos casos utilizaremos a i para las posiciones del array, en el siguiente bucle for, vamos a incrementar a j pero tambien vamos a seguir incrementando a i, y el indice de temp seguira siendo i pero el de rhs va a ser j, por ultimo una vez finalizado a la posicion final de temp le agrega el valor nulo, y retorna el valor final de temp.

Anuncios

Pasemos al ultimo bloque: void Cadena::operator+=(const Cadena & rhs), este se encarga de cambiar una cadena actual pero no devuelve nada. Primero crearemos una variable rhsLong la cual se encargara de tener el valor de la longirud de rhs, en la siguiente variable calcularemos el valor de la longitud total, en base a suLongitud mas el valor antes almacenado, luego volveremos a crear un objeto del tipo Cadena llamado temp y con el valor de longitudTotal, creamos dos variables mas y procedemos al primer bucle for, haremos como en el bloque anterior, iremos rellenando a temp con suCadena, en el siguiente bucle for, como en el caso anterior comenzaremos a incrementar a j, seguiremos incrementando a i, seguiremos rellenando a temp con rhs en base a la posicion obtenida por la diferencia del valor actual de i con suLongitud, en la siguiente linea agregamos el valor nulo a temp y por ultimo le asignamos al apuntador this el valor de temp. Hasta aqui la libreria creada, como ven no produce ninguna salida pero se encarga de manipular la informacion de tipo texto, nuestro siguiente paso sera implementar la libreria creada en un programa:

advher00.cpp

# include <iostream>
# include <string.h>
# include "advher.hpp"

using namespace std;

class Empleado
{
public:
 	Empleado();
 	Empleado(const char *, const char *, const char *, long);
 	~Empleado();
 	Empleado(const Empleado &);
 	Empleado & operator=(const Empleado &);
 	const Cadena & ObtenerPrimerNombre() const { return suPrimerNombre; }
 	const Cadena & ObtenerApellido() const { return suApellido; }
 	const Cadena & ObtenerDireccion() const { return suDireccion; }
 	long ObtenerSalario() const { return suSalario; }
 	void AsignarPrimerNombre(const Cadena &primNombre) 
		{ suPrimerNombre = primNombre; }
 	void AsignarApellido(const Cadena &Apellido) { suApellido = Apellido; }
 	void AsignarDireccion(const Cadena &direccion) { suDireccion = direccion; }
 	void AsignarSalario(long salario) { suSalario = salario; }
private:
 	Cadena suPrimerNombre;
 	Cadena suApellido;
 	Cadena suDireccion;
 	long suSalario;
};

Empleado::Empleado():
 	suPrimerNombre(""),
 	suApellido(""),
 	suDireccion(""),
 	suSalario(0)
 	{}

Empleado::Empleado(const char * primerNombre, const char * apellido,
 	const char * direccion, long salario):
 	suPrimerNombre(primerNombre),
 	suApellido(apellido),
 	suDireccion(direccion),
 	suSalario(salario)
 	{}

Empleado::Empleado(const Empleado & rhs):
 	suPrimerNombre(rhs.ObtenerPrimerNombre()),
 	suApellido(rhs.ObtenerApellido()),
 	suDireccion(rhs.ObtenerDireccion()),
 	suSalario(rhs.ObtenerSalario())
 	{}

Empleado::~Empleado(){}

Empleado & Empleado::operator=(const Empleado & rhs)
{
 	if (this == &rhs)
 		return *this;
	suPrimerNombre = rhs.ObtenerPrimerNombre();
 	suApellido = rhs.ObtenerApellido();
 	suDireccion = rhs.ObtenerDireccion();
 	suSalario = rhs.ObtenerSalario();
 	return *this;
}

int main()
{
 	Empleado Edie("Juan", "Perez", "Av. Rivadavia 1197", 20000);
 	Edie.AsignarSalario(50000);
 	Cadena cApellido("Miranda");
 	Edie.AsignarApellido(cApellido);
 	Edie.AsignarPrimerNombre("Martin");
 	cout << "Nombre: ";
 	cout << Edie.ObtenerPrimerNombre().ObtenerCadena();
 	cout << " " << Edie.ObtenerApellido().ObtenerCadena();
 	cout << ".\nDireccion: ";
 	cout << Edie.ObtenerDireccion().ObtenerCadena();
 	cout << ".\nSalario: ";
 	cout << Edie.ObtenerSalario();
 	cout << endl;
 	return 0;
}
Anuncios

En este caso, incluimos las librerias: iostream, string.h y advher.hpp. Aca creamos una clase llamada Empleado, primero hacemos un prototipo de contructor por defecto, luego otro para ser receptor de cuatro variables, tres tipo char y una tipo long, luego el de destructor, y otro prototipo de tipo de Empleado con un tipo Cadena, luego vendran un operador, despues declararemos todos y cada uno de las funciones miembro para asignar u obtener los datos en privado, luego definiremos todos y cada uno de nuestros prototipos donde la mayoria sera establecer el valor en las variables almacenadas en privado. El operador igual lo utilizaremos para redefinir las variables privadas, primero chequea si el apuntador this es igual a rhs, en caso afirmativo retorna el apuntador this, sino los asigna a traves de las funciones para obtener y despues retorna el apuntador this. Ahora pasemos al main(), en este caso primero crearemos un objeto de tipo Empleado llamada Edie y le asignaremos cuatro valores, luego haremos tres cambios, modificaremos el salario, luego el apellido a traves de un objeto tipo Cadena y por ultimo le cambiaremos el nombre directamente sin ninguna variable y objeto, todas las siguiente lineas generan la salida a ver en pantalla, compilemos y probemos nuestro programa:

tinchicus@dbn001dsk:~/lenguaje/c++$ ./advher00
Nombre: Martin Miranda.
Direccion: Av. Rivadavia 1197.
Salario: 50000
tinchicus@dbn001dsk:~/lenguaje/c++$
Anuncios

Observen como se modifico los datos enviados en la inicializacion, pero se mantuvo la direccion de la declaracion inicial, tambien otro detalle, como nombre, apellido y direccion son del tipo Cadena para poder verlas necesitamos agregarle el metodo ObtenerCadena(), esto es muy hermoso pero cual es el costo de todo esto? la increible cantidad de objetos y copias creadas para ejecutar estas tareas de creacion de los tipos Cadena. Para ver y evitar esto vamos a hacer algunas modificaciones, una en nuestra libreria advher.hpp y despues modificaremos el codigo de nuestro programa, primero debemos agregar la siguiente linea a la clase Cadena:

static int ConstructorCuenta;
Anuncios

Nuestro siguiente paso sera hacer unas modificaciones en las declaraciones de nuestros prototipos, el primero que modificaremos sera el constructor predeterminado con el siguiente bloque:

Cadena::Cadena()
{
 	suCadena = new char[ 1 ];
 	suCadena[ 0 ] = '\0';
 	suLongitud = 0;
	cout << "\tConstructor de cadena predeterminado \n";
	ConstructorCuenta++;
}
Anuncios

En este caso solamente agregamos una notificacion cuando es invocado y el incremento de la variable estatica de la clase Cadena, en el siguiente caso modificaremos al siguiente bloque de declaracion:

Cadena::Cadena(int longitud)
{
 	suCadena = new char[ longitud+1 ];
 	for(int i = 0; i <= longitud; i++)
 		suCadena = '\0';
 	suLongitud = longitud;
 	cout << "\tConstructor de Cadena(int)\n";
 	ConstructorCuenta++;
}
Anuncios

Al igual que en el caso agregamos una notificacion para cuando es creado e incrementamos a la variable estatica, modifiquemos el siguiente bloque:

Cadena::Cadena(const char * const cCadena)
{
 	suLongitud = strlen(cCadena);
 	suCadena = new char[ suLongitud + 1 ];
 	for (int i = 0; i < suLongitud; i++ )
 		suCadena[ i ]=cCadena[ i ];
 	suCadena[ suLongitud ] = '\0';
	cout << "\tConstructor de Cadena(Char *) constructor\n";
 	ConstructorCuenta++;
}
Anuncios

De vuelta agregamos las mismas acciones antes descriptas, el siguiente bloque que modificaremos sera:

Cadena::Cadena (const Cadena & rhs)
{
 	suLongitud = rhs.ObtenerLongitud();
 	suCadena = new char[ suLongitud+1 ];
 	for(int i = 0; i < suLongitud; i++ )
 		suCadena[ i ] = rhs[ i ];
 	suCadena[ suLongitud ] = '\0';
	cout << "\tConstructor de Cadena(Cadena &)\n";
 	ConstructorCuenta++;
}
Anuncios

De vuelta agregamos las mismas modificaciones, nuestro siguiente bloque sera el destructor donde lo modificaremos de la siguiente manera:

Cadena::~Cadena()
{
 	delete [] suCadena;
 	suLongitud = 0;
	cout << "\tDestructor de Cadena\n";
}
Anuncios

En este caso solo mostraremos la notificacion cuando es invocado pero no incrementa a la variable estatica a diferencia de los casos anteriores, pasemos al siguiente bloque a modificar:

Cadena & Cadena::operator=(const Cadena & rhs)
{
 	if (this == &rhs)
 		return *this;
 	delete [] suCadena;
 	suLongitud = rhs.ObtenerLongitud();
 	suCadena = new char[ suLongitud + 1 ];
 	for(int i = 0; i < suLongitud; i++)
 		suCadena[ i ] = rhs[ i ];
 	suCadena[ suLongitud ] = '\0';
	cout << "\tOperador = de Cadena\n";
 	return *this;
}
Anuncios

De vuelta en este caso agregamos una notificacion de cuando es invocado el operador igual, tampoco incrementa a la variable estatica, y nuestra ultima modificacion sera agregar la linea que inicie a la variable estatica:

int Cadena::ConstructorCuenta = 0;

Con estas modificaciones realizadas veamos como quedo nuestro codigo final:

# include <iostream>
# include <string.h>

using namespace std;

class Cadena
{
public:
 	Cadena();
 	Cadena(const char * const);
 	Cadena(const Cadena &);
 	~Cadena();
 	char & operator[](int desplazamiento);
 	char operator[](int desplazamiento) const;
 	Cadena operator+(const Cadena &);
 	void operator+=(const Cadena &);
 	Cadena & operator= (const Cadena &);
 	int ObtenerLongitud() const { return suLongitud; }
 	const char * ObtenerCadena() const { return suCadena; }
	static int ConstructorCuenta;
private:
 	Cadena(int);
 	char *suCadena;
 	unsigned short suLongitud;
};

Cadena::Cadena()
{
 	suCadena = new char[ 1 ];
 	suCadena[ 0 ] = '\0';
 	suLongitud = 0;
	cout << "\tConstructor de cadena predeterminado \n";
	ConstructorCuenta++;
}

Cadena::Cadena(int longitud)
{
 	suCadena = new char[ longitud+1 ];
 	for(int i = 0; i <= longitud; i++)
 		suCadena = '\0';
 	suLongitud = longitud;
 	cout << "\tConstructor de Cadena(int)\n";
 	ConstructorCuenta++;
}

Cadena::Cadena(const char * const cCadena)
{
 	suLongitud = strlen(cCadena);
 	suCadena = new char[ suLongitud + 1 ];
 	for (int i = 0; i < suLongitud; i++ )
 		suCadena[ i ]=cCadena[ i ];
 	suCadena[ suLongitud ] = '\0';
	cout << "\tConstructor de Cadena(Char *) constructor\n";
 	ConstructorCuenta++;
}

Cadena::Cadena (const Cadena & rhs)
{
 	suLongitud = rhs.ObtenerLongitud();
 	suCadena = new char[ suLongitud+1 ];
 	for(int i = 0; i < suLongitud; i++ )
 		suCadena[ i ] = rhs[ i ];
 	suCadena[ suLongitud ] = '\0';
	cout << "\tConstructor de Cadena(Cadena &)\n";
 	ConstructorCuenta++;
}

Cadena::~Cadena()
{
 	delete [] suCadena;
 	suLongitud = 0;
	cout << "\tDestructor de Cadena\n";
}

Cadena & Cadena::operator=(const Cadena & rhs)
{
 	if (this == &rhs)
 		return *this;
 	delete [] suCadena;
 	suLongitud = rhs.ObtenerLongitud();
 	suCadena = new char[ suLongitud + 1 ];
 	for(int i = 0; i < suLongitud; i++)
 		suCadena[ i ] = rhs[ i ];
 	suCadena[ suLongitud ] = '\0';
	cout << "\tOperador = de Cadena\n";
 	return *this;
}

char & Cadena::operator[](int desplazamiento)
{
 	if (desplazamiento > suLongitud)
 		return suCadena[ suLongitud - 1 ];
 	else
 		return suCadena[ desplazamiento ];
}

char Cadena::operator[](int desplazamiento) const
{
 	if (desplazamiento > suLongitud)
 		return suCadena[ suLongitud - 1 ];
 	else
 		return suCadena[ desplazamiento ];
}

Cadena Cadena::operator+(const Cadena & rhs)
{
 	int longitudTotal = suLongitud + rhs.ObtenerLongitud();
 	Cadena temp(longitudTotal);
 	int i, j;
	for(i = 0; i < suLongitud; i++)
 		temp[ i ] = suCadena[ i ];
 	for(j = 0; j < rhs.ObtenerLongitud(); j++, i++)
 		temp[ i ] = rhs[ j ];
 	temp[ longitudTotal ] = '\0';
 	return temp;
}

void Cadena::operator+=(const Cadena & rhs)
{
 	unsigned short rhsLong = rhs.ObtenerLongitud();
 	unsigned short longitudTotal = suLongitud + rhsLong;
 	Cadena temp(longitudTotal);
 	int i, j;
	for(i = 0; i < suLongitud; i++)
 		temp[ i ] = suCadena[ i ];
 	for(j = 0; j < rhs.ObtenerLongitud(); j++, i++)
 		temp[ i ] = rhs[ i - suLongitud ];
 	temp[ longitudTotal ] = '\0';
 	*this = temp;
}

int Cadena::ConstructorCuenta = 0;
Anuncios

Como pueden ver es el mismo codigo pero ahora tiene la salvedad de mostrar en cual momento es invocado tal o cual constructor y tambien lleva un contador de la cantidad de veces que son llamados a este ultimo lo hicimos a traves de una variable de tipo estatica como vimos en este post, para comprobar las nuevas modificaciones debemos volver a compilar el programa anterior, si lo probamos obtendremos la siguiente salida:

tinchicus@dbn001dsk:~/lenguaje/c++$ ./advher00
        Constructor de Cadena(Char *) constructor
        Constructor de Cadena(Char *) constructor
        Constructor de Cadena(Char *) constructor
        Constructor de Cadena(Char *) constructor
        Operador = de Cadena
        Constructor de Cadena(Char *) constructor
        Operador = de Cadena
        Destructor de Cadena
Nombre: Martin Miranda.
Direccion: Av. Rivadavia 1197.
Salario: 50000
        Destructor de Cadena
        Destructor de Cadena
        Destructor de Cadena
        Destructor de Cadena
tinchicus@dbn001dsk:~/lenguaje/c++$
Anuncios

En este caso podemos ver como se llamo a cada uno de los constructores, se trae la informacion del programa y despues se llaman a los destructores para eliminar a todos y cada uno de ellos, para solucionar este inconveniente veamos el siguiente ejemplo:

advher01.cpp

# include <iostream>
 # include <string.h>
 # include "advher.hpp"

using namespace std;

class Empleado
{
public:
 	Empleado();
 	Empleado(const char *, const char *, const char *, long);
 	~Empleado();
 	Empleado(const Empleado &);
 	Empleado & operator=(const Empleado &);
 	const Cadena & ObtenerPrimerNombre() const { return suPrimerNombre; }
 	const Cadena & ObtenerApellido() const { return suApellido; }
 	const Cadena & ObtenerDireccion() const { return suDireccion; }
 	long ObtenerSalario() const { return suSalario; }
 	void AsignarPrimerNombre(const Cadena &primNombre)
 		{ suPrimerNombre = primNombre; }
 	void AsignarApellido(const Cadena &Apellido) { suApellido = Apellido; }
 	void AsignarDireccion(const Cadena &direccion) { suDireccion = direccion; }
 	void AsignarSalario(long salario) { suSalario = salario; }
private:
 	Cadena suPrimerNombre;
 	Cadena suApellido;
 	Cadena suDireccion;
 	long suSalario;
};

Empleado::Empleado():
 	suPrimerNombre(""),
 	suApellido(""),
 	suDireccion(""),
 	suSalario(0)
 	{}

Empleado::Empleado(const char * primerNombre, const char * apellido,
 	const char * direccion, long salario):
 	suPrimerNombre(primerNombre),
 	suApellido(apellido),
 	suDireccion(direccion),
 	suSalario(salario)
 	{}

Empleado::Empleado(const Empleado & rhs):
 	suPrimerNombre(rhs.ObtenerPrimerNombre()),
 	suApellido(rhs.ObtenerApellido()),
 	suDireccion(rhs.ObtenerDireccion()),
 	suSalario(rhs.ObtenerSalario())
 	{}

Empleado::~Empleado(){}

Empleado & Empleado::operator=(const Empleado & rhs)
{
 	if (this == &rhs)
 		return *this;
	suPrimerNombre = rhs.ObtenerPrimerNombre();
 	suApellido = rhs.ObtenerApellido();
 	suDireccion = rhs.ObtenerDireccion();
 	suSalario = rhs.ObtenerSalario();
 	return *this;
}

void FuncImpr(Empleado);

void rFuncImpr(const Empleado&);

int main()
{
 	Empleado Edie("Juan", "Perez", "Av. Rivadavia 1197", 20000);
 	Edie.AsignarSalario(20000);
 	Cadena cApellido("Miranda");
 	Edie.AsignarApellido(cApellido);
 	Edie.AsignarPrimerNombre("Martin");
 	cout << "Cuenta de constructores: ";
 	cout << Cadena::ConstructorCuenta << endl;
 	rFuncImpr(Edie);
 	cout << "Cuenta de constructores: ";
 	cout << Cadena::ConstructorCuenta << endl;
 	FuncImpr(Edie);
 	cout << "Cuenta de constructores: ";
 	cout << Cadena::ConstructorCuenta << endl;
 	return 0;
}

void FuncImpr(Empleado Edie)
{
 	cout << "Nombre: ";
 	cout << Edie.ObtenerPrimerNombre().ObtenerCadena();
 	cout << " " << Edie.ObtenerApellido().ObtenerCadena();
 	cout << ".\nDireccion: ";
 	cout << Edie.ObtenerDireccion().ObtenerCadena();
 	cout << ".\nSalario: ";
 	cout << Edie.ObtenerSalario();
 	cout << endl;
}

void rFuncImpr(const Empleado & Edie)
{
 	cout << "Nombre: ";
 	cout << Edie.ObtenerPrimerNombre().ObtenerCadena();
 	cout << " " << Edie.ObtenerApellido().ObtenerCadena();
 	cout << ".\nDireccion: ";
 	cout << Edie.ObtenerDireccion().ObtenerCadena();
 	cout << ".\nSalario: ";
 	cout << Edie.ObtenerSalario();
 	cout << endl;
}
Anuncios

Basicamente, es igual al ejercicio anterior pero con una salvedad, ahora usaremos dos funciones para mostrar la salida, FuncImpr(Empleado) y rFuncImpr(const Empleado &) y en ambos casos seran void, Crearemos los prototipos de nuestras funciones antes del main(), luego haremos algunas modificaciones, en primer lugar del codigo anterior utilizaremos los comandos para mostrar en pantalla en nuestras funciones, por ende los quitaremos y crearemos unas lineas para ver la cantidad de constructores creados y ver la salida de nuestras funciones:

cout << "Cuenta de constructores: ";
cout << Cadena::ConstructorCuenta << endl; // muestra la cantidad de constructores creados
rFuncImpr(Edie);          // llama a la funcion con referencias.
cout << "Cuenta de constructores: ";
cout << Cadena::ConstructorCuenta << endl; // muestra la cantidad de constructores creados
FuncImpr(Edie);          // llama a la otra funcion sin referencias.
cout << "Cuenta de constructores: ";
cout << Cadena::ConstructorCuenta << endl; // muestra la cantidad de constructores finales
Anuncios

Despues en el caso de las funciones vamos a crear una para utilizar con las referencias creadas y otra sin referencias en ambos casos, utilizaremos las mismas instrucciones, y estas son las utilizadas para mostrar los datos de nuestros empleados, tal como lo vimos en el caso anterior, si compilamos y ejecutamos obtendremos la siguiente salida:

tinchicus@dbn001dsk:~/lenguaje/c++$ ./advher01
        Constructor de Cadena(Char *) constructor
        Constructor de Cadena(Char *) constructor
        Constructor de Cadena(Char *) constructor
        Constructor de Cadena(Char *) constructor
        Operador = de Cadena
        Constructor de Cadena(Char *) constructor
        Operador = de Cadena
        Destructor de Cadena
Cuenta de constructores: 5
Nombre: Martin Miranda.
Direccion: Av. Rivadavia 1197.
Salario: 20000
Cuenta de constructores: 5
        Constructor de Cadena(Cadena &)
        Constructor de Cadena(Cadena &)
        Constructor de Cadena(Cadena &)
Nombre: Martin Miranda.
Direccion: Av. Rivadavia 1197.
Salario: 20000
        Destructor de Cadena
        Destructor de Cadena
        Destructor de Cadena
Cuenta de constructores: 8
        Destructor de Cadena
        Destructor de Cadena
        Destructor de Cadena
        Destructor de Cadena
tinchicus@dbn001dsk:~/lenguaje/c++$
Anuncios

En este caso ahora podemos ver todas las llamadas que antes aparecian invisibles para nosotros, este programa nos muestra como se crearon cinco objetos tipo Cadena, tres como parte de un objeto tipo Empleado cuando lo creamos en la primer linea, uno mas al asignar el nombre y el ultimo al asignar el apellido. ahora cuando nosotros pasamos esto a nuestra funcion por referencias, rFuncImpr(), y como trabaja por referencias no es necesario crear objetos tipo Empleado adicionales y por ende no crea objetos Cadena adicionales, en cambio si lo pasamos por valor a FuncImpr() esta creara una copia del objeto Empleado y se crean mas objetos de tipo Cadena por medio de llamadas de constructor de copia por esto es mas conveniente trabajar todo por referencia en lugar de por valor. Para nuestro siguiente hablaremos de Delegacion y para ello lo haremos a traves del siguiente ejemplo:

advher02.cpp

# include <iostream>

using namespace std;

class Pieza
{
public:
 	Pieza(): suNumeroPieza(1){}
 	Pieza(int NumeroPieza):
 	suNumeroPieza(NumeroPieza){}
 	virtual ~Pieza(){}
 	int ObtenerNumeroPieza() const { return suNumeroPieza; }
 	virtual void Desplegar() const = 0;
private:
 	int suNumeroPieza;
};

void Pieza::Desplegar() const
{
 	cout << "\nNumero de Pieza: " << suNumeroPieza << endl;
}

class PiezaAuto : public Pieza
{
public:
 	PiezaAuto(): suAnioModelo(94){}
 	PiezaAuto(int anio, int numeroPieza);
 	virtual void Desplegar() const
 	{
 		Pieza::Desplegar();
 		cout << "Año del modelo: ";
 		cout << suAnioModelo << endl;
 	}
private:
 	int suAnioModelo;
};

PiezaAuto::PiezaAuto(int anio, int numeroPieza):
 	suAnioModelo(anio),
 	Pieza(numeroPieza)
 	{}

class PiezaAeroPlano : public Pieza
{
public:
 	PiezaAeroPlano(): suNumeroMotor(1){}
 	PiezaAeroPlano(int NumeroMotor, int NumeroPieza);
 	virtual void Desplegar() const
 	{
 		Pieza::Desplegar();
 		cout << "Motor Numero: ";
 		cout << suNumeroMotor << endl;
 	}
private:
 	int suNumeroMotor;
};

PiezaAeroPlano::PiezaAeroPlano(int NumeroMotor, int NumeroPieza):
 suNumeroMotor(NumeroMotor),
 Pieza(NumeroPieza)
 {}

class NodoPieza
{
public:
 	NodoPieza(Pieza *);
 	~NodoPieza();
 	void AsignarSiguiente(NodoPieza * nodo) { suSiguiente = nodo; }
 	NodoPieza * ObtenerSiguiente() const;
 	Pieza * ObtenerPieza() const;
private:
 	Pieza * suPieza;
 	NodoPieza * suSiguiente;
};

NodoPieza::NodoPieza(Pieza * apPieza):
 	suPieza(apPieza),
 	suSiguiente(0)
 	{}

NodoPieza::~NodoPieza()
{
 	delete suPieza;
 	suPieza = NULL;
 	delete suSiguiente;
 	suSiguiente = NULL;
}

NodoPieza * NodoPieza::ObtenerSiguiente() const { return suSiguiente; }

Pieza * NodoPieza::ObtenerPieza() const
{
 	if (suPieza)
 		return suPieza;
 	else
 		return NULL;
}

class ListaPiezas
{
public:
 	ListaPiezas();
 	~ListaPiezas();
 	void Iterar(void (Pieza::*f)() const) const;
 	Pieza * Encontrar(int & posicion, int NumeroPieza) const;
 	Pieza * ObtenerPrimero() const;
 	void Insertar(Pieza *);
 	Pieza * operator[](int) const;
 	int ObtenerCuenta() const { return suCuenta; }
 	static ListaPiezas& ObtenerListasPiezasGlobal() { return ListaPiezasGlobal; }
private:
 	NodoPieza * apCabeza;
 	int suCuenta;
 	static ListaPiezas ListaPiezasGlobal;
};

ListaPiezas ListaPiezas::ListaPiezasGlobal;

ListaPiezas::ListaPiezas():
 	apCabeza(0),
 	suCuenta(0)
 	{}

ListaPiezas::~ListaPiezas() { delete apCabeza; }

Pieza *ListaPiezas::ObtenerPrimero() const
{
 	if (apCabeza)
 		return apCabeza->ObtenerPieza();
 	else
 		return NULL;
}

Pieza * ListaPiezas::operator[](int desplazamiento) const
{
 	NodoPieza * apNodo = apCabeza;
 	if (!apCabeza)
 		return NULL;
 	if (desplazamiento > suCuenta)
 		return NULL;
 	for(int i = 0; i < desplazamiento; i++)
 		apNodo = apNodo->ObtenerSiguiente();
 	return apNodo->ObtenerPieza();
}

Pieza *ListaPiezas::Encontrar(int & posicion, int NumeroPieza) const
{
 	NodoPieza * apNodo = NULL;
	for(apNodo = apCabeza, posicion = 0; apNodo!=NULL;
 		apNodo = apNodo->ObtenerSiguiente(), posicion++)
 	{
 		if (apNodo->ObtenerPieza()->ObtenerNumeroPieza() == NumeroPieza)
 		break;
 	}
 	if (apNodo == NULL)
 		return NULL;
 	else
 		return apNodo->ObtenerPieza();
}

void ListaPiezas::Iterar(void (Pieza::*func)() const) const
{
 	if (!apCabeza)
 		return;
 	NodoPieza * apNodo = apCabeza;
 	do
 	(apNodo->ObtenerPieza()->*func)();
 	while(apNodo = apNodo->ObtenerSiguiente());
}

void ListaPiezas::Insertar(Pieza * apPieza)
{
 	NodoPieza * apNodo = new NodoPieza(apPieza);
 	NodoPieza * apActual = apCabeza;
 	NodoPieza * apSiguiente = NULL;
 	int Nuevo = apPieza -> ObtenerNumeroPieza();
 	int Siguiente = 0;
	suCuenta++;
	if (!apCabeza)
 	{
 		apCabeza=apNodo;
 		return;
 	}
	if (apCabeza->ObtenerPieza()->ObtenerNumeroPieza() > Nuevo)
 	{
 		apNodo->AsignarSiguiente(apCabeza);
 		apCabeza = apNodo;
 		return;
 	}
 	for(;;)
 	{
 		if (!apActual->ObtenerSiguiente())
 		{
 			apActual->AsignarSiguiente(apNodo);
 			return;
 		}
 	apSiguiente = apActual->ObtenerSiguiente();
 	Siguiente = apSiguiente->ObtenerPieza()->ObtenerNumeroPieza();
 	if (Siguiente > Nuevo)
 	{
 		apActual->AsignarSiguiente(apNodo);
 		apNodo->AsignarSiguiente(apSiguiente);
 		return;
 	}
 	apActual = apSiguiente;
 	}
}

class CatalogoPiezas
{
public:
 	void Insertar(Pieza *);
 	int Existe(int NumeroPieza);
 	Pieza * Obtener(int NumeroPieza);
 	int operator+(const CatalogoPiezas &);
 	void MostrarTodo() { laListaPiezas.Iterar(&Pieza::Desplegar); }
private:
 	ListaPiezas laListaPiezas;
};

void CatalogoPiezas::Insertar(Pieza * nuevaPieza)
{
 	int numeroPieza = nuevaPieza->ObtenerNumeroPieza();
 	int desplazamiento;
	if (!laListaPiezas.Encontrar(desplazamiento, numeroPieza))
 		laListaPiezas.Insertar(nuevaPieza);
 	else
 	{
 		cout << numeroPieza << " fue la ";
 		switch(desplazamiento)
 		{
 			case 0: cout << "primera "; break;
 			case 1: cout << "segunda "; break;
 			case 2: cout << "tercera "; break;
 			default: cout << desplazamiento+1 << "a "; break;
 		}
 	cout << "entrada. Rechazada!\n";
 	}
}

int CatalogoPiezas::Existe(int NumeroPieza)
{
 	int desplazamiento;
 	laListaPiezas.Encontrar(desplazamiento, NumeroPieza);
 	return desplazamiento;
}

Pieza * CatalogoPiezas::Obtener(int NumeroPieza)
{
 	int desplazamiento;
	Pieza * laPieza = laListaPiezas.Encontrar(desplazamiento,NumeroPieza);
 	return laPieza;
}

int main()
{
 	CatalogoPiezas cp;
 	Pieza * apPieza = NULL;
 	int NumeroPieza;
 	int valor;
 	int opcion;
	while(1)
 	{
 		cout << "(0)Salir (1)Auto (2)Avion: ";
 		cin >> opcion;
		if(!opcion)
 			break;
 		cout << "Nuevo Numero de Pieza?: ";
 		cin >> NumeroPieza;
 		if (opcion == 1)
 		{
 			cout << "Año del modelo?: ";
 			cin >> valor;
 			apPieza = new PiezaAuto(valor,NumeroPieza);
 		}
 		else
 		{
 			cout << "Numero de Motor?: ";
 			cin >> valor;
 			apPieza = new PiezaAeroPlano(valor, NumeroPieza);
 		}
 		cp.Insertar(apPieza);
 	}
 	cp.MostrarTodo();
 	return 0;
}
Anuncios

En este ejemplo, bastante extenso, vamos a ver como trabajar con delegacion, primero vamos a crear una clase base, a la cual llamaremos Pieza. En esta vamos a tener una funcion miembro para el resto de las clases herederas, Desplegar(), luego crearemos una clase heredera de Pieza llamada PiezaAuto y en esta redefiniremos Desplegar(), despues tendremos otra clase llamada PiezaAeroPlano y en esta tambien redefiniremos Desplegar(), en ambos se redefinen para desplegar la informacion de cada una de sus clases, la siguiente clase sera NodoPieza. esta se encargara de conseguir algunos datos en las siguientes clases, la siguiente clase es ListaPiezas, esta se encargara de los metodos Iterar(), Encontrar() e Insertar(), ObtenerPrimero() y el operador [], este se encargara de armar un valor de la pieza y devolverla primero creando un apuntador  del tipo NodoCabeza chequea si esta vacia apCabeza en caso afirmativo devuelve NULL, el siguiente condicional if verifica si desplazamiento es mayor a suCuenta, en caso afirmativo tambien devuelve NULL y por ultimo utiliza un bucle for donde asigna a apNodo el siguiente valor almacenado en suSiguiente una vez completo procede a obtener ese valor y devolerlo, el Encontrar() se encarga de buscar y verificar si el valor informado ya fue subido, en caso afirmativo lo devuelve en caso contrario devuelve NULL, Insertar() este se encargara de ingresar los datos en forma ordenada, primero crearemos tres apuntadores del tipo NodoPieza, una variable int para almacenar el valor resultante de ObtenerPieza(), y otra variable int llamada Siguiente.

Anuncios

Primero incrementamos el valor de suCuenta, luego verifica si existe apCabeza, en este caso la condicion verdadera es que no exista, en caso de ser verdad la establece por medio de apNodo, en el siguiente condicional verifica si el nuevo valor es menor a apCabeza, en caso afirmativo lo asigna como tal, en el siguiente bucle for se encargara de ordenar los datos en forma correcta, y por ultimo establece el apActual con el valor de apSiguiente. la siguiente clase es CatalogoPiezas, esta clase se encargara principalmente de ingresar los nuevos ingresos y de chequear si existen o no, la funcion Insertar() se encarga de chequear si existe o no, en caso afirmativo nos devuelve un mensaje de error y no hace nada en caso contrario lo ingresa a traves de ListaPiezas. Siguiendo con el programa main(), vamos a crear un objeto del tipo CatalogoPiezas, crearemos un apuntador de tipo Pieza y tres variables de tipo int, luego utilizaremos un bucle while donde le diremos al usuario ingresar una opcion: cero para salir, uno para auto y dos para avion, si es cero usa el primer condicional disponible y sale del programa, en el segundo condicional if verifica si el valor es uno, en caso verdadero nos preguntara el año para luego crear un apuntador de tipo PiezaAuto, en caso contrario preguntara el numero de motor y luego creara un apuntador de tipo PiezaAeroPlano, en los dos casos se llama apPieza, luego utilizaremos el objeto creado del tipo CatalogoPiezas, y usaremos el metodo Insertar() y en caso de cumplir todas las condiciones de verificacion procedera a la carga de lo contrario nos mostrara un mensaje de error, les muestro la salida:

tinchicus@dbn001dsk:~/lenguaje/c++$ ./advher02
(0)Salir (1)Auto (2)Avion: 1
Nuevo Numero de Pieza?: 110011
Año del modelo?: 2010
(0)Salir (1)Auto (2)Avion: 2
Nuevo Numero de Pieza?: 220022
Numero de Motor?: 2012
(0)Salir (1)Auto (2)Avion: 1
Nuevo Numero de Pieza?: 110011
Año del modelo?: 2011
110011 fue la primera entrada. Rechazada!
(0)Salir (1)Auto (2)Avion: 2
Nuevo Numero de Pieza?: 330033
Numero de Motor?: 2014
(0)Salir (1)Auto (2)Avion: 2 
Nuevo Numero de Pieza?: 110011
Numero de Motor?: 2011
110011 fue la primera entrada. Rechazada!
(0)Salir (1)Auto (2)Avion: 0

Numero de Pieza: 110011
Año del modelo: 2010

Numero de Pieza: 220022
Motor Numero: 2012

Numero de Pieza: 330033
Motor Numero: 2014
tinchicus@dbn001dsk:~/lenguaje/c++$
Anuncios

En este ejemplo, como pueden ver se utilizaron listas enlazadas para ordenar los items cargados, la carga de los mismos se hace entre CatalogoPiezas y ListaPiezas esto gracias al miembro enlazado de ListaPiezas (laListaPiezas) en el privado de CatalogoPiezas, esto nos permite tener todo perfectamente «delegado» sin necesidad de repetir la estructura de ListaPiezas en CatalogoPiezas, procedamos a escribir una breve definicion de lo visto hasta ahora:

  • Contencion: La manera de declarar una clase como miembro de otra clase que es contenida por esta clase.
  • Delegacion: Uso de los atributos de una clase contenida para lograr funciones que no estan disponibles de otra forma para la clase contenedora.
  • Implementacion con base en: Construir una clase con base en las capacidades de otra, sin utilizar herencia publica.
Anuncios

Resumiendo, hoy hemos visto el metodo de contencion entre clases y delegacion, el primero hemos trabajado sobre un ejemplo de como poder utilizar mas de un tipo de clase para una clase conteniendo el objeto sin utilizar herencia publica, y en delegacion hemos visto como utilizar los atributos de una clase sin tener que duplicar por sobrecarga o a traves de herencia publica o derivados, espero les haya sido util sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios

Tengo un Patreon donde podes acceder de manera exclusiva a material para este blog antes de ser publicado, sigue los pasos del link para saber como.

Tambien podes donar

Es para mantenimiento del sitio, gracias!

$1.50

Anuncio publicitario