How to get pointers initialized back from the .txt file - c++

I'm using pointers of class objects.Then i write in reservation file data.And if i read from .txt file i cannot read poiners.What's the good way to initialize pointers
Should i use additional variables(userLogin,roomId) and then match additional variables with variables of objects and get address from them?
class Guest
{
public:
string login;
string password;
string name;
};
class Room
{
public:
int id;
string type;
};
class Reservation
{
public:
string checkOut;
string checkIn;
Room* room;
Guest* user;
string userLOgin;
int roomId;
};
class Manage
{
void MakeResrvation(Guest* user,Room* room,bool appendToFile)
{
string checkIn,checkOut;
ofstream writer(ReservationPath, appendToFile ? ios::app : ios::out);
if (writer.is_open()) {
writer<<room->id<<Delimiter<<user->login<<Delimiter<<checkIn<<Delimiter<<checkOut<<Delimiter;
writer<<endl;
}
}
vector<Reservation*> LoadFromFile() {
ifstream reader(ReservationPath);
vector<Reservation*> reservations;
if (reader.is_open()) {
string line;
while (getline(reader, line))
{
vector<string> items = Split(line, Delimiter);
Reservation* reservation = new Reservation;
reservation->roomId=stoi(items.at(0));
reservation->userLOgin=items.at(1);
reservation->checkIn=items.at(2);
reservation->checkOut=items.at(3);
reservations.push_back(reservation);
}
reader.close();
}
return reservations;
};
I expect to get pointer in the most optimized way.

Why it doesn't make sense to read and write pointers to files ?
It makes no sense to read a pointer from a file. A pointer is an address within the memory space. When you create your reservation, the pointer points to an address where a valid object (e.g. a Room or a Guest) is supposed to be stored:
void test()
{
Room r{12,"big room"};
Guest g{"XYZ","...","Mr XY, Z"};
manager.MakeReservation(&g, &r, true);
} // r and g are destroyed here
When you later read the pointer, you are not sure that a valid object is still at the place where it was before. In the example above, the pointers pointed to local objects, and these have been destroyed in the meantime.
Worse, if you would read the file from another process (e.g., when running your program again the next day) the address space might be completely different, and the object you where hoping pointing to might never have existed there.
How to solve your problem ?
In a Reservation you have the key information to uniquely identify the objects you are looking for: roomId should uniquely identify a Room, and userLOgin should uniquely identify a Guest (at least I'll assume so hereafter), and regardless of the place where they are stored.
So what you need to do is to store somewhere repositories of rooms and guests: when you read your reservation, you need to search in the repository the corresponding objects, and set the pointers accordingly. OF course, you need to adapt your code: either pass the repositories as argument to the function that needs them, or store them as members of Manage.
Another alternative, could be when you save the object, also save the data of the pointed objects. When you then read the data, you'd have to create not only the reservation, but also the "embedded" objects, and initialise the pointers accordingly. This is a little bit more complex, but you can google for serialization to know more about this process.
Other improvements
Attention when you write data, in case the strings you are writing contain the delimiter: this could lead to nasty bugs when reading the file.
I'd suggest also to use in reservation either a roomId and always find the room in the repository when it's needed, or a pointer to Room, knowing that the id can be found there. Having redundant information in Reservation could lead weird things to happen such as pointing to an object with a different id than expected.
Now if you're managing your repositories in form of vector<Reservation*>, vector<Room*>, and vector<Guest*>, you may be interested in using vector<shared_ptr<Reservation>>, vector<shared_ptr<Room>>, and vector<shared_ptr<Guest>>, since shared_ptr facilitates memory management.
Finally, for your repositories, you may consider using std::map instead of std::vector, where the key to the map would be the unique identifier.

Related

C++ object reference returned by another object lifetime/scope

I'm currently playing with an idea of recursive reader/unpacker of various "mountable" fileformats. It should for example allow to read zipped file stored in vmdk file stored in NTFS directory structure.
Currently I have only this header, where I use CZipMounter.mount to load and pre-parse zip file in memory - this fills private list of file info in vecEntries (and a set of not-yet mentioned zip parsing variables). Then the caller calls CZipMounter.openDir, which returns the iterator object reference (~equivalent of POSIX DIR). CZipIterator must have access to CZipMounter (at least I think it has to, there is a handle open to underlying file, handle to un-zip library etc). Then the caller can call CZipIterator.readDir in while loop. When CZipIterator instance is destroyed, it's an equivalent of POSIX CloseDir.
And now I'm lost regarding to scoping/lifetime problems of CZipMounter. Because if it gets destroyed prematurely, all CZipIterator instances would have pointers (or references, it does not matter IMO) pointing to already destroyed object.
Basically I may have two completely different questions:
Is there any better way how to implement this kind of recursion here? I somehow wouldn't think that's it's a good idea to copy needed data from Mounter to Iterator.
If my example does make sense, how to prevent CZipMounter from premature destruction (ie. I want CZipMounter to be destroyed after all `CZipIterator~ instances.
The whole thing is planned to be slightly more complicated as there would be more CXXXMounter classes and one virtual CMotherOfAllMounters class.
enum commonDirTypes
{
myFILE = 0,
myDIR = 1
};
struct commonDirEntry
{
std::string entryName;
enum commonDirTypes entryType;
};
struct zipDirEntry : commonDirEntry
{
std::vector<zipDirEntry> vecEntries;
};
class CZipMounter;
class CZipIterator
{
public:
//readDir must return type of object and whether or not we can continue reading
struct zipDirEntry* readDir();
private:
CZipMounter* pMount;
};
class CZipMounter
{
public:
bool mount(const char*);
CZipIterator& openDir(const char*);
private:
std::vector<zipDirEntry> vecEntries;
};

Populating a vector of objects within a class

and I've hit my first wall on my coding project/assignment.
I'm to implement functionality into code that's been done to some stage, and I cannot alter the given code so I have to work around the given structure.
The code, in a nutshell, reads family relations from a text file and populates database with the family relation data and later on allows user to print out information he wants to access.
What I'm having trouble with is understanding how I can and how I have to utilize a struct given to me in the assignment. The struct is
struct Person
{
std::string id_ = NO_ID;
int height_ = NO_HEIGHT;
std::vector<Person*> parents_{nullptr, nullptr};
std::vector<Person*> children_;
};
and I'm using it at least in the initialization phase of the data structure.
I start by calling the process in main.cpp with
database->addRelation(it->child_, it->parents_, std::cout);
In the naming/height adding phase I'd simply do it with
MyPerson.id_ = id;
MyPerson.height_ = height;
where MyPerson is defined by Person MyPerson;
but as far as I can tell, I have to somehow access the object pointers to be able to populate the vectors for when I want to add children/parents to the person.
The class functions that are called when initializing person's name, height and family relations are these two:
void Familytree::addNewPerson(const string &id, const int &height, ostream &output)
{
MyPerson.id_ = id;
MyPerson.height_ = height;
}
void Familytree::addRelation(const string &child,
const std::vector<string>
&parents, ostream &output)
{
}
The addRelation fuction is what I'm having a hard time getting to work. Simply appending the strings to it won't work since it expects Person* -objects, which are, as far as I can tell, just pointers to the other Persons, but I'm not sure how I can access them.
Also, let me know if anything here is excessive or if I'm missing anything crucial, I'll edit it to the best of my ability
Editing with additional information:
The only things I've added myself that can be seen here is
Person MyPerson;
and the contents of the class function addNewPerson. The other snippets I can not change in any shape or form.
Edit#2
Current progress, debatable whether I'm closer or further from the goal
My persons map is using Personmap = std::map<std::string, Person >;
and I'm using it in addNewPerson with
persons_[id] = id;
persons_[id] = height;
, but I'm still randomly trying different things to try and make it work for the next phase where I need to somehow add the objects to the vectors.
The biggest problem I have is the fact that I do now know how to play around the difference of *Person and Person

How to edit and overwrite specific location in file in C++

I am making an airline reservation software and I don't know much about the Visual C++. I am using the simple compiler "TCWIN45". In my program I wish to use file handling and I am succeed to save all the inputs in text file. i need to add search option and modification option. if user choose search and Enter the name then how can I access specific number of lines. because my files contains the record of multiple passengers but I want to show the only one's data. same is the case for modification. I want to access specific location or line and also to overwrite it. please suggest me the most simplest way.
This is my code to save all the record in one text file:
ofstream thefile("ID.txt" , ios::app);
thefile<<"\n\nDay : "<<p1[i].day<<"\nFlight Date : "<<p1[i].date<<"\nFlight Type : "<<p1[i].type<<"\nReturn Date : "<<p1[i].rdate<<"\nDeparture Place : "<<p1[i].from<<"\nDestination : "<<p1[i].to<<"\nClass Type : "<<p1[i].clas<<"\nTime of Flight : "<<p1[i].time<<"\nTitle : "<<p1[i].prefix<<"\nFirst Name : "<<p1[i].fname<<"\nLast Name : "<<p1[i].lname<<"\nDate of Birth : "<<p1[i].dob<<"\nPassport Number : "<<p1[i].ppt_no<<"\nExpiry Date : "<<p1[i].edate<<"\n Contact Number : "<<p1[i].cont<<"\nMeal Type : "<<p1[i].meal<<"\n\n------------------------------";
Ali, this can be done in a flat file if you really want to not use a database. The trick, is to either: 1.) have all records the same size OR 2.) have a "record header" that provides "enough" information to be able to unserialize the record from the hard disk. If you store different kinds of records, "enough" information could be size of the record or a record type for RTTI purposes. I find it useful to also store an ID for each record so that I can store an index table for record offsets.
If you records have varying sizes, then your record's serialization functions have to be able to handle this. In fact, it is trivial to do this.
The index table is a table of file offsets.
typedef uint16_t record_id;
typedef long offset_t;
offset_t indices[ MAX_RECORDS ];
typedef struct _record {
uint16_t type;
uint16_t id;
offset_t next;
offset_t prev;
} record;
typedef struct _header {
uint32_t count;
offset_t first_record;
offset_t deleted_record;
} header;
So to find the position of the record, you find the offset into the file, which is indices[ record_id ]. Adding a record is like adding a node to a linked list, but the nodes are in the file.
Deleting records is a little tricky. You have to use "lazy delete" to delete records and later these deleted records get reused. You can even write a shrink function that will remove all deleted records from the file to free up unused space.
The limitations of this technique is that you can only search by record id. If you have other information, you will need to generate additional data structures to support this.
I have code available that does this in C if you would like a working example. However, doing this from scratch is feasible but NOT WORTH THE EFFORT. Just use a database like Sqlite or MySQL--it will save time!
Example Code
flat-db.c
flat-db.h
test-flat-db.c
From your comments to other answers, it does not seem like the best way for you to do this is to store the data in a text file at all. You will probably want a Reservation class that contains all of the information for the reservation. Then, use some kind of Collection to store all of the reservations. Writing to a text file just adds a huge amount of unnecessary difficulty.
Something like this:
class Reservation
{
std::string day;
std::string date;
std::string flightType;
std::string meal;
/* ... */
};
It would be even better if you made separate classes for each one of the class members (like a Day class, a FlightType class, etc.).
You would then use some kind of Map to access a particular reservation and change its members.
You'll probably want to define a reservation class that represents a single reservation, and a data class, that holds all your data, as a vector of reservations. The data class will want to have a member function that takes a std::ostream by reference, and saves the reservations to a text file, (easiest is one variable per line). It will also want a member function that takes a std::istream by reference and reads in the data from the text file.
The main part of your program would (I'm making TONS of assumptions here) load the file into the data class with the std::istream member function, and asks the user for some sort of ID. You then call a member function of data that checks all of the elements in datas vector until it finds the matching ID (by reference), and lets the user change some members. Then it calls the std::ostream member function again to save the changes.
Streams are handled like this. In this sample, I do not use the data class or a vector, since this question looks suspiciously like homework, but this shows the tricky parts of file handling.
#include <string>
#include <iostream>
#include <fstream>
class Reservation {
std::string ID;
std::string date;
public:
//default constructor
Reservation()
{}
//helpful constructor
Reservation(std::string _id, std::string _date)
:ID(_id), date(_date)
{}
//copy constructor
Reservation(const Reservation& b)
:ID(b.ID), date(b.date)
{}
//move constructor
Reservation(Reservation&& b)
:ID(std::move(b.ID)), date(std::move(b.date))
{}
//destructor
~Reservation()
{}
//assignment operator
Reservation& operator=(const Reservation& b)
{
ID = b.ID;
date = b.date;
return *this;
}
//move operator
Reservation& operator=(Reservation&& b)
{
ID = std::move(b.ID);
date = std::move(b.date);
return *this;
}
//save
std::ostream& save(std::ostream& file) {
file << ID << '\n';
file << date << '\n';
return file; //be in the habit of returning file by reference
}
//load
std::istream& load(std::istream& file) {
std::getline(file, ID);
std::getline(file, date);
return file; //be in the habit of returning file by reference
}
};
int main() {
Reservation reserve; //create a Reservation to play with
{ //load the reservation from loadfile
std::ifstream loadfile("myfile.txt");
reserve.load(loadfile);
}
//display the reservation
reserve.save(cout);
{ //save the reservation to a different file
std::ofstream savefile("myfile2.txt");
reserve.save(savefile);
}
return 0;
}

Does this cause a memory leak / How should I structure the code

I am slightly concerned that this code may have a memory leak. I would like to know if in fact there is a leak, and also what is the correct way to go about this problem.
Description: I have a base class Pet with derived classes Cat, Dog, and Bird. I am parsing lines from a file, and depending on certain contents in that line, I need to create an instance of the derived classes, and then parse part of the line again in a specific way.
Here is a sample file:
Dog Spot Brown,Labrador,5
Cat Felix Black,7
Bird Polly Green,Parrot,12,Crackers
And some code:
class Pet
{
protected:
string _type;
string _name;
string _desc;
public:
Pet();
bool ParseLine(std::string line);
string Type() { return _type; }
string Name() { return _name; }
string Desc() { return _desc; }
};
class Dog : public Pet
{
private:
string _color;
string _type;
int _age;
public:
Dog(string type, string name, string desc);
bool ParseDesc(string desc);
};
Main Code:
ifstream infile(filename, ifstream::in);
string line;
while(getline(infile, line))
{
Pet* pet = new Pet(); // "new" called once
if(pet->ParseLine(line))
{
if(pet->Type() == "Dog")
{
pet = new Dog(pet->Type(), pet->Name(), pet->Desc()); // "new" called again
pet->ParseDesc(pet->Desc());
}
else if(pet->Type() == "Cat")
{
// ...
}
}
}
Basically what happens is this:
I take a line from the file and parse it into three fields (this is what ParseLine() does:
Type (Dog, Cat, Bird, etc.)
Name (Spot, Felix, Polly, etc.)
Description ("Brown,Labrador,5", "Black,7", "Green,Parrot,12,Crackers", etc)
I then assign these three fields to my Pet* variable.
Then, according to the value in Pet*->Type(), I parse Pet*->Desc() to get the additional information for that particular type of animal.
I am worried about calling operator "new" twice. I think there is probably a better way to format the code that could avoid this altogether.
I would really like to keep my getline() routine. I do NOT want to peek at the line to determine the type, and then decide how to create my instance.
Also, I have to reassign my variables _type, _name, and _desc, when I recreate the Dog(), and I'd rather not have to do that.
Thanks.
--
Specifically, how do I avoid this:
Pet* pet = new Pet();
pet->ParseLine(line);
string type = pet->Type();
string name = pet->Name();
string desc = pet->Desc();
delete pet;
if(type == "Dog")
{
Pet* dog = new Dog(type, name, desc);
dog->ParseDesc(desc);
}
Yes, this causes a memory leak, since you allocate a new Pet() which is never deleted and the pointer to it is overridden with either a new Dog() or something else.
I would suggest you create a so-called factory function, which reads a line from the file, and creates the type of Pet the line states.
Yes, there is a leak. If you do not want to release pointers manually, use some smart pointer library, e.g Boost shared_ptr.
As for how the leaks happen in your code: When you call new again and assign into the same pointer, you have a leak. When you leave the scope containing the pointer and do not release the memory, you have another leak.
However, your whole design smells to me and does not look right. You should not create a Pet only to realize later it is a Dog or a Cat and recreate it. You should have some "creator"(factory) object instead, which would handle this, and ParseLine would be a member of this factory object, not of the Dog or Pet.
In addition to other comments here, you need a virtual destructor in the base class to ensure derived classes are cleaned up properly when deleting via a Pet*.
virtual ~Pet() {}
See here for rationale.
Here is what I did in a similar situation. Note this is not the only or best way to do this.
I was reading random length records from a file where each record had a common fixed-sized header and the type of record/object would be determined from the header information. I created a factory-like class which read in the header, search a container for a matching entry and use a factory class/function to create the desired object. The header data was passed to the new object's constructor for copying.
In simple pseudo-code it was something like:
struct header_t
{
int Type
int Size
....
}
map<Type, CreateFunction> RecordFactory =
{
{ TYPE1, CreateRecord1 },
{ TYPEX, CreateRecordX },
....
}
header_t NewHeader = ReadHeader()
RecordCreate = RecordFactory.Find(NewHeader.Type)
if (RecordCreate is valid) NewRecord = RecordCreate(NewHeader)
If you have a small, fixed number of classes then you don't necessarily need a complex factory class and a short if/switch list would work just as well. If you are not familiar with the Factory Pattern then read up on it as it is useful in a variety of situations.
I agree with comment about boost::shared_ptr, learn how to use it - it will make your life better
Looking at your '2 news' solution I now see what you are asking. THe ParseLine function should not be an instance method of Pet (A pet doesnt parse a line, a pet barks, eats, walks etc)
YOu should have a PetFactory class, that has a static method ParseLine and that returns a polymorphic Pet* (or better a PetPtr)
the rule of thumb is always to delete after you call new.
if(pet)
delete pet;
For detecting memory leaks:-
There are tools available like purify and valgrind
When you do not have any access to these tools, there is a simple method
Put the section of code you are doubtful of having a memory leak in infinite loop. Let the program run in that loop (for may be an overnight or so) If there is a memory leak, memory will get finished and new allocations will cease to happen. If you find it running smooth, enjoy there is not any memory leak.
Hope this helps.

Return a vector knowing it will always contain a single record in order to be consistent with the rest of the interface?

I'm writing a little address book application and have a design dilemna regarding the interface for the data source / backend.
I have the following abstract base class for data source classes:
class DataSource
{
private:
public:
virtual std::auto_ptr<Contact> getContact(int id) = 0;
virtual ContactRecordSet getAllContacts() = 0;
virtual bool addContact(const Contact& c) = 0;
virtual bool updateContact(int id, const Contact& c) = 0;
virtual bool deleteContact(int id)=0;
virtual ~DataSource() {};
};
Below is my record struct and tmy record set is a typedef to an STL vector of these objects.
class Contact
{
public:
std::string firstName;
std::string lastName;
std::string phoneNumber;
std::string address;
std::string email;
};
typedef std::vector<Contact> ContactRecordSet;
My question involves the return value type used for the DataSource::getContact() method and the DataSource::getAllContacts() method and the search method to be added soon that will get records based on a query.
DataSource::getContact() will return zero or 1 records since I'm looking up by unique id.
DataSource::getAllContacts() will return zero or more contacts.
The upcoming search method will return zero or more contacts.
As I have it now the getContact() method is returning an auto_ptr to a Contact because it seemed wasteful to return a ContactRecordSet if I know for sure they'll never be more than one and it allows me to return NULL if there is no record that has that id.
Would it be better to for getContact() to return a ContactRecordSet also, simply for the interface to remain consistent?
Part of me chafes at the idea of returning a data structure like that for a single object, but on the other hand it is more consistent and the semantics for checking if a value was found for that id seem more in line with the overall abstraction of the design (check length of returned recordset vs. check for a NULL auto_ptr).
What do you all think?
(Note - I'm aware I'm probably be over-engineering for a simple address book application but I want it to be easy to swap out different back ends (flat file, SQL, etc...) provided they implement the common interface. The goal is to practice good modular design & seperation of concerns.)
UPDATE
I suppose I could look at from the opposite perspective and make the multiple record methods return auto_ptrs to ContactRecordSet objectcs. That way a)it's consistent in that you're always getting a pointer to an object and b) you don't have the overhead of returning a std::vector if the record set is empty, simply return a NULL pointer.
I always follow the design principle of return the least complex thing that defines your object. Vectors are meant to hold lists of things not single item and while it might make the semantics symmetric it will undoubtedly be nonintuitive to another developer.
What's consistent about returning a plural type for a non-plural function?
I think you need to be working with a different definition of "consistency".