Practica7

=**Practica 7: [|Librería de Entrada/Salida (E/S)]**=

**1. Introducción**
C++ define una jerarquía de clases que permite usar de forma muy similar cualquier dispositivo de E/S (consola, ficheros, cadenas,...)

La librería se llama iostream y se define clases de solo entrada, de solo salida y de entrada/salida. Toda la idea se basa en el concepto de flujo (stream). Y en función de si el flujo es de entrada se tienen las clases istream, si es de salida se define la clase ostream, y si es de E/S se define la clase iostream. Despues, de estas clases derivan clases especificas para cada dispositivo. Por ejemplo, la clase cout, se usa para salida por consola (hereda de ostream). La clase cin, para entrada por consola (hereda de istream).

Igualmente, para ficheros. La clase ofstream, se usa para manejar ficheros donde solo queremos escribir. La clase ifstream, se usa para acceder a ficheros de solo lectura. Y la clase fstream, se usa para poder escribir y leer en ficheros. La principal ventaja de usar una jerarquía de clases, es que permite trabajar de forma muy estandarizada.

Veamos un ejemplo (testio.cpp) code format="cpp" using namespace std; int main {
 * 1) include //incluye cin y cout
 * 2) include // incluye ifstream, ofstream y fstream

int ivalue=10; float fvalue=15;

cout<>ivalue; cout<<"El valor introducido es ="<>ivalue>>fvalue;

cout<<"Valores leidos de fichero "<<ivalue<<" "<<fvalue<<endl;

}

code

Bueno, más simple imposible. Fijaros que en realidad, las clases de C++ ofstream y ifstream, internamente están usando las librerías de E/S de C. Lo bueno es que están protegiéndolas para evitar usos indebidos. En realidad, las instrucción: code format="cpp" ofile.open("file.txt");

code Internamente esta haciendo: code format="cpp" FILE *f=fopen("file.txt","r"); code

**2. Ficheros de Salida**
En esta sección explicaremos en detalle el uso de los ficheros de salida

**2.1 Apertura y cierre**
Para explicar las clases de salida, nos centraremos en la clase ofstream.En primer lugar esta clase define un constructor vacío. Pero también define un constructor parametrizado que es equivalente a la llamada a open. Es decir

code format="cpp" ofstream ofile; ofile.open("file.txt");

code

es equivalente a code format="cpp" ofstream ofile ("file.txt"); code La función open, así como el constructor parametrizado, reciben dos parámetros. El segundo es un parámetro por defecto que especifica el modo de apertura del fichero. Para definir los modos de apertura, se definen las //variables estáticas de clase// siguientes.


 * ios::app: abre el fichero y escribe siempre al final
 * ios::trunc : abre el fichero
 * ios::binary :.......

Para más información vease el manual de la libreria [|iostream.] Estas variables son flags que se pueden usar en la apertura. Ejemplo: code format="cpp" ofstream ofile("file.txt",ios::app);

code

Por defecto, la opción de apertura es truncate (elimina el contenido actual). Es decir:

code format="cpp" ofstream ofile("file.txt"); code

equivale a code format="cpp" ofstream ofile("file.txt",ios::trunc); code Para cerrar el fichero. Basta con llamar a la función close. Pero además, hay un aspecto positivo. Si no llamamos de forma explicita a close, el destructor de la clase lo hará por nosotros.

La escritura de ficheros puede ser binaria o de texto. Para escritura de texto de tipos genericos (float,int ,char ...), se usa el operador <<. El operador << permite concatenar entradas por el fichero. code format="cpp" ofstream ofile("file.txt") if (!ofile) exit(1); ofile<<ivalue<<" "<<fvalue<<endl;//escribe en el fichero code

Ahora bien, también es posible definir el operador << para nuestras clases de forma que la entrada y salida puede ser muy cómoda. Fijaros en cómo se hace (testio2.cpp) code format="cpp" using namespace std;
 * 1) include
 * 2) include
 * 3) include

