使用C ++中的指针序列化类

时间:2012-03-19 22:18:39

标签: c++ serialization pointers

我想序列化Person类型的对象。我想稍后使用它来保存数据甚至保存游戏。我知道如何为intcharbool这样的原语执行此操作,甚至像char[]这样的c-strings。

问题是,我希望字符串尽可能大,而不是声明一个大小为256的char数组,并希望没有人输入太大的东西。我读到,使用std::string作为成员序列化一个类是行不通的,因为它有一个内部指针,但是有没有办法将我的类序列化为char*作为成员?

我意识到Boost有一个序列化库,但是我想在不需要外部库的情况下这样做,这似乎是一个很好的尝试。

这是我的Person课程:

class Person
{
private:
   char* _fname; 
   char* _lname;

public:
   Person();
   Person(const char* fname, const char* lname);
   Person(const string& fname, const string& lname);

   string fname() const;
   void fname(const char* fname);
   void fname(const string& fname);

   string lname() const;
   void lname(const char* lname);
   void lname(const string& lname);
};

4 个答案:

答案 0 :(得分:3)

首先:在你的课堂上使用std :: string,从长远来看,它会让你的生活更加轻松。

但是这个建议适用于std :: string和char *(稍微调整一下应该很明显)。

基本上你想序列化未知大小的数据(在编译时)。这意味着当您对数据进行反序列化时,您必须具有一种技术,该技术可以告诉您数据的长度(对象的大小前缀)或查找数据结束的方式(终止标记)。

终止标记更易于序列化。但是反序列化更难(因为你必须寻求结束)。此外,您必须在对象中转义任何终止标记,并且反序列化必须知道转义并将其删除。

因此,由于这种并发症,我不想使用终止标记。因此,我在对象前面加上一个大小。这样做的代价是我必须以不会破坏的方式编码对象的大小。

因此,如果我们为对象添加其大小前缀,您可以这样做:

// Place a ':' between the string and the size.
// There must be a marker as >> will continue reading if
// fname contains a digit as its first character.
// I don;t like using a space as >> skips spaces if you are not carefull
// and it is hard to tell the start of the string if the first characters in fname
// are the space character.
std::cout << strlen(fname) << ":" << fname;

然后你可以像这样反序列化:

size_t size;
char   mark;
std::cint >> size >> mark;
if (!std::cin || mark != ':')
{    throw BadDataException;
}
result = new char[size+1]();  // Note the () to zero fill the array.
std::cin.read(result, size)

编辑1(基于注释)更新:与字符串一起使用:

size_t size;
char   mark;
std::cint >> size >> mark;
if (!std::cin || mark != ':')
{    throw BadDataException;
}
std::string  result(' ', size);  // Initialize string with enough space.
std::cin.read(&result[0], size)  // Just read directly into the string

编辑2(基于评论)

序列化字符串的辅助函数

struct StringSerializer
{
    std::string&    value;
    StringSerializer(std::string const& v):value(const_cast<std::string&>(v)){}
    friend std::ostream& operator<<(std::ostream& stream, StringSerializer const& data)
    {
        stream << data.value.size() << ':' << data.value;
    }
    friend std::istream& operator>>(std::istream& stream, StringSerializer const& data)
    {
        std::size_t size;
        char        mark(' ');
        stream >> size >> mark;
        if (!stream || mark != ':')
        {    stream.setstate(std::ios::badbit);
             return stream;
        }
        data.value.resize(size);
        stream.read(&data.value[0], size);
    }
};

序列化某人

std::ostream& operator<<(std::ostream& stream, Person const& data)
{
    return stream << StringSerializer(data.fname) << " "
                  << StringSerializer(data.lname) << " "
                  << data.age                     << "\n";
}
std::istream& operator>>(std::istream& stream, Person& data)
{
    stream    >> StringSerializer(data.fname)
              >> StringSerializer(data.lname)
              >> data.age;
    std::string line;
    std::getline(stream, line);

    if (!line.empty())
    {    stream.setstate(std::ios::badbit);
    }
    return stream;
}

用法:

int main()
{
    Person p;
    std::cin  >> p;
    std::cout << p;

    std::ofstream  f("data");
    f << p;
}

答案 1 :(得分:1)

您无法序列化指针,需要将数据指针序列化为。

您需要序列化整个对象网络,从Person(或Game)开始并查看可从起始对象访问的每个对象。

反序列化时,您从存储中读取数据,为该数据分配内存,并将此新分配的内存的地址用作Person / Game对象的成员

答案 2 :(得分:0)

指针字段使得它更难,但不是不可能序列化。如果您不想使用任何序列化库,请按以下步骤操作。

您应该确定序列化时指向的大小(例如,它可能是固定大小,或者它可能是一个末尾带有空字符的C字符串),然后您可以保存一个标记,指示您将序列化间接对象以及大小和指向的区域的实际内容。

当您在反序列化期间偶然发现该标记时,您可以分配适当的内存量,将对象复制到其中并将指针存储到反序列化对象中的区域。

答案 3 :(得分:0)

我建议使用向量来封装字符串 序列化。

#include <vector>
using namespace std;
map vector<unsigned char> cbuff;
inline cbuff vchFromString(const std::string &str) {
unsigned char *strbeg = (unsigned char*) str.c_str();
  return cbuff(strbeg, strbeg + str.size());
}
inline std::string stringFromVch(const cbuff &vch) {
 std::string res;
 std::vector<unsigned char>::const_iterator vi = vch.begin();
 while (vi != vch.end()) {
  res += (char) (*vi);
  vi++;
 }
 return res;
}

class Example
{
  cbuff label;
  Example(string labelIn)
  {
    SetLabel(labelIn);
  }
  IMPLEMENT_SERIALIZE
  (
    READWRITE(label);
  )
  void SetLabel(string labelIn)
  {
    label = vchFromString(labelIn);
  }
  string GetLabel()
  {
    return (stringFromVch(label));
  }
};