I am currently working on a school assignment which requires us to read and write two kind of derived objects into a file, but i cant seem to read them in correctly once i have saved them into a .dat file.

here's what i have got so far:


the base class, Human

#ifndef HUMAN_H
#define HUMAN_H

#include <iostream>
#include <string>
using namespace std;

class Human {
	protected:
		string name, id;
	public:
		Human(string n = "", string id = "") {
			name = n;
			this->id = id;
		}
		
		string getName() { return name; }
		
		string getId() { return id; }
		
		void changeName(string n) { name = n; }
		
		void changeId(string id) { this->id = id; }
		
		void showData() {
			cout << "Name: " << name << endl;
			cout << "ID: " << id << endl;
		}
};

#endif

the derived class Advisee

// Forward declaration of class Advisor to selve the circular dependency of classes
// A pointer is used instead of an instance
class Advisor;

#ifndef ADVISEE_H
#define ADVISEE_H

#include "Human.h"
#include <fstream>

class Advisee : public Human {
	private:
		Advisor *myAdvisor;
		
	public:
		Advisee(string n = "", string id = "") : Human(n, id) { myAdvisor = NULL; }
		Advisee(string n, string id, Advisor * myAdv);
		~Advisee() {}
		void load(string);
		void save(string);
		void showAdvisor();
		void setAdvisor(Advisor * myAdv);
		void killAdvisor() { myAdvisor = NULL; }
	
};

void Advisee::load(string path) {
	ifstream loader(path.c_str(), ios::binary);
	loader.read(reinterpret_cast<char*>(this), sizeof(*this));
	loader.close();
}

void Advisee::save(string path) {
	ofstream writer(path.c_str(), ios::binary);
	writer.write(reinterpret_cast<char*>(this), sizeof(*this));
	writer.close();
}

#endif

the other derived class Advisor

#ifndef ADVISOR_H
#define ADVISOR_H

#include "Advisee.h"
#include "Human.h"

class Advisor : public Human {
	private:
		static const int maxAdvisees = 10;
		int numAdvisees;
		Advisee * myAdvisees[maxAdvisees];
	public:
		Advisor(string n = "", string id = "") : Human(n, id) { numAdvisees = 0; }
		~Advisor() {
			/*for (int i = 0; i < numAdvisees; i++) {
				delete myAdvisees[i];
			}*/
		}
		
		int totalAdvisees() { return numAdvisees; }
		
		void load(string path) {
			ifstream loader(path.c_str(), ios::binary);
			loader.read(reinterpret_cast<char*>(this), sizeof(*this));
			loader.close();
		}
		
		void save(string path) {
			ofstream writer(path.c_str(), ios::binary);
			writer.write(reinterpret_cast<char*>(this), sizeof(*this));
			writer.close();
		}
		
		void addAdvisee(Advisee *newAdv) {
			if (!this->adviseeAdded(newAdv)) {
				myAdvisees[numAdvisees] = new Advisee();
				myAdvisees[numAdvisees] = newAdv;
				myAdvisees[numAdvisees]->setAdvisor(this);
				numAdvisees++;
			}
		}
		
		void showAdvisees() {
			if (numAdvisees) {
				cout << "Advisees of " << name << endl;
				cout << "--------------------------------------" << endl;
				for (int i = 0; i < numAdvisees; i++) {
					cout << myAdvisees[i]->getId() << "   " << myAdvisees[i]->getName() << endl;
				}
				cout << endl;
			} else {
				cout << "No advisees set yet." << endl;
			}
		}
		
		void killMe() {
			for (int i = 0; i < numAdvisees; i++) {
				myAdvisees[i]->killAdvisor();
			}
			for (int i = 0; i < numAdvisees; i++) {
				delete myAdvisees[i];
			}
		}
		
		void removeAdvisee(Advisee *adv) {
			bool found = false;
			for (int i = 0, x = 0; i < numAdvisees; i++) {
				if (myAdvisees[i] == adv) {
					delete myAdvisees[i];
					found = true;
					x++;
				}
				myAdvisees[i] = myAdvisees[i+x];
			}
			
			if (found) {
				cout << "Advisee deleted." << endl;
				adv->killAdvisor();
				numAdvisees--;
			} else {
				cout << "Advisee not found." << endl;
			}
		}
		