class MyIOTest { public: MyIOTest{_val=0;} MyIOTest(int v){_val=v;} MyIOTest(const MyIOTest & M){ _val=M._val; } friend ostream & operator<<(ostream &str,const MyIOTest &M) {       str<<M._val; return str; }

private: int _val; };

int main {   MyIOTest M1,M2(2);

cout<<M1<<" "<<M2<<endl;

ofstream ofile;//Crea fichero ofile.open("file.txt"); //lo abre //comprueba que se ha podido abrir mediante el operador negación (!) assert(ofile.is_open); ofile<<M1<<" "<<M2<<endl;//escribe en el fichero ofile.close;//cierra el fichero } code

//Lo único que debemos es definir el operador << en nuestra clase// code format="cpp" friend ostream & operator<<(ostream &str,const MyIOTest &M) {       str<<M._val; return str; } code

La palabra clave friend es para indicar que MyIOTest es una clase amiga de ostream. Por ello, puede acceder a los elementos privados de la misma. Pero de eso no preocuparos por el momento. Con lo que nos debemos quedar es que como argumento recibe un ostream por referencia, es decir, que sirve tanto para ofstream como para cout, y que tras escribir lo deseado en ese stream, se retorna. La idea es que está función modifica el stream str pasado como parámetro (por referencia) y después el stream modificado es pasado para que pueda ser modificado por otro elemento. De ahi que sea posible hacer cosas como

code format="cpp" cout<<M1<<" "<<M2<<endl; code

Ya que lo que se hacuso de es llamar primero a << con endl, después, se le pasa a M2, después a “ “, después a M1 y finalmente a cout que coge toda la información la pone en consola.

2.3 ****Escritura en modo binario****
Para escribir en modo binario, tenemos la función write. Ejemplo: (testio3.cpp):

code format="cpp" using namespace std; int main { //Create vector and fill it int nE=10; int *v=new int[nE]; for(int i=0;i<nE;i++) v[i]=i; //open new file ofstream vfile("file.vec"); assert( vfile.is_open); //write the num elements of the vector (an integer) vfile.write((char*) &nE,sizeof(int)); //writes the elements of the vector vfile.write((char*) v,nE*sizeof(int)); } code
 * 1) include //incluye cin y cout
 * 2) include // incluye ifstream, ofstream y fstream
 * 3) include

Como se puede ver, es algo igualmente simple. Lo único que merece la pena indicar es que write espera recibir un (char*). Por tanto, tenemos que hacer un casting adecuado. También, que como segundo elemento requiere el número de bytes a escribir.

2.4 Resumen
code format="cpp" ofstream file; file.open(name); //abrir file.is_open; //esta abierto? file<< var1<<var2 <<... //escribir en modo texto file.write((char*)&var,sizeof(var));//escribir en modo binario file.close; //cerrar code

**3. Ficheros de Lectura**
En esta sección explicaremos en detalle el uso de los ficheros de salida

3.1 Apertura y cierre
La apertura es identica a un fichero de salida: usamos el contructor o la funcion open. En este caso, normalmente, no será necesario usar parámetros adicionales al nombre del fichero.

3.2 Lectura modo texto
La lectura es análoga a la escritura. Si vamos a leer un archivo que está en formato texto solemos usar los operadores >>. Podemos por tanto definir el operador >> para nuestras clases ( testio4.cpp):

code format="cpp" using namespace std;
 * 1) include
 * 2) include
 * 3) include

