martes, 10 de enero de 2017

Capítulo 16. Clases y sus amigas

Clases dentro de Clases Una clase puede ser el atributo de otra clase. Veamos como
metemos la clase soldado dentro del tanque, esta seria la cabecera:
/**
* Tanque.hpp
* Clase que define el objeto Tanque . El objeto tanque estara lleno
* de Objeto soldados, lo que nos sirve para demostrar el uso de clases
* como atributos, etc..
*
* Pello Xabier Altadill Izura
*
*/
using namespace std;
#include <iostream>
#include "Soldado.hpp"
class Tanque {
public:
// constructores
Tanque();
Tanque(char *nombre, int proyectiles,Soldado soldado);
// destructor
~Tanque();
// copia
Tanque(Tanque const &);
// get/set
char *getNombre () const { return this->nombre; }
void setNombre (char *nombre) { this->nombre = nombre; }
int getProyectiles () const { return this->proyectiles; }
void setProyectiles (int proyectiles) { this->proyectiles = proyectiles; }
Soldado getSoldado () const { return this->soldado; }
void setSoldado (Soldado soldado) { this->soldado = soldado; }
void avanzar(int metros) const;
void disparar();
private:
char *nombre;
int proyectiles;
Soldado soldado;
};
Y su implementacion:
81
Capítulo 16. Clases y sus amigas
/**
* Tanque.cpp
* Programa que implementa la clase Tanque
*
* Pello Xabier Altadill Izura
* Compilacion: g++ Tanque.cpp -o Tanque
*/
#include "Tanque.hpp"
// Constructor
Tanque::Tanque(): nombre("Supertanque"),
proyectiles(10), soldado(Soldado()) {
cout << "-clase Tanque- Tanque " << nombre << " construido. Proyectiles: " << proyectiles << endl;
}
// Constructor parametrizado
Tanque::Tanque(char *nombre, int proyectiles, Soldado soldado) {
this->nombre = nombre;
this->proyectiles = proyectiles;
this->soldado = soldado;
cout << "-clase Tanque- " << nombre << " :Tanque construido. Proyectiles: " << proyectiles << endl;
}
// Destructor
Tanque::~Tanque() {
cout << "-clase Tanque- Tanque "<< this->getNombre() << " destruido."<< endl;
}
// constructor copia
Tanque::Tanque(const Tanque & original) {
nombre = new char;
nombre = original.getNombre();
cout << "-clase Tanque- Tanque copia creada."<< endl;
}
// metodo avanzar
void Tanque::avanzar(int metros) const {
cout << "-clase Tanque-" << this->getNombre() << " avanzando: " << metros << " m." << endl;
}
// metodo disparar
void Tanque::disparar(){
if (proyectiles > 0) {
proyectiles--;
cout << "-clase Tanque-" << this->getNombre() << "BOOOOM!!" << endl;
} else {
82
Capítulo 16. Clases y sus amigas
cout << "-clase Tanque-" << this->getNombre() << " No queda municion." << endl;
}
}
// funcion principal
// Aqui haremos multiples pruebas...
int main () {
int i, resp;
// creamos los Tanques
Tanque tanqueta = Tanque();
// podemos sacar lso datos del soldado asi:
cout << "El nombre del soldado es: " << (tanqueta.getSoldado()).getNombre()<< endl;
tanqueta.avanzar(5);
tanqueta.disparar();
tanqueta.getSoldado().matar();
return 0;
}
friend: haciendo amigos Mediante la palabra reservada friend podemos declara relaciones
de confianza entre clases y permitir que una clase amiga pueda acceder a los
atributos y metodos privados de esa clase. Veamos el ejemplo con la Pluma y la Espada.
La pluma vence a la espada pero ademas la declara como amiga porque es asi
de enrollada. Veamos la declaracion de Pluma:
/**
* Pluma.hpp
* Clase que define el objeto pluma, un objeto que sirve para escribir
*
* Pello Xabier Altadill Izura
*
*/
using namespace std;
#include <iostream>
class Pluma {
public:
Pluma();
Pluma(char *tipo, char *usuario);
~Pluma();
Pluma(Pluma const &);
// ATENCION!!! definimos la clase ESPADA como friend
// por tanto desde ella se podra acceder a los elementos PRIVADOS de la Pluma
friend class Espada;
char *getTipo() const { return this->tipo;}
char *getUsuario() const { return this->usuario;}
83
Capítulo 16. Clases y sus amigas
private:
// metodo para escribir con la pluma
void escribe (char *texto) {cout << "escribo con la pluma: " << texto << endl;}
void test() { cout << "Mega funcion privada de Pluma!" << endl;}
char *tipo;
char *usuario;
};
Y su implementacion:
/**
* Pluma.cpp
* Programa que implementa la clase Pluma
*
* Pello Xabier Altadill Izura
* Compilacion: g++ -c Pluma.cpp
*/
#include "Pluma.hpp"
// Constructor
Pluma::Pluma(): tipo("tinta china"), usuario("Bertrand Russel") {
cout << "Pluma construida." << endl;
}
// Constructor
Pluma::Pluma(char *tipo, char *usuario) {
this->tipo = tipo;
this->usuario = usuario;
cout << "Pluma construida de tipo: " << tipo << endl;
}
// Destructor
Pluma::~Pluma() {
cout << "Pluma destruida." << endl;
}
// Constructor copia
Pluma::Pluma(Pluma const & original) {
tipo = new char;
tipo = original.tipo;
}
Y ahora la declaracion de la Espada
/**
* Espada.hpp
84
Capítulo 16. Clases y sus amigas
* Clase que define el objeto Espada, un objeto que
sirve para matar
*
* Pello Xabier Altadill Izura
*
*/
using namespace std;
#include <iostream>
class Espada {
public:
Espada();
Espada(char *tipo);
~Espada();
Espada(Espada const &);
// desde este metodo accederemos a la
// parte privada de la pluma
void usarPluma (char *texto);
char *getTipo() const { return this->tipo;}
private:
char *tipo;
};
Y su implementacion:
/**
* Espada.cpp
* Programa que implementa la clase Espada
*
* Pello Xabier Altadill Izura
*
* Compilacion: g++ -o Espada Pluma.o Espada.cpp
*/
#include "Espada.hpp"
#include "Pluma.cpp"
// Constructor
Espada::Espada(): tipo("katana") {
cout << "Espada construida." << endl;
}
// Constructor
Espada::Espada(char *tipo) {
this->tipo = tipo;
cout << "Espada construida de tipo: " << tipo << endl;
}
85
Capítulo 16. Clases y sus amigas
// Destructor
Espada::~Espada() {
cout << "Espada destruida." << endl;
}
// Constructor copia
Espada::Espada(Espada const & original) {
tipo = new char;
tipo = original.tipo;
}
// metodo desde el que accedemos a Pluma
void Espada::usarPluma(char *texto) {
// implementamos una pluma y...
Pluma plumilla = Pluma();
// y ahora accedemos a sus miembros privados: atributos ...
cout << "La pluma es tipo: " << plumilla.tipo << endl;
cout << "Y su usuario es: " << plumilla.usuario << endl;
plumilla.escribe(texto);
// e incluso a sus metodos!
plumilla.test();
}
// funcion principal
int main () {
int i;
Espada tizona = Espada("mandoble");
// invocamos un metodo que accedere a la zona privada de la clase
tizona.usarPluma("jaja uso la pluma a mi antojo");
return 0;
}
La funcion amiga Podemos declarar una funcion como amiga y nos dara acceso a
TODO a traves de ese funcion. Para ilustrar esto definimos las clases Chico y Chica
en un unico fichero
/**
* ChicoChica.cpp
* Clase que define el objeto Chico y Chica. Chico tiene una funcion llamada
* esNovio que dentro de chica la declaramos como friend
y le dara acceso a todo
*
* Pello Xabier Altadill Izura
* Compilacion: g++ -o ChicoChica ChicoChica.cpp
*/
using namespace std;
#include <iostream>
86
Capítulo 16. Clases y sus amigas
class Chico {
public:
// constructor
Chico():nombre("Romeo") {}
// constructor
Chico(char *nombre) { this->nombre = nombre;}
// destructor
~Chico() {}
// constructor copia
Chico(Chico const & origen) {
nombre = new char;
nombre = origen.nombre;
}
// desde este metodo accederemos a la
// parte privada de la clase chica
void esNovio();
char *getNombre() const { return this->nombre;}
private:
char *nombre;
};
class Chica {
public:
// constructor
Chica():
nombre("Julieta"),
edad(23),
coeficienteInteligencia(140),
medidas("95-60-95") {
}
// destructor
~Chica() {}
// constructor copia
Chica(Chica const & origen) {
nombre = new char;
nombre = origen.nombre;
}
// Aqui definimos un metodo friend externo
// que tendra acceso a toda la clase chica
friend void Chico::esNovio();
87
Capítulo 16. Clases y sus amigas
// otra opcion seria declara Chico como friend:
// friend class Chico;
private:
void pensar() { cout << "estoy pensado..." << endl; }
void entrarHabitacion() { cout << "estoy entrando en la habitacion..." << endl; }
char *nombre;
int edad;
int coeficienteInteligencia;
char *medidas;
};
// implementacion de la funcion del chico esNovio
void Chico::esNovio() {
Chica neska = Chica();
neska.entrarHabitacion();
cout << "Con esta funcion entro en todo! " << endl;
cout << "Dime tu edad real chica: " << neska.edad << endl;
cout << "Y tu coeficiente intelectual: " <<
neska.coeficienteInteligencia << endl;
cout << "joder, me parece que no te gustara el futbol." << endl;
}
// funcion principal, para las pruebas
int main () {
int i;
Chico mutiko = Chico();
// vamos a ver si llamamos a esNovio...
mutiko.esNovio();
return 0;
}
88
Capítulo 17. Entrada/Salida
Entrada y salida A vueltas con el flujo (cin cout), vamos a ver un uso mas extendido
del habitual. De paso conoceremos algunas de las trampas que nos esperan con los
flujos, sobre todo por el tema de buffers. Veamos este ejemplo comentado
/**
* Flujos.cpp
* Programa para mostrar el uso de flujos
*
* Pello Xabier Altadill Izura
* Compilacion: g++ -o Flujos Flujos.cpp
*/
using namespace std;
#include <iostream>
// Disponemos de varios flujos:
// cin : para la entrada de distintis tipos de datos (std input)
// cout : para la salida de distintos tipos de datos (std output)
// cer: para la salida de errores (std error)
// clo: para la salida de errores y redireccion a ficheros tipo log
// cin utiliza buffers y nos podemos llevar sorpresas al recoger datos
// si el usuario no los mete bien. Por ejemplo si se pide una palabra y se meten
// dos, la siguiente vez que se pida otro dato se tomara el que se habia metido!
// lo podemos evitar con cin.ignore
// Funcion principal
int main () {
unsigned int i;
char nombre_apellidos[25];
char linea[80];
int entero;
char caracter;
// ATENCION
// hay que tener cuidado con los strings. prueba a meter nombre y apellido
// y veras que el string solo se llena hasta el primer espacio en blanco,
// o incluso para a la siguiente variable i y el programa casca.
cout << "Mete tu nombre y tu apellido resalao: " << endl;
cin >> nombre_apellidos;
cout << "Tu nombre y apellidos: " << nombre_apellidos << endl;
// con esta llamada evitamos que se tome en cuenta las sobras
cin.ignore(255,’\n’);
// Entrada multiple!
cout << "Mete una palabra y un numero entero" << endl;
cin >> nombre_apellidos >> entero;
cout << "El texto: " << nombre_apellidos << " y el entero: " << entero << endl;
// explicacion: >> es un operador que se puede sobrecargar como hemos visto
// anteriormente: la expresion cin >> nombre_apellidos devuelve otro objeto iostream
// y se podria reescribir asi: (cin >> nombre_apellidos) >> entero;
// cin.get(string,tama&ntilde;o) para recoger string completos
cout << " Mete un string largo con espacios. " << endl;
cin.getline(linea,80);
cout << "resultado: " << linea << endl;
89
Capítulo 17. Entrada/Salida
// hay veces que puede interesar ignorar un numero de caracteres hasta llegar al final
// de la linea, para eso podemos usar la funcion cin.ignore(70,’\n’); en lugar de \n
// podemos usar cualquier caracter de terminacion que nos interese.
// no hay que olvidar que cin es un buffer. Que pasa si solo queremos leer un caracter
// sin tener que ’sacarlo’ del buffer? podemos usar cin.peek() y si queremos meter
// un caracter podemos usar cin.putback(’.’) -meteria un . en el buffer de cin
// cin.get() tomando un unico caracter. Si metemos mas imprimira todos
// puede usarse parametrizado: cin.get(caracter)
cout << "Vete metiendo caracteres. termina con un ." << endl;
while ( (caracter = cin.get()) != EOF) {
if ( cin.peek() == ’.’ ) {
cout << "nos vamos" << endl;
break;
}
cout << caracter;
}
cin.ignore(255,’\n’);
return 0;
}
En este otro se habla mas de cout
/**
* FlujosOut.cpp
* Programa para mostrar el uso de flujos de SALIDA
*
* Pello Xabier Altadill Izura
* Compilacion: g++ -o FlujosOut FlujosOut.cpp
*/
using namespace std;
#include <iostream>
// cout tambien utiliza buffers y nos podemos llevar sorpresas al recoger datos
// aunque si queremos tirar de la cadena ejecutamos:
cout << flush;
// Funcion principal
int main () {
unsigned int i;
char nombre_apellidos[25];
char linea[80];
int entero;
char caracter;
char frase[] = "Clatu verata nictu\n";
// si en cin teniamos get aqui tenemos: put
// mandamos un saludo
cout.put(’K’).put(’a’).put(’i’).put(’x’).put(’o’).put(’\n’);
// vamos a mostrar una linea:
90
Capítulo 17. Entrada/Salida
entero = strlen(frase);
// con esto la mostramos entera
cout.write(frase,entero);
// con esto... no
cout.write(frase, (entero-5));
cout << " ahora con formato: " << endl;
// vamos a ponerla con cierto formato: width y fill
cout.width(30); // esto mete espacios en blanco equivalente = setw(30)
cout << frase << endl;
cout.width(50); // esto vuelve a meter espacios
cout.fill(’>’); // y esto RELLENA los ESPACIOS
cout << frase << endl;
// Estableciendo el estado de cout con setf
// alineacion: setf(ios::left) y setf(ios::right)
// hay mas, para investigar: ios::showbase, ios::internal, etc...
cout.setf(ios::right);
entero = 666;
// podemos alterar la base con dec, oct y hex
cout << "entero hexadecimal alineado a la derecha: " << hex << entero << endl;
return 0;
}
Ficheros en c++ Oh si, podemos manejar ficheros de entrada/salida con las clases
mas std. veamos unos ejemplos, metidos dentro de un objeto. Es bastante mejorable.
/**
* Fichero.hpp
* Clase que define el objeto Fichero, un objeto que
sirve gestionar un fichero
*
* Pello Xabier Altadill Izura
*
*/
using namespace std;
#include <iostream>
#include <fstream> // atencion hay que incluir esto
enum tipo_fichero { ENTRADA, SALIDA, APPEND };
class Fichero {
public:
Fichero();
Fichero(char *nombre, tipo_fichero tipo);
~Fichero();
Fichero(Fichero const &);
char *getNombre() const { return this->nombre;}
91
Capítulo 17. Entrada/Salida
// operaciones sobre ficheros
int cerrar () const; // cierra el fichero
char leer() const; // lee del fichero
void escribir (char linea[255]) const; // escribe linea
private:
// esta funcion decide que tipo de fichero iniciar
void inicializaFichero();
tipo_fichero tipo;
char *nombre;
ofstream *saliente;
ifstream *entrante;
};
Y su implementacion.
/**
* Fichero.cpp
* Programa que implementa la clase Fichero
*
* Pello Xabier Altadill Izura
* Compilacion: g++ -o Fichero Fichero.cpp
*
*/
#include "Fichero.hpp"
// Constructor
Fichero::Fichero(): nombre("test.txt"), tipo(ENTRADA) {
inicializaFichero();
cout << "Fichero construido." << endl;
}
// Constructor
Fichero::Fichero(char *nombre, tipo_fichero tipo) {
this->nombre = nombre;
this->tipo = tipo;
inicializaFichero();
cout << "Fichero construido con nombre: " << nombre << endl;
}
// Destructor
Fichero::~Fichero() {
cout << "Fichero destruido." << endl;
}
92
Capítulo 17. Entrada/Salida
// Constructor copia
Fichero::Fichero(Fichero const & original) {
nombre = new char;
nombre = original.nombre;
}
// cierra el fichero
int Fichero::cerrar () const {
if (this->tipo == 0) {
entrante->close();
} else {
saliente->close();
}
return 0;
}
// lee linea del fichero
char Fichero::leer () const {
return entrante->get();
}
// escribir sobre el fichero
void Fichero::escribir (char linea[255]) const {
saliente->write(linea,255);
}
// esta funcion decide que tipo de fichero iniciar
void Fichero::inicializaFichero() {
switch (this->tipo) {
case 0 : cout << "tipo ENTRADA" << endl;
entrante = new ifstream(this->nombre);
break;
case 1 : cout << "tipo SALIDA" << endl;
saliente = new ofstream(this->nombre);
break;
case 2 : cout << "tipo APPEND" << endl;
saliente = new ofstream(this->nombre,ios::app);
break;
default : cout << "nada" << endl;
break;
93
Capítulo 17. Entrada/Salida
}
}
// funcion principal, en la que de paso vemos
// PARAMETROS de linea de comandos
int main (int argc, char **argv) {
int i;
char temp;
char linea[255];
// vamos a revisar los argumentos que se han pasado al programa
for (i=0; i<argc; i++) {
cout << "argumento (" << i << "): " << argv[i] << endl;
}
Fichero fichero = Fichero("prueba.txt",APPEND);
cout << "escribe algo para a&ntilde;adir al fichero: ";
cin.getline(linea,255);
cout << "has puesto: " << linea << endl;
fichero.escribir(linea);
fichero.cerrar();
// leyendo de forma directa. Leemos el parametro que hayamos pasado
ifstream leer("prueba.txt");
// abrimos el fichero
leer.open("prueba.txt");
// recorremos el fichero y mostramos contenido
while ( leer.get(temp) ) { // esto indica el final
cout << temp;
}
// cerramos el fichero
leer.close();
return 0;
}

No hay comentarios:

Publicar un comentario