C++ 序列化:从设计到实现

6 篇文章 0 订阅

C++ 序列化:从设计到实现

介绍

github地址

概念

序列化 (serialization) 是将对象的状态信息转化为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入临时或持久性存储区。以后可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。

序列化的方式

  1. 文本格式:JSON,XML
  2. 二进制格式:protobuf

二进制序列化

序列化: 将数据结构或对象转换成二进制串的过程

反序列化:经在序列化过程中所产生的二进制串转换成数据结构或对象的过程

  • 序列化后,数据小,传输速度快
  • 序列化、反序列化速度快

演示

1. 基本类型序列化、反序列化

基本类型序列化

int main()
{
    DataStream ds;
    int n =123;
    double d = 23.2;
    string s = "hellow serialization";
    ds << n <<d <<s;
    ds.save("a.out");
}

基本类型的反序列化

{
    DataStream ds;
    int n;
    double d;
    string s;
    ds.load("a.load");
    ds<<d<<s<<d;
    std::cout<<n<<d<<s<<std::endl;
}
2.复合类型数据序列化、反序列化

复合类型数据序列化

int main()
{
    std::vector<int>v{3,2,1};
    std::map<string,string>m;
    m["name"] = "kitty";
    m["phone"] = "12121";
    m["gender"] = "male";
    DataStream ds;
    ds<<v<<s;
    ds.save("a.out");
}
复合类型数据反序列化
```C++
int main()
{
    DataStreawm ds;
    ds.load("a.out");
    std::vector<int>v;
    std::map<string,string>m;
    ds>>v>>m;
    for(auto it = v.begin();it != v.end();it++)
    {
        std::cout<<*it<<std::endl;
    }
    for(auto it = m.begin();it!= m.end;it++)
    {
        std::cout<<it->first<<"="<<it->second<<std::endl;
    }
}
3.自定义类的序列化、反序列化

自定义类的序列化

class A:public Serialization
{
public:
    A();
    ~A();
    A(const string & name,int age):m_name(name),m_age(age){}
    void show()
    {
        std::cout<<m_name<<" "<<m_age<<std::endl;
    }
    //需要序列化的字段
    SERIALIZE(m_name,m_age);
private:
    string m_name;
    int m_age;
}
int main()
{
    A a("Hell",12);
    DataStream ds;
    ds<<a;
    ds.save("a.out");
}

反序列化类的类型

int main()
{
    DataStreawm ds;
    ds.load("a.out");
    std::vector<int>v;
    std::map<string,string>m;
    ds>>v>>m;
    for(auto it = v.begin();it != v.end();it++)
    {
        std::cout<<*it<<std::endl;
    }
    for(auto it = m.begin();it!= m.end;it++)
    {
        std::cout<<it->first<<"="<<it->second<<std::endl;
    }
}
3.自定义类的序列化、反序列化

自定义类的反序列化

class A:public Serialization
{
public:
    A();
    ~A();
    A(const string & name,int age):m_name(name),m_age(age){}
    void show()
    {
        std::cout<<m_name<<" "<<m_age<<std::endl;
    }
    //需要序列化的字段
    SERIALIZE(m_name,m_age);
private:
    string m_name;
    int m_age;
}
class B:public Serialization
{
public:
    B();
    ~B();
    void add(const A & a)
    {
        m_vector.add(a);
    }
    B(const string & name,int age):m_name(name),m_age(age){}
    void show()
    {
        for(auto it = m_vector.begin();it! = m_vector.end();it++)
        {
            it->show();
        }
    }
    //需要序列化的字段
    SERIALIZE(m_vector);
private:
    std::vector<A> m_vector;
}
int main()
{
    // 序列化
    // B b;
    // b.add(A("hello",12));
    // b.add(A("liuc",21));
    // b.add(A("wang",34));
    // DataSream ds;
    // ds<<b;
    // ds.save("a.out");
    //反序列化
    B b;
    DataSream ds;
    ds.load("a.out");
    ds>>b;
    b.show();
}
Protobuf 与 srialization的区别
protobufserialize
二进制格式
数据体积
编码速度
数据格式类型丰富更加丰富
消息定义文件需要不需要
需要编译需要不需要
代码实现复杂简单

数据类型的定义

enum DataType
{
    BOOL =0,
    CHAR,
    INT32,
    INT64,
    FLOAT,
    DOUBLE,
    STRING,
    VECTOR,
    LIST,
    MAP,
    SET,
    CUSTOM
}

基本类型序列化+反序列化

基本数据类型编码
字段类型字段长度(字节)底层编码格式
bool2Type(1) + Value(1)
char2Type(1) + Value(1)
int325Type(1) + Value(4)
int649Type(1) + Value(8)
float5Type(1) + Value(4)
double9Type(1) + Value(8)
stirng可变长度Type(1) +Length(5) + Value(变成)

对于string类型,1个字节代表类型,长度用的是int32

复合类型序列化+ 反序列化

复合数据类型编码
字段类型字段长度(字节)底层编码格式
vector可变长Type(1) + length(5) + Value(T+T+T+…)
list可变长Type(1) + length(5) + Value(T+T+T+…)
map<K,V>可变长Type(1) + length(5) + Value((k,v)+(k,v)+(k,v)+…)
set可变长Type(1) + length(5) + Value(T+T+T+…)
其中length代表的int32的表示的长度
自定义类型序列化+ 反序列化
自定义对象类型编码
字段类型字段长度(字节)底层编码格式
自定义类可变长Type(1) +Value(D1+D2+D3+…)

Serializable 接口类

class Serializable
{
    public:
        virtual void serializable (DataStream & stream) const =0;
        virtual bool unserializable (DataStream & stream) =0;
}

SERIALIZE宏(参数化实现)

#define SERIALIZE(...)                              \
    void serialize(DataStream & stream) const       \
    {                                               \
        char type = DataStream::CUSTOM;             \
        stream.write((char *)&type, sizeof(char));  \
        stream.write_args(__VA_ARGS__);             \
    }                                               \
                                                    \
    bool unserialize(DataStream & stream)           \
    {                                               \
        char type;                                  \
        stream.read(&type, sizeof(char));           \
        if (type != DataStream::CUSTOM)             \
        {                                           \
            return false;                           \
        }                                           \
        stream.read_args(__VA_ARGS__);              \
        return true;                                \
    }

大端与小端

字节序列

字节顺序又称为端序或尾序(Endianness),在计算机科学领域,指的是电脑内存中在数字通信链路中,组成多字节的字的字节排列顺序。

小端

little-Endian:将低序字节存储在起始地址(在低位编地址),在变量指针转换过程中地址保存不变,比如,int64* 转到 int*32,对于机器计算来说更友好和自然

大端

Big-Endian:将高序字节存储在起始地址(高位编制),内存顺序和数字的书写顺序是一致的,对于人的直观思维比较容易理解,网络字节序统一采用Big-Endian

检测字节序
  1. 使用库函数
#include <endian.h>
__BYTE_ORDER == __LITTLE_ENDIAN

__BYTE_ORDER == __BIG_ENDIAN

  1. 通过字节存储地址判断
#include <stdio.h>
#include<string.h>
int main()
{
    int n = 0x12345678;
    char str[4];
    memcpy(str,&n,sizeof(int));
    for(int i = 0;i<sizeof(int);i++)
    {
        printf("%x\n",str[i]);
    }
    if(str[0]==0x12)
    {
        printf("BIG");
    }else if (str[0] == 0x78){
        printf("Litte");
    }else{
        printf("unknow");
    }
}
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++ 序列化是将对象转换为字节流的过程,这样可以将对象存储到磁盘或网络中,并在需要时重新读取。以下是一个简单的示例: ```c++ #include <iostream> #include <fstream> #include <string> class Person { public: Person() = default; Person(std::string name, int age) : m_name(name), m_age(age) {} std::string getName() const { return m_name; } int getAge() const { return m_age; } friend std::ostream& operator<<(std::ostream& os, const Person& p) { os << "Name: " << p.m_name << ", Age: " << p.m_age << std::endl; return os; } friend std::istream& operator>>(std::istream& is, Person& p) { is >> p.m_name >> p.m_age; return is; } private: std::string m_name; int m_age; }; int main() { // 创建 Person 对象 Person p1("Tom", 30); // 将对象序列化写入文件 std::ofstream ofs("person.dat", std::ios::binary); ofs.write(reinterpret_cast<const char*>(&p1), sizeof(p1)); ofs.close(); // 从文件中读取序列化的对象 std::ifstream ifs("person.dat", std::ios::binary); Person p2; ifs.read(reinterpret_cast<char*>(&p2), sizeof(p2)); ifs.close(); std::cout << p2; return 0; } ``` 在这个示例中,我们定义了一个名为 `Person` 的类,它有两个成员变量:姓名和年龄。我们还重载了输入输出运算符,以便我们可以将对象输出到控制台或从控制台读取对象。 在 `main` 函数中,我们首先创建一个 `Person` 对象 `p1`,然后将其序列化写入名为 `person.dat` 的二进制文件中。接下来,我们从文件中读取序列化的对象并将其存储在 `p2` 中。最后,我们将 `p2` 输出到控制台。 请注意,在将对象写入文件时,我们使用了 `reinterpret_cast` 将指向 `Person` 对象的指针转换为 `char` 指针。同样,在从文件读取对象时,我们也使用了 `reinterpret_cast` 将 `char` 指针转换为 `Person` 对象指针。这是因为我们正在处理字节流,而不是对象本身。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值