class MyIOTest { public: MyIOTest{_val=0;} MyIOTest(int v){_val=v;} MyIOTest(const MyIOTest & M){ _val=M._val; } friend ostream & operator<<(ostream &str,const MyIOTest &M) {       str<>(istream &str,MyIOTest &M) {       str>>M._val; return str; }   bool operator==(const MyIOTest &M){ return _val==M._val;}

private: int _val; };

int main {   MyIOTest M1,M2(2);

ofstream ofile;//Crea fichero ofile.open("file.txt"); //lo abre //comprueba que se ha podido abrir mediante el operador negación (!) assert(ofile.is_open); ofile<>M3>>M4; ifile.close; cout<<"Readed "<<M3<<" "<<M4<<endl; assert(M1==M3 && M2==M4); cout<<"Perfect"<<endl; } code

Como se puede observar, el funcionamiento es bastante similar.

3.3 Lectura modo binario
Para lectura binaria, usamos la función read. Atención, no hay que especificar en la pertura nada de que el fichro es binario si no se desea. podremos abrir un fichero y leer alternativamente en modo binario o texto sin ningun problema.

Ejemplo ( testio5.cpp):

code format="cpp" using namespace std;
 * 1) include
 * 2) include
 * 3) include
 * 4) include

void writeVectorFile(string fileName,int *v,int nElements) {   //opens the file ofstream ofile; ofile.open(fileName.c_str); //important note: fileName.c_str returns a (const char*) with the contents // of the string. It is required since the open function requires a char* //as first argument

//assert the file is ooened assert(ofile.is_open); //writes the number of elements of the vector ofile.write((char*) &nElements,sizeof(int)); //writes the elements of the vector ofile.write((char*) v,nElements*sizeof(int)); //closes ofile.close; }

void readVectorFile(string fileName,int * &v,int &nElements) {   //opens the file ifstream ifile; ifile.open(fileName.c_str); //important note: fileName.c_str returns a (const char*) with the contents // of the string. It is required since the open function requires a char* //as first argument

//assert the file is ooened assert(ifile.is_open); //read the number of elements of the vector ifile.read((char*) &nElements,sizeof(int)); //read the elements of the vector if (v!=NULL) delete v;   v=new int[nElements]; ifile.read((char*) v,nElements*sizeof(int)); //closes ifile.close; }

int main {

//Create vector and fill it int nE=10; int *v=new int[nE]; for(int i=0;i> y << podemos definirlos para que produzcan E/S binaria si lo preferimos:

code format="cpp" class MyIOTest { public: MyIOTest{_val=0;} MyIOTest(int v){_val=v;} MyIOTest(const MyIOTest & M){ _val=M._val; } friend ostream & operator<<(ostream &str,const MyIOTest &M) {       str.write((char*)&M._val,sizeof(int)); return str; }    friend istream & operator>>(istream &str,MyIOTest &M) {       str.read((char*)&M._val,sizeof(int)); return str; }   bool operator==(const MyIOTest &M){ return _val==M._val;}

private: int _val; }; code

Esto ya es algo que tenemos que decidir nosotros al crear la aplicación.

3.4 Moverse por el fichero
La clase ostream tiene un puntero a la posicion del flujo donde se esta leyendo, es lo que podemos llamar el puntero get (coger datos). Para saber en que posicion del fichero esta, basta con llamar a la función tellg de la clase y nos devolvera el numero de bytes desde el principio hasta la posicion del puntero.

Para moverse dentro del fichero tendremo las funcion //seekg//. file.seekg(0,ios::beg): mueve al principio, como fseek(f,0,SEEK_SET) file.seekg(0,ios::end): mueve al final, como fseek(f,0,SEEK_END) file. seekg(1,ios::curr): mueve una posicion hacia adelante desde la posicion actual, como fseek(f,1,SEEK_CUR);

log pos=file.tellg; Me da la posicion.

3.5 Fin de fichero
Para detectar el fin de fichero tengo file.eof. Retorna un booleano que es true si se ha llegado al fin del fichero.

3.6 Resumen
code ifstream file; file.open(fileName); //abrir. NO hace falta que especifique si es binario o no file>>va1>>var2; //leer variables que estan guardadas como texto y separadas por espacios, tabuladores o fin de linea file.read((char*)&var,sizeof(var)); //lectura de datos binarios file.seekg(0,ios::beg); //se va al principio file.seekg(0,ios::end); //se va al final file.seekg(1,ios::cur); //se va 1 caracter hacia adelante file.tellg; //number de bytes hasta el principio del fichero file.eof; //indica si se ah llegado al fin del fichero

code

**4. Flujos de E/S**
Como ya hemos comentado anteriormente, C++ proporciona la clase fstream, que permite entrada y salida usando el mismo objeto. Para ello, internamente tiene una pareja de “punteros” independientes al flujo E/S. El primer puntero esta posicionado en la posición de lectura. El segundo, esta posicionado en el lugar del flujo en el cual estamos escribiendo. Se les llama put and get pointers.

En los casos anteriores, ifstream tiene solo get pointer y ofstream tiene put pointer. Ahora puede no quedar claro pero se verá con más claridad en el siguiente ejemplo:

code format="cpp" using namespace std; int main {
 * 1) include
 * 2) include
 * 3) include

//Tryto open the file fstream file("myFile.mf",ios::in | ios::out ); //If is not open is because it does not exits //Then, we must create it   if (file.is_open ==false) {       cout<<"Creating file"<<endl; file.open("myFile.mf", ios::out ); //se only out, then is created file.close; //reopen the file now that is created file.open("myFile.mf",ios::in | ios::out ); assert(file.is_open);

}

