在C ++中使用指针序列化一个类 [英] Serializing a class with a pointer in C++

查看:144
本文介绍了在C ++中使用指针序列化一个类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我要序列化Person类型的对象.我想稍后将其用于保存数据甚至保存游戏.我知道如何针对intcharbool之类的原语,甚至是诸如char[]之类的c字符串执行此操作.

I want to serialize an object of type Person. I want to use it later on for data saving or even game saving. I know how to do it for primitives like int, char, bool, and even c-strings like char[].

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

The problem is, I want the string to be as big as it needs to rather than declaring a char array of size 256 and hoping no one enters something too big. I read that serializing a class with std::string as a member doesn't work because it has an internal pointer, but is there a way to serialize my class which has a char* as a member?

我意识到Boost具有序列化库,但是我想不需要外部库就可以这样做,这似乎是一个很好的尝试.

I realize Boost has a serialization library, but I'd like to do this without the need of external libraries, it seems like a good activity to try.

这是我的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);
};

推荐答案

首先:在类中使用std :: string,从长远来看,这将使您的生活变得更加轻松.

First: Use std::string in your class it will make your life so much easier in the long run.

但是此建议对std :: string和char *都有效(应该进行细微调整).

But this advice works for both std::string and char* (with minor tweaks that should be obvious).

基本上,您想序列化未知大小的数据(在编译时).这意味着在对数据进行反序列化时,您必须具有一种可以告诉您数据有多长时间的技术(以大小为前缀的对象)或一种找到数据结尾的方法(终止标记).

Basically you want to serialize data of unknown size (at compile time). This means when you de-serialize the data you must either have a technique that tells you how long the data is (prefix the object with a size) or a way to find the end of the data (a termination marker).

终止标记更易于序列化.但是反序列化比较困难(因为您必须努力寻找最终目标).另外,您必须避免对象中出现终止标记的任何情况,反序列化必须了解转义并将其删除.

A termination marker is easier for serialization. But harder for de-serialization (as you must seek forward to find the end). Also you must escape any occurrences of the termination marker within your object and the de-serialization must know about the escaping and remove it.

因此,由于这种复杂性,我宁愿不使用终止标记.结果,我为对象添加了大小.这样做的代价是我必须以不会破坏的方式对对象的大小进行编码.

Thus because of this complications I prefer not to use a termination marker. As a result I prefix the object with a size. The cost of this is that I must encode the size of the object in a way that will not break.

因此,如果我们给对象加上大小,则可以执行以下操作:

So if we prefix an object with its size you can do this:

// 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;

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

Then you can de-serialize like this:

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(基于评论)

Helper函数来序列化字符串

Edit 2 (based on commented)

Helper function to serialize a string

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;
}

这篇关于在C ++中使用指针序列化一个类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