		// Function to test if the advisee passed is already in my advisee list
		bool adviseeAdded(Advisee * adv) {
			bool found = false;
			for (int i = 0; i < numAdvisees; i++) {
				if (myAdvisees[i] == adv) {
					found = true;
				}
			}
			
			return found;
		}

};

#endif

Advisee::Advisee(string n, string id, Advisor * myAdv) {
	name = n;
	this->id = id;
	if (myAdv) {
		myAdvisor = myAdv;
		if (!myAdvisor->adviseeAdded(this)) {
			myAdvisor->addAdvisee(this);
		}
	}
}

void Advisee::setAdvisor(Advisor * myAdv) {
	if (myAdv) {
		myAdvisor = myAdv;
		if (myAdvisor->adviseeAdded(this)) {
			myAdvisor->addAdvisee(this);
		}
	}
}

void Advisee::showAdvisor() {
	if (myAdvisor) {
		cout << "Advisor of " << this->name << endl;
		cout << "--------------------------------------" << endl;
		myAdvisor->showData();
	} else {
		cout << "No advisor set yet." << endl;
	}
}

each Advisor/Advisee has methods load() and save() respectively, which load or saves the object into a file as passed in by the parameter of the methods.

here's how i saved them into a file ( inside main() ):

string path1 = "write1.dat";
string path2 = "write2.dat";

Advisor david("David", "24234");
Advisor ken("Ken", "123213");
Advisee peter("Peter", "123023");
	
david.save(path1);
ken.save(path1);
peter.save(path2);

and here's how i load them:

Advisor advisors[5];
Advisee advisees[5];
	
advisors[0].load(path1);
advisors[1].load(path1);
advisees[0].load(path2);
	
advisees[0].setAdvisor(&advisors[0]);
	
advisors[0].showAdvisees();
cout << endl;
advisors[1].showAdvisees();
cout << endl;
advisees[0].showAdvisor();

when compiled, the program crashes and shows to the line "Advisor of"


can anyone teach me how to solve this? is the way im doing it correct or should there be another way? is it ok to write objects containing pointers to a file?


thanks in advance :D

>>writer.write(reinterpret_cast<char*>(this)

The class can not be written that simply because it contains std::string and pointers. That makes the serialization algorithms much more complex and makes the file a variable length records, almost like any ordinary text file.

If you have to use binary format, I would suggest you use character arrays for Human class name and id to simplify the file and make it use fixed-length records.

class Human {
	protected:
		char name[80], id[80];
	public:
		Human(string n = "", string id = "") {
			changeName(n);
			changeID(id);
		}
		
		string getName() { return name; }
		
		string getId() { return id; }
		
		void changeName(string n) { strcpy(name,n.c_str()); }
		
		void changeId(string id) { strcpy(this->id,id.c_str()); }
		
		void showData() {
			cout << "Name: " << name << endl;
			cout << "ID: " << id << endl;
		}
};

All it needs to write out is the base class since the derived classes have no objects of their own that need to be serialized. writer.write(reinterpret_cast<char*>(&*this->myAdvisor)


All it needs to write out is the base class since the derived classes have no objects of their own that need to be serialized. writer.write(reinterpret_cast<char*>(&*this->myAdvisor)

i don't quite understand this part...could you explain in more detail about serialization?

That line could have been coded more simply like this: writer.write(reinterpret_cast<char*>(this->myAdvisor) If you just write out the this pointer all that will get written is the value of the myAdvisor pointer, not the object to which it points.

Depending on how the rest of your program is written you may not have to write out myAdvisor at all.

When you read myAdvisor back you will first have to allocate memory for myAdvisor and then read into that pointer. I don't know how you set the value of that pointer initially, but you will have to do it again before reading back from the data file.

i am now working on another way to load in the data by tokenizing the data of each object (e.g. name, id, etc) into/from a plain text file...would that be a better way than to write in the whole object into the file?

thanks alot for your explaining :P

i am now working on another way to load in the data by tokenizing the data of each object (e.g. name, id, etc) into/from a plain text file...would that be a better way than to write in the whole object into the file?

thanks alot for your explaining :P

yes, I agree that would be an excellent idea. Binary files just don't work very well with c++ classes.

alright...thanks very much with ur help! i solved the question now by tokenizing the data.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.