//Lets check were are the put and get pointers //tellp returns the position of the put pointer(in relation to the beginning) int posP=file.tellp; //now, lets see where is the get pointer using tellg int posG=file.tellg; //Print the information cout<<"Put Pos="<<posP<<" Get Pos="<<posG<<endl;

//Let's see how to calculate the file size //First, move the get pointer to the end file.seekg(0,ios::end); //now, you can know the size cout<<"File size="<<file.tellg<<endl;

//You'll see that the put pointer has moved cout<<"File size="<<file.tellp<<endl;

//write 10 elements at the begining of the file //first, set the put pointer at the beggining file.seekp(0,ios::beg); float f;   for(int i=0;i<10;i++){ f=float(i); file.write((char*)&f,sizeof(float)); }

//now, lets read the elements just written //But first, move the pointer back to the beginning since it is at the end file.seekg(0,ios::beg); for(unsigned int i=0;i<10;i++){ file.read((char*)&f,sizeof(float)); cout<<f<< " ";cout.flush; assert(f==float(i)); }   cout<<endl; //Great, now, let's modify the second element //go to the beggining of the second element with the put pointer file.seekp(sizeof(float),ios::beg); //now, overwrite data f=1000; file.write((char*)&f,sizeof(float));

//lets read everything again //Since the get pointer is at the end, wemust move it to the beginning file.seekg(0,ios::beg); for(unsigned int i=0;i<10;i++){ file.read((char*)&f,sizeof(float)); cout<<f<< " "; assert(f==float(i) || (i==1 && f==1000)); }   cout<<endl; //Ok, done file.close; }

code

Es importante indicar aqui, que cuando abrimos un fichero para lectura y escritura simultanea, si el fichero no existe no se crea. Por tanto, en caso de que no se pueda abrir es que no existe. Para crearlo lo abrimos en modo de escritura solo, con lo que sea crea, y despues lo abrimos para lectura y escritura.

5. Ejercicios
En esta ocasión, vamos a realizar una ejercicio algo más extenso. El objetivo es crear una lista de empleados que estará almacenada en fichero y que podrá se modificada, añadiendo elementos, borrándolos e incluso modificándolos.

Para ello, comenzaremos creando una jerarquía de clases que permitan manejar de forma genérica registros en ficheros. En primer lugar, crearemos la clase abstracta Register. Esta clase representa un registro que puede ser leido o escrito en fichero. Después, crearemos la clase RegisterFileManipulator. Esta clase permite leer y escribir registros de tipo Register. Ahora, la idea es que podamos mantener un registro de cualquier tipo de clase usando RegisterFileManipulator, lo único que se deberá es heredar de Register. Por tanto, crearemos una clase Employee que hereda de Register.

El fichero que creemos, tendrá el siguiente formato: code SIGNATURE NELEM REG0 REG1 REG2 ... SIGNATURE (firma en ingles): Entero: Numero que identifica el tipo de fichero. Este número es diferente en función del tipo de Clase que se esté guardando (más adelante veremos como se define esto) NELEM: Entero: Numero total de registros en el fichero REG(i) i-esimo registro

