2015-05-30 3 views
0

У меня есть класс AccountManagement в AccountManagement.cpp. У меня есть еще один класс Account в Account.cpp. У меня есть шаблон, который упорядочивает данные в списке с помощью класса OrdereList, который также имеет свой собственный итератор. Класс AccountManagement выводит список учетных записей в виде двоичного файла, как показано ниже:C++: проблемы чтения ввода из двоичного файла

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(); 
} 

Я определил следующую функцию внутри класса AccountManagement, который считывает все данные из двоичного файла и выводит его. Эта функция работает отлично. Показано здесь:

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(); 
} 

Однако, когда я использую эту же функцию (с другим именем) в другом файле, который имеет Account.h файл заголовка, а также для извлечения данных из того же «account.dat» файл это дает мне ошибку сегментации. В чем может быть проблема? Ниже приводится функция: декларация класса

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(); 
} 

акаунта:

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 
}; 
+2

Как выглядит класс 'Accounts'? Вы читаете из другого процесса (другая программа)? В классе 'Account' содержатся указатели, строки, векторные или другие динамические типы? –

+0

Если 'Account' содержит сложные типы, такие как' std :: string' или 'std :: vector', этот код, скорее всего, не удастся. –

+0

Да, он содержит строки stl и другие классы. Но как он может работать в AccountManagement, но не в другом файле? –

ответ

3

Я не знаю, о MYLIB::Date классе, но достаточно того, что у вас есть std::string объект там.

Объект std::string выделяет память динамически, чтобы соответствовать содержащейся в нем строке. И память, выделенная в куче, доступна только для текущего процесса, вы не можете сохранить указатель (который находится внутри класса std::string) и загрузить его из какого-либо другого процесса и надеяться, что на этом указателе будет действительная память.

Если вы сохраняете указатель на динамически выделенную память в одном процессе и загружаете и используете ее из другого процесса, то у вас будет неопределенное поведение.

Для того, чтобы его сохранить, вам необходимо указать номер serialize. Возможно также объект MYLIB::Data.


Отказ от ответственности: Это будет работать на небольшие встроенные системы с одной единой адресной картой, к сожалению, все ставки ориентированных на пользователь операционных системы (например, Windows, OSX и Linux) имеют отдельные адресные пространства и стены между процессы.

+0

Итак, я должен использовать для преобразования всех моих сложных типов в простые типы перед записью в двоичный файл и при чтении из двоичного файла я должен снова прочитать их в простых типах, а затем ввести более сложные типы? –

+0

@FahadUrRehman Как упоминалось, _serialization_ является ключевым решением. –

+0

@FahadUrRehman Что-то вроде этого да. Существуют библиотеки, которые помогут вам в этом, например, [сериализация Boost] (http://www.boost.org/doc/libs/1_58_0/libs/serialization/doc/index.html), но для простого примера, например возможно, вы сможете сделать это сами. –

0

Ваша функция AccountManagement::output() создает впечатление, что работает отлично, если вы сохраните объект и загрузить его снова в том же объекте и при условии, что строка не изменилась за это время.

Что случилось?

Как только ваш объект больше не является объектом POD (т. Е. Содержит данные, которые используют указатели, или использует виртуальные функции и т. Д.), Вы не можете просто сохранить его, просто записав свою память на диск.

В этом случае вторая функция не работает по этой причине. Первая функция создает впечатление, что она работает. Строка представляет собой сложный объект, который хранит где-то указатели на динамически выделенную память. Если вы пишете объект и читаете его так же, как и вы, без изменения объекта, значения, которые находятся в памяти, просто перечитываются. Значение скрытого указателя, которое считывается, точно такое же, как и перед чтением. Это очень удачная ситуация.Но в большинстве случаев это провалится.

Как это решить?

Чтобы сохранить свой объект, вы должны его сериализовать: каждый раз записывать/редактировать каждый компонент в файл, используя соответствующую функцию.

Самый простой способ сделать это - использовать некоторые существующие библиотеки сериализации, такие как boost serialization.

Смежные вопросы