Dans beaucoup de programmes, il peut être utile lors de l'exécution d'enregistrer un objet.
Pour faire ceci, au lieu de sauvegarder chacune des variables, on utilise l'une des méthodes de
fstream : write
.
Son prototype est
ostream& write ( const char* s , streamsize n );
Le premier argument est une chaine de caractère (qui sera l'objet), et le deuxième, la taille de l'objet.
Pour la lecture on utilise la méthode
read : istream& read ( char* s, streamsize n );
Pour enregistrer un objet, il suffit alors de faire :
#include <fstream> ofstream Fichier (chemin, ios ::binary); /* (le paramètre binary, indispensable ici, peut-être refusé par certain compilateur, toutefois il fonctionne parfaitement sur Code ::Blocks).*/ if ( !Fichier) { return ; } write ((char*)&objet, sizeof(objet)) ; // ... read((char*) &objet, sizeof(objet));
Ici, on va utiliser cette méthode sur une classe Personnage. Les personnages auront un nom (std ::string wNom), un age (unsigned short wAge) et des points de vie (unsigned short wVie).
Il y aura 3 fichiers :
-main.cpp
-Personnage.hpp
-Personnage.cpp
On va commencer par le main.cpp :
#include "Personnage.hpp" int main() { // CREATION D'UN OBJET PERSONNAGE : Personnage *John = new Personnage; // LECTURE DE L'OBJET std::cout << "Creation d'un personnage : \n"; std::cout << *John; // MODIFICATION DE L'OBJET std::string nom = "John Bazq"; John->SetNom(nom); John->SetAge (35); John->SetVie (70); // LECTURE DE L'OBJET std::cout << "Personnage defini : \n"; std::cout << *John; // ENREGISTREMENT DE L'OBJET Enregistrer ("JOHN"); // SUPRESSION DE L'OBJET delete John; // CREATION DE L'OBJET std::cout << "Creation d'un personnage : \n"; John = new Personnage; // LECTURE DE L'OBJET std::cout << *John; // MODIFICATION A PARTIR DU FICHIER Lire ("JOHN"); // LECTURE DE L'OBJET std::cout << "Personnage du fichier : \n"; std::cout << *John; system ("PAUSE"); return EXIT_SUCCESS; }
Ensuite on va créer Personnage.hpp, et lui donner toutes les méthodes nécessaires à l'exécution du main.
Il va donc lui falloir les méthodes (publiques) :
void SetNom(std::string &); std::string GetNom() const; void SetAge (unsigned short); unsigned short GetAge () const; void SetVie (unsigned short); unsigned short GetVie () const; void Enregistrer (std::string) const; void Lire (std::string); friend std::ostream& operator<< (std::ostream&, Personnage&);
Plus les constructeurs :
Personnage(); ~Personnage() ;
Et les variables membres (privées) énoncées précédemment :
std::string wNom; unsigned short wAge; unsigned short wVie;
Il faut aussi penser à protéger le header des inclusions multiples.
On obtient donc :
#ifndef DEF_PERSONNAGE_HPP #define DEF_PERSONNAGE_HPP #include #include <fstream> class Personnage { public: Personnage(); ~Personnage(); void SetNom(std::string &); std::string GetNom() const; void SetAge (unsigned short); unsigned short GetAge () const; void SetVie (unsigned short); unsigned short GetVie () const; void Enregistrer (std::string) const; void Lire (std::string); friend std::ostream& operator<< (std::ostream&, Personnage&); private: std::string wNom; unsigned short wAge; unsigned short wVie; }; #endif
Pour finir, on va implémenter cette classe dans Personnage.cpp.
On commence par les méthodes basiques de modifications et d'accessions, en vérifiant les valeurs entrantes et les exceptions.
Le constructeur et le destructeur :
Personnage::Personnage() { wNom = "Aucun_nom"; wAge = 20; wVie = 100; } Personnage::~Personnage() { }
La méthode de modification du nom :
void Personnage::SetNom(std::string &Nom) { try { if (Nom == "") throw "La chaine de caractere est vide !"; if (Nom.size() > 40) throw "Nom trop long !"; wNom = Nom; } catch (const char *exception) { std::cerr << "\n*** " << exception << " ***\n"; } catch (...) { std::cerr << "\n*** Une erreur s'est produite ! ***\n"; } }
La méthode d'accès du nom :
std::string Personnage::GetNom() const { return wNom; }
La méthode de modification de l'âge :
void Personnage::SetAge (unsigned short Age) { try { if (Age > 137) throw "L'age est trop grand !"; wAge = Age; } catch (const char *exception) { std::cerr << "\n*** " << exception << " ***\n"; } catch (...) { std::cerr << "\n*** Une erreur s'est produite ! ***\n"; } }
La méthode d'accès de l'âge :
unsigned short Personnage::GetAge () const { return wAge; }
La méthode de modification de la vie :
void Personnage::SetVie (unsigned short Vie) { try { if (Vie > 100) throw "Il y a trop de points de vie !"; wVie = Vie; } catch (const char *exception) { std::cerr << "\n*** " << exception << " ***\n"; } catch (...) { std::cerr << "\n*** Une erreur s'est produite ! ***\n"; } }
La méthode d'accès de la vie :
unsigned short Personnage::GetVie () const { return wVie; }
Sur le principe : Fichier.write ((char*)&objet, sizeof(objet)) ;
La méthode se trouvant dans la classe devient :
Fichier.write ((char*)this, sizeof(*this)) ;
On peut donc implémenter notre méthode Enregistrer sous cette forme :
void Personnage::Enregistrer (std::string Chemin) const { try { if (Chemin == "") throw "Chemin vide !"; if (Chemin.size() > 300) throw "Chemin trop long"; std::ofstream fichier_sortant (Chemin.c_str(), std::ios::binary); if (!fichier_sortant) { std::cerr << "\a\n\nImpossible de creer le fichier de sauvegarde\n\n"; return; } fichier_sortant.write ((char*) this, sizeof (*this)); fichier_sortant.close(); } catch (const char *exception) { std::cerr << "\n*** " << exception << " ***\n"; } catch (...) { std::cerr << "\n*** Une erreur s'est produite ! ***\n"; } }
Sur le même principe : Fichier.read((char*) &objet, sizeof(objet));
devient : Fichier.read((char*) this, sizeof(*this));
La méthode finale :
void Personnage::Lire (std::string Chemin) { try { if (Chemin == "") throw "Chemin vide !"; if (Chemin.size() > 300) throw "Chemin trop long"; std::ifstream fichier_entrant(Chemin.c_str(), std::ios::binary); if (!fichier_entrant) { std::cerr << "\a\n\nImpossible de lire le fichier de sauvegarde\n\n"; return; } fichier_entrant.read((char*) this, sizeof(*this)); fichier_entrant.close(); } catch (const char *exception) { std::cerr << "\n*** " << exception << " ***\n"; } catch (...) { std::cerr << "\n*** Une erreur s'est produite ! ***\n"; } }
Sans oublier d'implémenter la surcharge de l'opérateur << :
std::ostream& operator<< (std::ostream &Flux, Personnage &rhs) { try { Flux << "\nNom du personnage : " << rhs.GetNom() << "\nAge du personnage : " << rhs.GetAge() << "\nVie restante du personnage : " << rhs.GetVie() << "\n\n"; return Flux; } catch (...) { std::cerr << "\n*** Une erreur s'est produite ! ***\n"; } }
Le code source final :
#include "Personnage.hpp" Personnage::Personnage() { wNom = "Aucun_nom"; wAge = 20; wVie = 100; } Personnage::~Personnage() { } void Personnage::SetNom(std::string &Nom) { try { if (Nom == "") throw "La chaine de caractere est vide !"; if (Nom.size() > 40) throw "Nom trop long !"; wNom = Nom; } catch (const char *exception) { std::cerr << "\n*** " << exception << " ***\n"; } catch (...) { std::cerr << "\n*** Une erreur s'est produite ! ***\n"; } } std::string Personnage::GetNom() const { return wNom; } void Personnage::SetAge (unsigned short Age) { try { if (Age > 137) throw "L'age est trop grand !"; wAge = Age; } catch (const char *exception) { std::cerr << "\n*** " << exception << " ***\n"; } catch (...) { std::cerr << "\n*** Une erreur s'est produite ! ***\n"; } } unsigned short Personnage::GetAge () const { return wAge; } void Personnage::SetVie (unsigned short Vie) { try { if (Vie > 100) throw "Il y a trop de points de vie !"; wVie = Vie; } catch (const char *exception) { std::cerr << "\n*** " << exception << " ***\n"; } catch (...) { std::cerr << "\n*** Une erreur s'est produite ! ***\n"; } } unsigned short Personnage::GetVie () const { return wVie; } void Personnage::Enregistrer (std::string Chemin) const { try { if (Chemin == "") throw "Chemin vide !"; if (Chemin.size() > 300) throw "Chemin trop long"; std::ofstream fichier_sortant (Chemin.c_str(), std::ios::binary); if (!fichier_sortant) { std::cerr << "\a\n\nImpossible de creer le fichier de sauvegarde\n\n"; return; } fichier_sortant.write ((char*) this, sizeof (*this)); fichier_sortant.close(); } catch (const char *exception) { std::cerr << "\n*** " << exception << " ***\n"; } catch (...) { std::cerr << "\n*** Une erreur s'est produite ! ***\n"; } } void Personnage::Lire (std::string Chemin) { try { if (Chemin == "") throw "Chemin vide !"; if (Chemin.size() > 300) throw "Chemin trop long"; std::ifstream fichier_entrant(Chemin.c_str(), std::ios::binary); if (!fichier_entrant) { std::cerr << "\a\n\nImpossible de lire le fichier de sauvegarde\n\n"; return; } fichier_entrant.read((char*) this, sizeof(*this)); fichier_entrant.close(); } catch (const char *exception) { std::cerr << "\n*** " << exception << " ***\n"; } catch (...) { std::cerr << "\n*** Une erreur s'est produite ! ***\n"; } } std::ostream& operator<< (std::ostream &Flux, Personnage &rhs) { try { Flux << "\nNom du personnage : " << rhs.GetNom() << "\nAge du personnage : " << rhs.GetAge() << "\nVie restante du personnage : " << rhs.GetVie() << "\n\n"; return Flux; } catch (...) { std::cerr << "\n*** Une erreur s'est produite ! ***\n"; } }
Et voilà ce qu'affiche la console lors de l'exécution :