code

A continuación se explican mejor cada clase.

En primer lugar, crearemos la clase Register. Esta es una clase abstracta, esto quiere decir que tiene, al menos, una función virtual pura.

code CLASE: Register HEREDA DE : ninguna FICHERO DELCARACION:  FICHERO IMPLEMENTACION: ninguno ESPACIO DE NOMBRES: storage

DESCRIPCION: La clase representa un registro genérico que puede ser leído o escrito. La clase tiene todos los miembros virtuales puros. -- FUNCIONES MIEMBRO PUBLICAS: -- FUNCION: virtual ~Register ESPECIFICACION: Destructor de la clase

FUNCION: int getSignature ESPECIFICACION: Función que deberá ser virual pura. Retorna un entero que identifica la clase. Cada clase, que herede de Register, debe retornar un número y este debería ser diferente para cada clase.

FUNCION: void writeToStream(ostream & str) ESPECIFICACION: Función que deberá ser virtual pura. Esta función hace que la clase guarde su estado(variables) en el stream pasado. Lo hará en forma binaria (usando write).

FUNCION: void readFromStream(istream &str) ESPECIFICACION: Función que deberá ser virtual pura. Esta función hará que la clase lea su estado desde el stream pasado.

FUNCION: int getSizeOfRegister ESPECIFICACION: Función que deberá ser virtual pura. Devuelve el tamaño en bytes escritos en la llamada a writeToStream o leído en readFromStream (deben ser los mismos) code

code CLASE: RegisterFileManipulator HEREDA DE : ninguna FICHERO DELCARACION:  FICHERO IMPLEMENTACION:  ESPACIO DE NOMBRES: storage

DESCRIPCION: Esta clase se encarga de leer y reescribir registros en un fichero de tipo binario. A continuación se proporciona la declaración de la clase. Usted deberá implementarla.

DECLARACION : registerfilemanipulator.h, (está más abajo)

code

code format="cpp" using namespace std; namespace storage { /**\brief This class allows to keep a file with registers _signature _nElments REG(0) REG(1) ... REG(NELEMENTS-1) Where: _signature:Integer that indicates the type of register _nElments: Integer that indicates the total number of registers REG(i): i-th register class RegisterFileManipulator {   public: /**Emtpy constructor */       RegisterFileManipulator;
 * 1) ifndef _RegisterFileManipulator_H__
 * 2) define _RegisterFileManipulator_H__
 * 3) include 
 * 4) include
 * 5) include
 * The file has the following format:
 * The file has the following format:

/**Parametrized constructor. Creates an object opening the file indicated *@param filePath path to the file to be managed */       RegisterFileManipulator(string filePath);

/**Destructor. Closes the file if opened */       ~RegisterFileManipulator; /**Opens a file. If the objet has already an opened file, the program is aborted. *       * If the file is not created, this function creates it. Then, writes the _signature. Since at this point  _signature is not known, * writes -1 indicating that the signature is not valid yet. Then, writes the _nElments that at this point must be 0. *       * If the file is already created, the function reads _signature and _nElments. *       *@param true if  the file can be opened withour problems. false if the file can't be opened */       bool open(string filePath);

/**Indicates wherther the file is already opened */       bool isOpen const;

/**Closes the file if opened.If it is not opened, does nothing */       void close; /**When the file is opened, returns the signature of the class stored in the file. * If the file does not contains any register yet, returns -1. */       int getSignatureconst;

/**Returns the number of registers in the file */       unsigned int sizeconst;

/**Adds a register to the end of the file. *       * If the file is not opened, the program is aborted. *       * If it is the first register to be written, first, get the _signature from the Register. Second, write _signature in the file. Thrid, * write the new value of _nElments in the file. Finally, write the register *       * If it is not the first register in the file, the function must examine wherther * the signature of the register passed is the one of the * elements already stored in the file. If so, write the register at the end of the file, and then, update  _nElments in the file. *       * In case, the register passed as parameters is not of the same type than these stored previously, the register is not written and the * function returns false. *       *@return true if the register can be written and false otherwise */       bool add(Register *R);

