Spawn3107
Messages postés84Date d'inscriptionmardi 14 décembre 2004StatutMembreDernière intervention28 mars 2011
-
19 mai 2007 à 12:47
luhtor
Messages postés2023Date d'inscriptionmardi 24 septembre 2002StatutMembreDernière intervention28 juillet 2008
-
22 mai 2007 à 13:31
Bonjour,
J'ai un vecteur qui est utilisée pour mémoriser les divers membres du
personnel, donc des instances de la classe MembrePersonnel (qui hérite de personne) et des classes dérivées. La classe résultant est la
classe Personnel.
Cette classe Personnel a pour rôle de faciliter
l'utilisation d'un fichier supporté par un flux et contenant les renseignements
concernant les divers membres du personnel.Mon vecteur contient des pointeurs d'objets, parce que je veux stocker des MembrePersonnel ou autres personnels.
Mes classes :
class Personne
{ ... }
class MembrePersonnel : public Personne
{ .. }
class PersonnelAdmin : public MembrePersonnel
{ ... }
Insertion dans le vecteur :
...
if(type=="MembrePersonnel")
{
MembrePersonnel* Mp = new MembrePersonnel;
Mp->EncodePers();
Vectpersonne.push_back(Mp);
}
if(type=="PersonnelScientifique")
{
PersonnelScientifique* Psc=new PersonnelScientifique;
Psc->EncodePers();
Vectpersonne.push_back(Psc);
}
...
Maintenant, je veux sauvegarder le vecteur dans un fichier :
class Personnel
{
protected:
string NomFich;
vector Vectpersonne;
...
void Personnel::Save_Fich()
{
ofstream fichier;
fichier.open(nom, ios::out);
for(int i=0;i<(int)Vectpersonne.size();i++)
{
fichier<<*Vectpersonne[i];
}
fichier.close();
}
}
Maintenant après la sauvegarde, mon fichier contient seulement les variables de la classe personne et non ceux de PersonnelAdmin.
Comment est ce que je peux obtenir tout les champs en fonction du personnel que je veux ajouter ?
luhtor
Messages postés2023Date d'inscriptionmardi 24 septembre 2002StatutMembreDernière intervention28 juillet 20086 19 mai 2007 à 15:00
Le paramètre, c'est simplement "le fichier" dans lequel on veut enregistrer tes données. Qd tu ouvres ton fichier:
ofstream fichier;
fichier.open(nom, ios::out);
C'est bien un object ofstream. C'est cet object qu'on passe par référence (!! Tres important) a ta fonction.
Apparemment, tu ouvres ton ficheir en mode texte, perso, je préfère de loin le mode binaire ( fichier.open(nom, std::ios::out | std::ios::binary) pour les sauvegardes de classe, mais bon, avec le mode texte, voila ce que ca donne dans ton cas.
Admettons que la classe Personne possède les champs: std::string nom, std::string prenom, int age, std::string addresse;
La classe MembrePersonnel : std::string motdepasse, int identificateur, float salaire;
(les membres sont pris au hasard ^^)
La fonction dans la classe Personne: virtual void Personne::WriteToFile(std::ofstream & _file) ( note: le mot clef virtual n'apparait pas dans le .cpp)
{
_file << nom << " " << prenom << std::endl;<= le std::endl c'est pour passer a la ligne (notamment)
_file << age << std::endl;
_file << adresse << std::endl;
}
Dans la classe MembrePersonnel :
virtual void MembrePersonnel ::WriteToFile(std::ofstream & _file) {
Personne::WriteToFile(_file); < = appel de la classe Personne
Dans la classe PersonnelAdmin:
virtual void PersonnelAdmin:::WriteToFile(std::ofstream & _file)
{
MembrePersonnel ::WriteToFile(_file); <= appel de la classe MembrePersonnel
etc...
}
J'vais pas fait attention dans mon premier post, ya peut etre quelques erreurs, je pensais que PersonnelAdmin et MembrePersonnel dérivait de Personne, alors que PersonnelAdmin dérive de MembrePersonnel.
Tu as juste a lire dans le meme sens que celui que tu as utilisé pour l'écriture.
L'opérateur >> de ifstream retourne un booléen, donc tu peux vérifier que le fichier est correct de cette facon:
if (!(_inFile >> variable_de_type_qcq_géré_par_istream))
{
ERREUR de conversion.
}
else
tout va bien
luhtor
Messages postés2023Date d'inscriptionmardi 24 septembre 2002StatutMembreDernière intervention28 juillet 20086 22 mai 2007 à 12:07
Ah je sais ... La, c 'est a toi d'essayer de voir ce qu'il se passe.
La fonction BuildObjectFromFile va lire la première ligne pour extraire le type de l'object qu'elle s'apprete a lire. Apres avoir extrait le type, faut que le pointeur du fichier se remette au début de la personne, cad:
Personne * Personne::BuildObjectFromFile(std::ifstream & _file)
{
std::string type;
int position = _file.tellg(); <= on mémorise là ou on se trouve avant d'extraire la ligne.
_file >> type;
_file.seekg(position); <= on se replace au début de la personne pour que la fonction ReadFromFile lise bien la premire ligne.
if (type == std::string("..."))
...
else return NULL;
}
Ca, c'est la solution que je te conseille, l'autre c'est de simplement zapper la lecture du type dans la fonction ReadFromFile, mais c'est vraiment pas terrible, car du coup un object ne peut plus se lire correctement lui meme. Puisqu'il lira jamais la première ligne et donc sera toujours décallé.
Compris ?
Vous n’avez pas trouvé la réponse que vous recherchez ?
luhtor
Messages postés2023Date d'inscriptionmardi 24 septembre 2002StatutMembreDernière intervention28 juillet 20086 22 mai 2007 à 13:08
Non je comprends pas, car peut importe ou se trouve ton object, il est en mémoire. Faut pas se dire que le "vector" c'est un truc magique et particulier. C'est juste un bloc en mémoire dans lequel sont juxtaposés tes objects. Mais post ici ton code entier, ou les fonctions de chargement et autre. Si qd tu fais Vectpersonne[i]->AffichePers() et que ca affiche pas des valeurs corrects, c'est que le fichier n'a pas été lue correctement.
Donc le vecteur n'a aucune influence, surtout que c'est un vecteur de pointeur. (pas le choix ici). Regarde avec ton débuggeur aussi pour lire le contenu de ton vecteur, cad la valeur des pointeurs et tout.
Il faut aussi faire des tests: par exemple, a chaque membre lue du fichier, ajoute un std::cout pour voir la valeur lue:
_inFile >> nom;
std::cout << nom << std::endl;
Et cela partout, il faut déterminer précisément PK et OU il y a un pb. Mais je le répète le plus simple deja, c'est que tu postes ton code ici. :)
luhtor
Messages postés2023Date d'inscriptionmardi 24 septembre 2002StatutMembreDernière intervention28 juillet 20086 19 mai 2007 à 13:54
Déclares une fonction "virtual" dans ta classe Personne:
class Personne
{
public:
virtual void WriteToFile(std::ofstream & _file)
{
... <= la tu enregistres tous les membres de personne
}
}
Tu fais de meme pour chaque classe dérivée:
class MembrePersonnel
{
public: virtual void WriteToFile(std::ofstream & _file) < = comme elle est virtual, c'est cette fonction qui va etre appelé
{
Personne::WriteToFile(_file); <= la on appelle la fonction qui va enregistrer les membres de personnes
... <= puis tu ajoutes les membres de "MembrePersonnel"
}
}
Donc le principe c'est:
Comme la fonction est virtual, c'est la fonction de la classe dérivée qui va etre appellé:
Personne * unePersonne = ...;
unePersonne->WriteToFile(...) <= ce n'est PAS la fonction WriteToFile de Personne qui est appelé, mais celle de la classe dérivée si elle contient une fonction WriteToFile.
Donc au final, qd tu veux enregistrer toutes les peronnes:
"Maintenant, je veux sauvegarder le vecteur dans un fichier :
Spawn3107
Messages postés84Date d'inscriptionmardi 14 décembre 2004StatutMembreDernière intervention28 mars 2011 19 mai 2007 à 14:04
J'avais oublie cette notion de Virtual, merci !
Mais je comprend pas bien ton parametre (std::ofstream & _file) pour enregistrer tous les membres de personne par exemple. Pourrais tu me donner un exemple pour enregistrer un champ ?
luhtor
Messages postés2023Date d'inscriptionmardi 24 septembre 2002StatutMembreDernière intervention28 juillet 20086 21 mai 2007 à 20:28
Ah oui, au temps pour moi, j'ai oublié un détail. Il faut en fait que ce soit la classe Personne qui s'occupe de gérer le chargement car on ne sait pas a priori quel type de personne est sauvegardé. Donc il faut que tu enregistres en meme temps le type de personne. Donc ce que j'ai dis dans le premier post doit etre un peu modifié.
Ce qui se fait en pratique, c'est que l'on construit des "class factory" mais bon dans ton cas, c'est pas nécessaire. Un truc codé brutalement suffit.
Donc apres, je te fais un exemple "sale" dans le sens où on peut faire des trucs mieux structurés, mais c'est le principe :
La fonction suivante retourne un object extrait du fichier. Cet object dépend de ce que l'on va trouver dans le fichier. Donc tu peux par exemple déclarer une fonction statique dans ta classe qui retourne l'objet lue dans le fichier.
static Personne * Personne::BuildObjectFromFile(ifstream & _inFile) {
std::string class_type;
_inFile >> class_type; <= la première chose enregistré est le nom de la classe ou n'importe quelle identificateur pouvant déterminer de facon précise la class concerné par l'object.
if (class_type == std::string("MembrePersonnel"))
{
MembrePersonnel * newPersonnel = new MembrePersonnel();
newPersonnel-> Load (_inFile);
return static_cast(newPersonnel); < = conversion implicite par le compilo, autant le préciser explicitement.
}
else if (class_type = = std::string("PersonnelAdmin"))
{
PersonnelAdmin * newPerson = new PersonnelAdmin();
newPerson-> Load (_inFile);
return static_cast(newPerson);
}
else if (class_type == ...)
else return NULL; <= object non reconnu
}
Donc en fait dans ton fichier, si tu imagines que tu as sauvé un object de type PersonnelAdmin, tu auras
PersonnelAdmin
Membre 1 de personne
...
Membre n de personne
Membre 1 de MembrePersonnel
...
Membre n de MembrePersonnel
Membre 1 de PersonnelAdmin
...
Membre n de PersonnelAdmin
Donc une classe comportera les fonctions:
class PersonnelAdmin : public MembrePersonnel
{
protected: void WriteToFile(std::ofstream & _file); < = Membre qui ne doit pas etre appelé de l'extérieur
{
MembrePersonnel::WriteToFile(_file);
La fonction Save et Load de chaque classe peut etre déclarer virtuelle pure: virtual void Save() = 0; ce qui signifie que chaque classe dérivé a pour OBLIGATION de redéfinir une fonction Save sauf si elle est elle meme une classe abstraite (cad qui possède une fonction virtuelle pure). Si tu tentes de déclarer un object d'une classe abstraite, la compilation échouera.
Donc hésite pas si t'as des questions, ce que j'ai écris, c'est fait en vitesse, ca doit etre truffer de petites erreurs, mais c'est le principe qui est important. Mais encore une fois, c'est juste l'idée de base. Pour des gros projets, on fait des trucs plus élaborés notamment au niveau de la fonction Personne::BuildObjectFromFile comme tu peux t'en douter.
luhtor
Messages postés2023Date d'inscriptionmardi 24 septembre 2002StatutMembreDernière intervention28 juillet 20086 21 mai 2007 à 22:30
Les fonctions WriteToFile et LoadFromFile sont utilisés par toute la hiérarchie dont dérivé une classe. Or il faut que le type soit uniquement marqué une fois et par la classe le plus bas de la hiérarchie de l'object. Donc le caractère "virtual" de Load et Save fait que le programme va aller chercher la fonction de la classe la plus dérivée de l'object et écrire le type. Alors que WriteToFile et LoadFromFile ne font qu'écrire les membres.
En fait, on pourrait faire un peu autrement:
virtual void Personne::WriteToFile(..)
{
file << "Personne" << std::endl;
file << members ...
}
Dans un fichier ou l'on enregistre un object PersonnelAdmin on aurait donc
PersonnelAdmin
MembrePersonnel
Personne
Membre de Personne 1
...
Membre de Personne n
Membre de MembrePersonnel 1
...
Membre de MembrePersonnel n
Membre de PersonnelAdmin 1
...
Membre de PersonnelAdmin n
Le ReadFromFile serait donc rigoureusement l'inverse. Cette solution est pas mal, puisque plus besoin de fonction Load/Save. C'est selon tes gouts finalement.
Spawn3107
Messages postés84Date d'inscriptionmardi 14 décembre 2004StatutMembreDernière intervention28 mars 2011 21 mai 2007 à 22:33
En fait , j'avais déja inseré une variable type dans la classe personne. Ce qui revient a la même chose que tu viens écrire. Je vais plutôt essayer cela au lieu des fonctions Load/Save.
static Personne * BuildObjectFromFile(ifstream & _inFile)
{
string class_type;
_inFile >> class_type; // la première chose enregistré est le nom de la classe
if (class_type == string("MembrePersonnel"))
{
MembrePersonnel * newPersonnel = new MembrePersonnel();
newPersonnel->Load(_inFile);
return static_cast(newPersonnel); // conversion implicite par le compilo, autant le préciser explicitement.
}
else if (class_type == std::string("PersonnelAdmin"))
{
PersonnelAdmin * newPerson = new PersonnelAdmin();
newPerson->Load(_inFile);
return static_cast(newPerson);
}
else if (class_type == string("PersonnelScientifique"))
...
else return NULL; //object non reconnu
}