2012-02-24 1 views
1

Я написал очень небольшой фрагмент кода, и уже получили следующее сообщение об ошибке:SIGABRT в двоичном чтения/записи

malloc: *** error for object 0x100100080: pointer being freed was not allocated

Проблема, я понятия не имею, что указатель компилятор говорит. Я передаю переменную по адресу в функции чтения/записи, но я никогда не освобождал ее, насколько мне известно. Где ошибка в моем коде? Я побежал с Утечками и Зомби, но ничего не получил.

Вот моя программа:

#include <iostream> 
#include <fstream> 
#include <string> 
#include <vector> 
#include <algorithm> 
using namespace std; 

class Bank 
{ 
private: 
    string __name; 

public: 
    Bank() 
    { 
     __name = ""; 
    } 
    Bank(string name) 
    { 
     __name = name; 
    } 

    string getName() const { return __name; } 
}; 


int main (int argc, char * const argv[]) 
{ 
    Bank bank("Bank of America"); 
    Bank bank2; 

    cout << "Bank1: " << bank.getName() << endl; 
    string filename = bank.getName() + ".bank"; 

    ofstream fout(filename.c_str(), ios::binary); 
    if (fout.good()) 
     fout.write((char *)&bank, sizeof(bank)); 
    fout.close(); 

    ifstream fin(filename.c_str(), ios::binary); 
    if (fin.good()) 
     fin.read((char *)&bank2, sizeof(bank2)); 
    fin.close(); 

    cout << "Bank2: " << bank2.getName() << endl; 

    return 0; 
} 
+2

Не используйте идентификаторы '' __like_this' ни _Like_this', они зарезервированы для осуществления. – Fanael

ответ

1

Поскольку ваш класс Bank содержит зЬй :: строку, вы не можете прочитать/записать его в двоичном виде, как вы думаете. Строка std :: содержит внутренние указатели. Если вы напишете его как двоичный, вы просто собираетесь писать указатели, а не фактическое содержимое строки. Аналогично, когда вы читаете строку, вы будете читать указатель. В этом случае вы в конечном итоге заставляете объекты вашего банка и bank2 иметь строки, указывающие на одну и ту же память, поэтому, когда эта память освобождается, она освобождается дважды.

Вам понадобится другой способ записи банковских данных в файл. В этом случае простой файл ASCII с именем банка будет в порядке.

+0

Позже, однако, он будет иметь несколько полей, таких как 'vector ' для хранения объектов типа 'Account' (будет определено позже). Двоичный файл будет казаться более удобным для работы, чтобы я мог читать данные в целом и не анализировать его. – rcplusplus

+0

К сожалению, в C++ нет ничего, что позволит вам сделать это легко. Существуют библиотеки, такие как boost :: serialization, которые стараются сделать это проще, но вам все равно придется внести изменения в код сериализации, если вы измените свой класс. –

1

Вы не можете делать то, что делаете, просто потому, что std::string не может быть скопирован таким образом. Внутренний объект string выделяет память, а простая копия внешней структуры не делает то, что вы ожидаете.

Необходимо соответствующим образом сериализовать эту структуру.

+0

Как вы правильно сериализуете объект? В книге, которую я читал, у них был объект «Bear», который они написали/читали из двоичного файла, как и выше. – rcplusplus

+1

Для POD (Plain-Old-Datatype) это нормально, однако эта структура включает в себя элементы, которые внутренне имеют динамически распределенные компоненты, побитовая копия (что фактически означает 'write'), не понимает, что' string' действительно сохраняет содержимое в другом месте в памяти. Посмотрите на что-то вроде синтаксиса, и вы увидите, как реализовать эту стратегию. – Nim

+0

Для POD это нормально для временного хранения. Перекомпилируйте свою программу с различными параметрами или с другой версией компилятора, и вы не сможете прочитать то, что вы написали ранее. –

1
  1. Не используйте подчеркивание, пожалуйста
  2. передавать объекты по ссылке: Bank(string& name), пожалуйста
  3. Это зло: fout.write((char *)&bank, sizeof(bank));
  4. Вы можете написать << и >> ostream операторов вашего Bank класса.

Например:

friend std::ostream& operator<<(std::ostream &out, const Bank& b); 
friend std::istream& operator>>(std::istream &out, const Bank& b); 
+0

Из любопытства, что не так с подчеркиванием и передачей по значению? И почему 'fout.write()' _evil_? – rcplusplus

+1

первые два не злые :) просто хорошая практика. получение адреса банка и литье на char не будет работать. Если вы хотите получить строковые байты, напишите метод GetBytes, который возвращает __name :: c_str(), который является указателем const. – vulkanino

+0

Что делать, если я создаю поле '__name' типа' char * ', будет ли сериализация хорошо работать? – rcplusplus

1

Вы не можете прочитать объект, который содержит зЬй :: строку (или что-нибудь, что это не данные Plain Ol») с fin.read() -

Объект считывается и записывается как поток байтов, но std: string содержит указатель на память, который хранится в другом месте и не написан с вашим fout.write() и не инициализирован правильно с вашим fin.read()

Это потому, что оно не является ini tialized правильно с вашим fin.read(), что вы получаете ошибку кучи; когда объект выходит из области видимости, вызывается деструктор неправильно инициализированной std :: string и пытается освободить память, которой она не принадлежит.

Возможно, вы захотите написать собственный метод ввода-вывода для своего объекта и сохранить или загрузить его по частям. Для быстрого доступа к этому используйте библиотеку сериализации Boost.

+0

Вы не можете надежно относиться к этому с помощью POD, за исключением одного и того же исполняемого файла. –

0

Функции членов, которые записывают ostream и считывают istream, специально предназначены для ввода и вывода двоичных данных.Если вы хотите, чтобы манипулировать двоичные данные, используйте следующее:

ifstream fin(filename.c_str(), ios::in|ios::binary|ios::ate); 
size = fin.tellg(); 
memblock = new char [size]; 
fin.seekg(0, ios::beg); 

if (fin.good()){ 
    fin.read(memblock, size); 
    fin.close(); 
} 
delete[] memblock; 
+0

Это неверно. Функции-члены 'write' и' read' предназначены для написания предварительно форматированного текста и чтения необработанного текста для анализа за пределами iostream. В стандарте нет поддержки какого-либо двоичного формата (вероятно, из-за отсутствия широко распространенного двоичного формата). –

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