/**Reads the i-th register and the information is introduced in "r" *       * If the file has not elements, return false and "r" is not modified *       * If the file is closed, return false and "r" is not modified *       * If "i" is greater or equal than _nElments, return false and "r" is not modified *       * If the signature of "r" is no the same that the _signature of the file, returns false and "r" is not modified *       * Else, "r" contains the information of the i-th register and true is returned *       *Note: you can set the put pointer in the position desired using the Register::getSizeOfRegister function */       bool read(unsigned int i,Register *r) ;

/**Overwrites the register indicated. This function goes to the position of the register indicated and rewrites it with the new value * passed. *       * If the file is closed, return false and the file is not modified

* If the signature of "r" is no the same that the _signature of the file, returns false and the file is not mdified. *       * If "i" is greater or equal that the number of elements, returns false and the file is not modified. *       * Else, rewrites the i-th register with "r" and returns true *       *Note: you can set the put pointer in the position desired using the Register::getSizeOfRegister function */       bool overWrite(unsigned int i,Register *r);

private: //This object manipulates the file. Open it for I/O access fstream _file; //Number of registers in the file unsigned int _nElments; //Signature of the elements in the file int _signature; //Indicates whether the file open bool _isOpen; };

}; code
 * 1) endif

//Ahora//

code //CLASE: Employee HEREDA DE: Register de forma pública FICHERO DELCARACION:  FICHERO IMPLEMENTACION: ninguno ESPACIO DE NOMBRES: company

DESCRIPCION: Esta clase representa un empleado de la empresa. La clase se creara en linea.

- ATRIBUTOS PUBLICOS: -- ENUMERACION: Positions (Ver mas abajo para explicación) VALORES POSIBLES: 0: maintainace 1: proffessor 2: cathedratic - ATRIBUTOS PRIVADOS: -- ATRIBUTO: _name TIPO: char [100]

ATRIBUTO: _dateOfBirth TIPO: time_t

ATRIBUTO: _salary TIPO: float

ATRIBUTO: _position TIPO: Positions

-- FUNCIONES MIEMBRO PUBLICAS: -- FUNCION: Employee DESCRIPCION : Constructor vacio

FUNCION: Employee(const Employee &E) DESCRIPCION : Constructor de copia

FUNCION: char * getName DESCRIPCION : Retorna el nombre

FUNCION: void setName(char * n) DESCRIPCION : Fija el nombre

FUNCION: time_t getDateOfBirth DESCRIPCION : ...

FUNCION: void setDateOfBirth(time_t) DESCRIPCION : ...

FUNCION: float getSalary DESCRIPCION : ...

FUNCION: void setSalary(float) DESCRIPCION : ...

FUNCION: void setPosition(Positions) DESCRIPCION : ...

FUNCION: Positions getPosition DESCRIPCION : ...

FUNCION: bool operator==(const Employee & E) DESCRIPCION : Indica si dos empleados son iguales comparando nombre y fecha de nacimiento

FUNCION: Employee & operator=(const Employee & E) DESCRIPCION : Operador de asignación

FUNCION: operator<< DESCRIPCION : Operador de salida code

Ademas de estos, deberá reimplementar las funciones que requiera al heredar de Register. Es decir, aquellas que son virtuales puras en Register.

A continuación, se da el código que deberá ejecutar para comprobar que su codigo funciona (testregister.cpp)

code format="cpp" using namespace storage; using namespace company; int main(int argc,char **argv) {   if (argc!=2){ cout<<"Uso: file"<
 * 2) include 
 * 3) include

cout<<"Error en la apertura del fichero"<<endl; return 1; }   Employee Eaux;

//Vamos a comenzar comprobando si el tipo de fichero es adecuado if (File.getSignature!=-1 && File.getSignature!=Eaux.getSignature){ cout<<"El fichero no es del tipo adecuado"<<endl; return 1; }

