0

I have a class AccountManagement in AccountManagement.cpp. I have another class called Account in Account.cpp. I have a template that Orders the given data inside the list using OrdereList class, which also has it's own iterator. The AccountManagement class outputs the Accounts list in a binary file as shown below:

void AccountManagement::saveData(const char * file) //saves data in the specified binary file
{
    ofstream out(file, ios::out | ios::binary);
    if(!out)
    {
        cerr<<"Problem opening output file!"<<endl;
    }   
    OrderedList<Account>::iterator it = this->account_manager.begin();
    for(int i = 0; i < this->total_accounts; i++)
    {
        Account temp = *it;
        out.write((char*)&temp, sizeof(Account));
        it++;
    }
    out.close();
}

I have defined a following function inside AccountManagement class that reads all the data from binary file and outputs it. This function works perfectly fine. It is shown here:

void AccountManagement::output()
{
    ifstream in("accounts.dat", ios::in | ios::binary);
    if(!in)
    {
        cerr<<"File doesn't exist!"<<endl;
        exit(1);
    }
    Account acc;
    while(in.read((char*)&acc, sizeof(Account)))
    {
        cout<<acc;
    }
    in.close();
}

However, when I use this same function (with different name) in another file, which has Account.h header file as well to retrieve data from the same "account.dat" file it gives me segmentation fault. What could be the problem? Following is the function:

void loadData()
{
    ifstream in("accounts.dat", ios::in | ios::binary);
    if(!in)
    {
        cerr<<"File doesn't exist!"<<endl;
        exit(1);
    }

    Account acc;
    while(in.read((char*)&acc, sizeof(Account)))
    {
        cout<<acc;
    }
    in.close();
}

Account's class declaration:

class Account 
{
    friend ostream& operator<<(ostream&,const Account&); //overloading << operator
    friend istream& operator>>(istream&,Account&); //overloading >> operator
    public:
        void operator=(const Account&); //overloading = operator
        bool operator<=(const Account&); //overloading <= operator
        bool operator<(const Account&); //overloading < operator

    private:
        string number; //Account Number
        char name[100]; //Account holder's name
        char sex; //M or F indicating the gender of account holder
        MYLIB::Date dob; //date of birth of account holder
        char address[100]; //address of account holder
        char balance[20]; //balance of account holder
};
7
  • 2
    What does the Accounts class looks like? Are you reading from another process (another program)? Do the Account class contain pointers, strings, vector or other dynamic types? Commented May 30, 2015 at 14:26
  • If Account contains complex types like std::string or std::vector, this code is likely to fail. Commented May 30, 2015 at 14:27
  • Yes it contains stl strings and other classes. But how can it work in AccountManagement but not in another file? Commented May 30, 2015 at 14:28
  • And if it has any virtual member, it will fail even harder ! Commented May 30, 2015 at 14:28
  • can you show us ode for Account class? Commented May 30, 2015 at 14:29

2 Answers 2

3

I don't know about the MYLIB::Date class, but it's enough that you have a std::string object in there.

The std::string object allocates memory dynamically to fit the string it contains. And memory allocated on the heap is available only to the current process, you can't save a pointer (which is inside the std::string class) and load it from some other process and hope there will be valid memory at that pointer.

If you save a pointer to dynamically allocated memory in one process, and load and use it from another process then you will have undefined behavior.

You need to serialize the string in order to save it. Possible the MYLIB::Data object as well.


Disclaimer: It will work on small embedded systems with a single unified address map, unfortunately all the bid user-oriented operating systems (like Windows, OSX and Linux) have separate address-spaces and walls between processes.

Sign up to request clarification or add additional context in comments.

3 Comments

So shall I use convert all of my complex types to simple types before writing to binary file and when reading from binary shall I again read them in simple types and then put in more complex types?
@FahadUrRehman As mentioned serialization is the key solution.
@FahadUrRehman Something like that yes. There are libraries that help you with that, like for example Boost serialization, but for a simple example like yours it might be possible to do it yourself.
0

Your function AccountManagement::output() gives the impression it works perfectly, if you save the object and load it again in the same object and provided the string hasn't changed in the meantime.

What's wrong ?

As soon as your object is no longer a POD object (i.e. it contains data that use pointers, or use virtual functions, etc...), you can't just save it just by writing its memory to the disk.

In your case, the second function fails for this reason. The first function only gives the impression that it works. The string is a complex object that stores somewhere pointers to dynamically allocated memory. If you write the object and read it back as you did, without changing the object, the values that are in memory are simply re-read. The value of the hidden pointer that is read is exactly what it was before the read. That's a very lucky situation. But in most cases it will fail.

How to solve it ?

To save your object, you should serialize it: write/reade each component to the file separatly, using an appropriate function.

THe easiest way to do this is to use some existing serialisation libraries, such as boost serialization.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.