C++ GCC下的对象加载segfault

C++ GCC下的对象加载segfault,c++,serialization,C++,Serialization,这些方法应该保存并加载与其关联的整个对象。当我通过gcc在Linux下编译程序时,save似乎可以工作,但在加载时会出错。当我在Windows下通过VisualStudio编译器编译它时,它就像做梦一样工作。我不确定两者的区别是什么,但我有一种预感,这涉及到一些gcc的奇怪之处 这两种方法: void User::SaveToFile() { ofstream outFile; string datafile_name = username + "_data"; outFile.op

这些方法应该保存并加载与其关联的整个对象。当我通过gcc在Linux下编译程序时,save似乎可以工作,但在加载时会出错。当我在Windows下通过VisualStudio编译器编译它时,它就像做梦一样工作。我不确定两者的区别是什么,但我有一种预感,这涉及到一些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;}
};
类用户
{
私人:
字符串用户名;
字符串实名;
字符串密码;
字符串提示;
双gpa;
矢量课程;
公众:
双预测gpa();
void ChangePassword();
void SaveToFile();
void LoadFromFile(字符串文件名);
void SetUsername(字符串_username){username=_username;}
字符串GetUsername(){return username;}
void SetRealname(字符串_realname){realname=_realname;}
字符串GetRealname(){return realname;}
void SetPass(字符串_password){password=_password;}
字符串GetPass(){返回密码;}
void SetHint(字符串_hint){hint=_hint;}
字符串GetHint(){return hint;}
};

您需要一种序列化和反序列化类的方法;当你像这样读入你的类时,它不能神奇地变成一个对象

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

所以不是

outFile.write((char*)this, sizeof(*this));
使用一些成员函数将其转换为一个字符串,该字符串的格式可以在加载时轻松解析(或者使用一些二进制格式,只要您觉得更容易),然后保存它

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

您需要一种序列化和反序列化类的方法;当你像这样读入你的类时,它不能神奇地变成一个对象

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

所以不是

outFile.write((char*)this, sizeof(*this));
使用一些成员函数将其转换为一个字符串,该字符串的格式可以在加载时轻松解析(或者使用一些二进制格式,只要您觉得更容易),然后保存它

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

您的
类用户
不是POD类型,也不是普通的旧数据类型(就像C结构一样)。您不能只是按位读写它的内存,然后期望它工作。
string
vector
都不是pod,它们保留指向动态分配数据的指针。当读回这些数据时,尝试访问无效内存将导致segfault。更重要的是,
字符串
向量
的内容实际上根本没有被保存,因为它们不在对象的内存布局中(有时它可能与SBO一起使用
字符串
,但这只是偶然的,而且还没有定义)。您的
类用户
不是POD类型,它不是一个普通的旧数据类型(就像C结构一样)。您不能只是按位读写它的内存,然后期望它工作。
string
vector
都不是pod,它们保留指向动态分配数据的指针。当读回这些数据时,尝试访问无效内存将导致segfault。更重要的是,
字符串
向量
的内容实际上根本没有被保存,因为它们不在对象的内存布局中(有时它可能与SBO一起使用
字符串
,但这只是偶然的,而且还没有定义)。

你不能像那样写入
字符串
。一方面,它通常动态存储数据,即根本不在对象内部,另一方面,您不应依赖于它的任何特定布局

向量也有类似的问题,而且您似乎根本没有考虑端点和填充

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

一般来说,不要在字节级别处理复杂(非POD)对象。改为使用某些文本格式序列化,使用对象的公共成员函数提取和恢复其状态


你考虑过JSON吗?

你不能像那样写入
字符串。一方面,它通常动态存储数据,即根本不在对象内部,另一方面,您不应依赖于它的任何特定布局

向量也有类似的问题,而且您似乎根本没有考虑端点和填充

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

一般来说,不要在字节级别处理复杂(非POD)对象。改为使用某些文本格式序列化,使用对象的公共成员函数提取和恢复其状态


您考虑过JSON吗?

字符串等可能包含指针,在这种情况下,您的方法可能会出现严重错误

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


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

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

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


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

如果您继续使用此路由,我将写入字符串的长度,而不是null终止它。加载时更容易分配。有很多要考虑的二进制格式。每个字段都应该有某种类型的ID,以便在错误的位置或程序的不同版本中可以找到它。此外,在文件的开头写下你正在使用的尾数和整数的大小等,或者为所有内容确定一个标准的大小和尾数。我过去一直在写这样的代码,用于网络和文件存储。有更好的现代方法。还考虑使用缓冲区并创建SerialZeIE()函数。 好,m