//Creamos un vector de empleados que agregaremos al final Employee *E=new Employee[3]; //Damos nombre y tal time_t tnow; time(&tnow); E[0].setName("antonio"); E[0].setDateOfBirth(tnow); E[0].setSalary(10); E[0].setPosition( Employee::mantainace );

E[1].setName("juan"); E[1].setDateOfBirth(tnow); E[1].setSalary(8); E[1].setPosition( Employee::proffessor );

E[2].setName("chaves"); E[2].setDateOfBirth(tnow); E[2].setSalary(99999); E[2].setPosition( Employee::catedratic );

//Comprobamos el numero de elementos ahora int size=File.size;

//añadimos los 3 elementos elementos for(int i=0;i<3;i++) File.add( & E[i] ) ;

//ahora, los leemos para comprobar que todo ha sido correcto for(int i=0;i<3;i++){ File.read(size+i, & Eaux ) ; assert( Eaux==E[i]); }   //Ok, vamos a modificar un registro porque //nos hemos equivocado. EL suldo del 3er elemento es incorrecto E[2].setSalary(9999999); //ahora esta bien File.overWrite(size+2,&E[2]); //comprobamos que esta correcto File.read(size+2, & Eaux ) ; assert( Eaux==E[2]);

//Vamos a imprimir la information escrita solo por verla for(unsigned int i=0;i<File.size;i++){ File.read(i, & Eaux ) ; cout<<Eaux<<endl; }

//Ok, todo bien cout<<"Perfect!!!"<<endl;

} code


 * Nota de ayuda 1:

//En C++ podemos definir una enumeración con enum. La enumeración se comporta como un tipo de dato entero que puede usarse con un nombre asignado. Ejemplo://**

code format="cpp" using namespace std; class MyC{ public: enum Puesto{bedel=0,profesor=1,manteniento=2}; MyC{} void imprimePuesto(Puesto p)       { switch(p){ case bedel:{cout<<"bedel"<<endl;}break; case profesor:{cout<<"profesor"<<endl;}break; case manteniento:{cout<<"manteniento"<<endl;}break; default:{cout<<"??????????"<<endl;} };       }
 * 1) include
 * 2) include
 * 3) include

};

int main {   MyC C;    C.imprimePuesto(MyC::bedel); C.imprimePuesto(MyC::profesor);

} code

En el ejemplo, se declara el tipo de dato Puesto que en realidad es la enumeración de una serie de valores posibles. Cada posible valor tiene asociado un nombre que puede usarse posteriormente.

//**Nota de ayuda 2:**//

Para clarificar lo que se desea que implementeis, a continuación se expone el código de un problema similar. En este caso, queremos llevar el registro de una secuencia de enteros. Para ello, definimos la clase IntSequenceFile que mantiene una secuencia de enteros en fichero y permite añadir y modificarlos. Leyendo el ejemplo os dará una mejor visión de cómo implementar la práctica que se os pide. A continuación se muestran los ficheros (intsequencefile.h)

code format="cpp" using namespace std; namespace storage{ /**\brief This class adds elements to a sequence of integers stored in a file. The class allows to read/write this elements class IntSequenceFile {
 * 1) ifndef _IntSequenceFile_H_
 * 2) define _IntSequenceFile_H_
 * 3) include
 * 4) include
 * The class manipulates a file that has the following structure:
 * nElem elem0 elmen1 ... elmen(n-1), where
 * nElem :inteeger indicating the number of elements
 * elem(i) : i-th element of the sequence
 * elem(i) : i-th element of the sequence

public: /**Emtpy constructor */   IntSequenceFile; /**Parametrized constructor *@param path to the file to open */   IntSequenceFile(string path); /**Destructor closes the file if opened */   ~IntSequenceFile; /**Opens the file if not opened */   bool open(string path); /**Indicates wherther the file is open */   bool isOpenconst; /**Closes the file if opened */   void close; /**adds a new element to the end */   void add(int v); /**Indicates the numer of elements in the file */   int sizeconst; /**Returns the i-th element of the file */   int get(unsigned int pos); private: fstream _file; //io file int _nElments; //total number of elements in the file }; };

code
 * 1) endif

