GCC下的对象加载段错误

时间:2011-11-07 17:59:07

标签: c++ serialization

这些方法应该保存并加载与其关联的整个对象。当我通过gcc在Linux下编译程序时,保存似乎工作但加载时会出现段错误。当我在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 个答案:

答案 0 :(得分:2)

您需要一种方法来序列化和反序列化您的类;当你以这种方式阅读它时,你的类不会神奇地成为一个对象。

相反,您需要提供在加载/保存您的类时调用的函数,这些函数以您选择的某种格式存储类,例如XML。

所以而不是

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

有一些成员函数将它转换为一个字符串,你可以在加载它时轻松解析一些格式(或者你发现的一些二进制格式更容易),然后保存它。

outFile.write(this->myserialize(), mysize);

答案 1 :(得分:2)

您的class User不是POD类型,它不是普通旧数据类型(如C结构所示)。你不能只按位读取和写入其内存并期望它能够正常工作。 stringvector都不是POD,它们会保留指向动态分配数据的指针。读取这些内容时,尝试访问无效内存将导致段错误。更重要的是,stringvector的内容实际上并没有被保存,因为它们不在对象的内存布局中(有时可能与string一起使用SBO,但它只是偶然但仍然没有定义去做。)

答案 2 :(得分:1)

你不能这样写string。一方面,它通常动态地存储数据,即根本不存储在对象内部,另一方面,你不应该依赖它的任何特定布局。

向量存在类似问题,您似乎根本没有考虑字节顺序和填充。

简单地说,你做的假设不成立。

通常,不要在字节级别上混淆复杂(非POD)对象。使用某些文本格式进行序列化,使用对象的公共成员函数来提取和恢复其状态。

你考虑过JSON吗?

答案 3 :(得分:1)

像字符串之类的东西可能包含指针 - 在这种情况下,你的方法可能会出现严重错误。

您需要序列化数据 - 即。将其转换为一系列字节。

然后在读取数据时,您只需读取字节,然后从中创建对象。新指针是正确的。

答案 4 :(得分:0)

如果你留在这条路线上,我会写字符串的长度而不是null终止它。在加载时更容易分配。有很多以二进制格式考虑。每个字段都应该有某种类型的ID,以便在错误的位置或程序的不同版本中找到它。同样在文件的开头写下你正在使用的endianess和整数的大小等。或者决定一切的标准大小和endianess。我总是用它来编写这样的代码来进行网络和文件存储。有更好的现代方法。还要考虑使用缓冲区并创建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
};