Segfault загрузки объекта под GCC - PullRequest
1 голос
/ 07 ноября 2011

Эти методы должны сохранять и загружать весь объект, с которым они связаны. Когда я компилирую программу под Linux через gcc, кажется, что сохранение работает, но при загрузке происходит сбой. Когда я компилирую его под Windows через компилятор Visual Studio, он работает как сон. Я не уверен, в чем различия, но у меня есть догадка, что это связано с некоторой странностью gcc.

Два метода:

void User::SaveToFile()
{
  ofstream outFile;
  string datafile_name = username + "_data";
  outFile.open(datafile_name.c_str(), ios::binary);
  outFile.write((char*)this, sizeof(*this));
}
void User::LoadFromFile(string filename)
{
  ifstream inFile;
  inFile.open(filename.c_str(), ios::binary);
  inFile.read((char*)this, sizeof(*this));
}

Декларация:

class User
{
private:
  string username;
  string realname;
  string password;
  string hint;
  double gpa;
  vector<Course> courses;
public:
  double PredictGPA();
  void ChangePassword();
  void SaveToFile();
  void LoadFromFile(string filename);

  void SetUsername(string _username){username = _username;}
  string GetUsername(){return username;}
  void SetRealname(string _realname){realname = _realname;}
  string GetRealname(){return realname;}
  void SetPass(string _password){password = _password;}
  string GetPass(){return password;}
  void SetHint(string _hint){hint = _hint;}
  string GetHint(){return hint;}
};

Ответы [ 5 ]

2 голосов
/ 07 ноября 2011

Ваш class User не является типом POD, он не является типом данных Plain Old (как структуры C). Вы не можете просто читать и записывать его память побитно и ожидать, что он будет работать. И string, и vector не являются POD, они сохраняют указатели на свои динамически распределяемые данные. При чтении этих данных попытки доступа к недопустимой памяти приведут к segfault. Более того, содержимое как string, так и vector на самом деле вообще не сохраняется, поскольку они не находятся внутри структуры памяти объекта (иногда это может работать с string с SBO, но это просто шанс и до сих пор не определено, чтобы сделать это).

2 голосов
/ 07 ноября 2011

Вам понадобится способ сериализации и десериализации вашего класса; ваш класс не может волшебным образом стать объектом, когда вы читаете его вот так.

Вместо этого вам нужно будет предоставить функции, которые вы вызываете при загрузке / сохранении вашего класса, которые хранят класс в каком-либо формате по вашему выбору, например. XML.

так вместо

outFile.write((char*)this, sizeof(*this));

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

outFile.write(this->myserialize(), mysize);
1 голос
/ 07 ноября 2011

Такие вещи, как строки и т. Д. Могут содержать указатели - в этом случае ваш метод может пойти ужасно неправильно.

Вам необходимо сериализовать данные - т.е. преобразовать их в серию байтов.

Затем при чтении данных вы просто читаете байты, а затем создаете объект из этого.Новые указатели будут правильными.

1 голос
/ 07 ноября 2011

Нельзя так писать в string.С одной стороны, он обычно хранит свои данные динамически, то есть вообще не внутри объекта, а с другой стороны, вы не должны полагаться на какую-либо конкретную его разметку.

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

Проще говоря, вы делаете предположения, которые не сохраняются.

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

Рассматривали ли вы JSON?

0 голосов
/ 07 ноября 2011

Если вы останетесь с этим маршрутом, я бы записал длину строки, а не ноль, заканчивая ее.Проще выделить на загрузку.Есть много для рассмотрения в двоичном формате.Каждое поле должно иметь определенный тип идентификатора, чтобы его можно было найти в неправильном месте или в другой версии вашей программы.Также в начале вашего файла напишите, какой порядок байтов вы используете, а также размер целых чисел и т. Д. Или определите стандартный размер и порядок байтов для всего.Я все время пишу такой код для работы в сети и хранения файлов.Есть гораздо лучшие современные подходы.Также рассмотрите возможность использования буфера и создания функции Serialize ().

Хорошие современные альтернативы включают: SQLite3, XML, JSON

Непроверенный пример:

class object
{

Load()
{
  ifstream inFile;
  int size;

  inFile.open("filename", ios::binary);

  inFile.read(&size, 4);  
  stringA.resize(size);
  inFile.read(&stringA[0], size);

  inFile.read(&size, 4);  
  stringB.resize(size);
  inFile.read(&stringB[0], size);

  inFile.close();          //don't forget to close your files
}
Save()
{
  ofstream outFile;
  int size;

  outFile.open("filename", ios::binary);

  size = stringA.size();   
  outFile.write(&size, 4);
  outFile.write(&stringA[0], size);

  size = stringB.size();       
  outFile.write(&size, 4);
  outFile.write(&stringA[0], size);
  outFile.close();
}

private:
std::string stringA
std::string stringB
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...