(intsequencefile.cpp)

code format="cpp" namespace storage{ //////////////////////////// // // //////////////////////////// IntSequenceFile::IntSequenceFile {
 * 1) include 
 * 2) include
 * 3) include

} //////////////////////////// // // //////////////////////////// IntSequenceFile::IntSequenceFile(string path) {   open(path); }

//////////////////////////// // // //////////////////////////// IntSequenceFile::~IntSequenceFile { //Closes if is opened if (isOpen) close; }

//////////////////////////// // // //////////////////////////// bool IntSequenceFile::open(string path) { assert(!isOpen); //check is not already opened //open the file. If it is not created, it creates an empty file //If it is created then open it and goes to the end _file.open(path.c_str);

if (!_file.is_open){ //file is not created. Create it   _file.open(path.c_str,ios::out); assert(_file.is_open); _file.close; _file.open(path.c_str,ios::in|ios::out); }

//Determine the size of the file. If size ==0, then, we've //just created the file. //First, make sure that the get pointer is at the end. //The get pointer is the position from which we read data _file.seekg(0,ios::end); //it is like fseek(FILE,0, SEEK_END) //now, read the position of the get pointer int size=_file.tellg; if(size==0){ //first time. Write the _nElments _nElments=0; _file.write((char*)&_nElments,sizeof(int)); //   } else{ //The file containts data //place the get pointer at the begining of the file and read the number of elments _file.seekg(0,ios::beg); //it is like fseek(FILE,0, SEEK_SET) _file.read((char*)&_nElments,sizeof(int)); //Check data is consistent assert(_nElments>=0); }

return true; }

//////////////////////////// // // //////////////////////////// bool IntSequenceFile::isOpenconst { return _file.is_open;

}

//////////////////////////// // // //////////////////////////// void IntSequenceFile::add(int v) { assert(isOpen); //adds an element at the end of the file //move the put pointer to the end of the file. //The put pointer is the position where we write _file.seekp(0,ios::end); //write _file.write((char*)&v,sizeof(int)); //update the _nElments variable _nElments++; }

//////////////////////////// // // //////////////////////////// int IntSequenceFile::sizeconst { return _nElments; }

//////////////////////////// // // //////////////////////////// int IntSequenceFile::get(unsigned int pos) { assert(isOpen); assert(pos<_nElments); //PLace the get pointer in the position indicated. //Calculate first the displacement in bytes int displacement= sizeof(int)+ pos*sizeof(int) ; //header(_nElments) + pos _file.seekg(displacement,ios::beg); //now read int value; _file.read((char*)&value,sizeof(int)); return value; }

//////////////////////////// // // //////////////////////////// void IntSequenceFile::close {   //Before closing, let's update the header of the file //with the current number of elements //First, go to the beggining of the file _file.seekp(0,ios::beg); //write the new value of _nElments _file.write((char*)&_nElments,sizeof(int)); //finally, close _file.close; } } code

(testintseq.h) code format="cpp" using namespace std; using namespace storage; int main {
 * 1) include 
 * 2) include
 * 3) include

storage::IntSequenceFile ISFile; assert(ISFile.open("fileseq.seq")); char c=0; while(c!='e'){ cout<>c; switch(c) {   case '1': {       cout<<"Insert number and press Enter"<>n; ISFile.add(n); }break; case '2': {       cout<<"Indicate position"<<endl; int n;       cin>>n; if (n>=ISFile.size) cout<<"Invalid position"<<endl; else cout<<"The element specified is ="<< ISFile.get(n)<<endl; }break; case '3': {       for (unsigned int i=0;i<ISFile.size;i++) cout<<ISFile.get(i)<<" "; cout<<endl; }break; case '4': {       cout<<"Size="<<ISFile.size<<endl; }break; case 'e':{}break; };

}

} code