C++實現(xiàn)數(shù)據(jù)的序列化和反序列化詳解
1.簡介
在C++應用程序中,經(jīng)常會涉及到對一些數(shù)據(jù)進行序列化和反序列化的處理。序列化可以將一個對象轉(zhuǎn)換為一串字節(jié)流,這樣就可以將其存儲在硬盤上或者通過網(wǎng)絡傳輸?shù)狡渌O備上。而反序列化則是將這些字節(jié)流解析成原始的對象。
在Qt中,數(shù)據(jù)的序列化和反序列化可以使用QDataStream類來完成。QDataStream是一個方便的Qt類,它可以將基本數(shù)據(jù)類型、Qt數(shù)據(jù)類型以及用戶定義的數(shù)據(jù)類型都進行序列化和反序列化。在使用QDataStream進行序列化時,需要指定一個QIODevice類的子類(例如QFile或QBuffer)來將數(shù)據(jù)寫入到文件或者內(nèi)存中。
示例如下:
//【1】結構體
typedef struct _stCtrlCmdHeader
{
unsigned char rCtrlObj;
unsigned char rCtrlObjID;
unsigned char rCtrlCmd;
unsigned short rDataLen;
public:
_stCtrlCmdHeader()
{
memset(this, 0, sizeof(_stCtrlCmdHeader));
rCtrlObj = 0x01;
rCtrlObjID = 0x00;
}
friend QDataStream& operator<<(QDataStream& dataStream, const _stCtrlCmdHeader& data) //序列化
{
dataStream << data.rCtrlObj;
dataStream << data.rCtrlObjID;
dataStream << data.rCtrlCmd;
dataStream << data.rDataLen;
return dataStream;
}
friend QDataStream& operator>>(QDataStream& dataStream, _stCtrlCmdHeader& data) //反序列化
{
dataStream >> data.rCtrlObj;
dataStream >> data.rCtrlObjID;
dataStream >> data.rCtrlCmd;
dataStream >> data.rDataLen;
return dataStream;
}
}stCtrlCmdHeader;
//【2】序列化到QByteArray
QByteArray data;
QDataStream out(&data, QIODevice::WriteOnly); // we will serialize the data into the data
dataStream.setByteOrder(QDataStream::LittleEndian); //設置小端對齊
out << QString("the answer is"); // serialize a string
out << (qint32)42; // serialize an integer
stCtrlCmdHeader header;
//...
out << header; //serialize struct or class
//【3】反序列化
char szBuffer[1024] = { 0 };
int len = 0 ;
//...獲取網(wǎng)絡數(shù)據(jù)
QByteArray byArray(szBuffer, len);
QDataStream dataStream(&byArray, QIODevice::ReadOnly);
dataStream.setByteOrder(QDataStream::LittleEndian);
char szText[20] = { 0 };
dataStream.readRawData((char*)szText, 10); //unserialize a string
qint32 number = 0;
dataStream >> number ; //unserialize an integer
stCtrlCmdHeader header;
//...
out >> header; //unserialize struct or class
在Qt中很方便地可以用到這些類,但是脫離Qt,在標準的C++標準庫就沒有這么方便了,今天就模仿QByteArray和QDataStream類的接口,寫了序列化和反序列化的類CByteArray和CDataStream,可以無縫替換QByteArray和QDataStream,下面就來介紹一下CByteArray和CDataStream的設計和實現(xiàn)。
2.CByteArray設計實現(xiàn)
設計要點:
1) CByteArray可以理解是純字節(jié)數(shù)組,可以存放任意格式的數(shù)據(jù),于是后臺用 std::basic_string<char>來存儲實際的數(shù)據(jù)。
2)構造函數(shù)接收字符數(shù)組(char*+len)
3) 構造函數(shù)接受右值引用,實現(xiàn)快速的移動語義
4)重載符號=,接受右值引用,實現(xiàn)快速的移動語義
5)根據(jù)長度寫入和讀取原始數(shù)據(jù)接口
int writeRawData(const char* data, PUInt32 size) override; int readRawData(char* datsa, PUInt32 size) override;
6) 整個類采用Pimpl技法實現(xiàn)
具體實現(xiàn):
類型定義:DataType.h
#ifndef _DATA_TYPE_H_ #define _DATA_TYPE_H_ typedef char PInt8; typedef unsigned char PUInt8; typedef short PInt16; typedef unsigned short PUInt16; typedef int PInt32; typedef unsigned int PUInt32; typedef long long PInt64; typedef unsigned long long PUInt64; typedef float PFloat32; typedef double PFloat64; #endif
抽象接口定義如下:IDataBuffer.h
#ifndef _IDATA_BUFFER_H_
#define _IDATA_BUFFER_H_
#include "DataType.h"
class IDataBuffer
{
public:
virtual ~IDataBuffer(){}
virtual int writeBytes(const char*, PUInt32) = 0;
virtual int readBytes(char*, PUInt32) = 0;
virtual int writeRawData(const char*, PUInt32) = 0;
virtual int readRawData(char*, PUInt32) = 0;
virtual int bytesAvailable() const = 0;
virtual PUInt64 skip(PUInt64 maxSize) = 0;
};
#endif
ByteArray.h
#ifndef _BYTE_ARRAY_H_
#define _BYTE_ARRAY_H_
#include <string>
#include <memory>
#include "IDataBuffer.h"
class CByteArrayPrivate;
class CByteArray : public IDataBuffer
{
public:
explicit CByteArray();
CByteArray(const CByteArray& other);
CByteArray(CByteArray&& other);
CByteArray(const char* buffer, int size);
CByteArray& operator=(CByteArray&& other);
operator bool() const;
virtual ~CByteArray();
public:
int writeRawData(const char* data, PUInt32 size) override;
int readRawData(char* datsa, PUInt32 size) override;
int writeBytes(const char*, PUInt32) override;
int readBytes(char*, PUInt32) override;
const char* data() const;
int size() const;
int bytesAvailable() const override;
PUInt64 skip(PUInt64 maxSize) override;
CByteArray& append(const char* data, PUInt32 size);
CByteArray& append(const char* data);
void clear();
private:
std::unique_ptr<CByteArrayPrivate> d_ptr;
};
#endif
ByteArray.cpp
#include "ByteArray.h"
#include <assert.h>
#include <string.h>
//#include <string_view>
class CByteArrayPrivate
{
public:
explicit CByteArrayPrivate():m_readPos(0) {}
CByteArrayPrivate(const char* buffer, int size): m_buffer(buffer, size), m_readPos(0) {}
//這個類,有點類似于Golang語言中的切片Slice。這就意味著std::string_view本身并不擁有內(nèi)存本身,
//它只是一個View,一個窗口,觀看內(nèi)存的窗口
//std::string_view m_buffer;
std::basic_string<char> m_buffer;
int m_readPos; //暫時沒用
};
CByteArray::CByteArray()
: d_ptr(new CByteArrayPrivate)
{
}
CByteArray::CByteArray(const CByteArray& other)
: d_ptr(new CByteArrayPrivate)
{
d_ptr->m_buffer = other.d_ptr->m_buffer;
d_ptr->m_readPos = other.d_ptr->m_readPos;
}
CByteArray::CByteArray(CByteArray&& other)
: d_ptr(other.d_ptr.release())
{
other.d_ptr.reset(new CByteArrayPrivate);
}
CByteArray::CByteArray(const char* buffer, int size)
: d_ptr(new CByteArrayPrivate(buffer,size))
{
}
CByteArray::operator bool() const
{
return (0 ==size());
}
CByteArray& CByteArray::operator=(CByteArray&& other)
{
if (d_ptr == other.d_ptr){
return *this;
}
CByteArrayPrivate* temp = d_ptr.release();
d_ptr.reset(other.d_ptr.release());
other.d_ptr.reset(temp);
return *this;
}
CByteArray::~CByteArray()
{
}
int CByteArray::writeRawData(const char* data, PUInt32 size)
{
d_ptr->m_buffer.append(data, size);
return size;
}
int CByteArray::readRawData(char* data, PUInt32 size)
{
if (d_ptr->m_buffer.copy(data, size, 0) != size){
assert(0);
return 0;
}
d_ptr->m_buffer.erase(0, size);
return size;
}
const char* CByteArray::data() const
{
return d_ptr->m_buffer.data();
}
int CByteArray::size() const
{
return d_ptr->m_buffer.size();
}
int CByteArray::bytesAvailable() const
{
return size();
}
PUInt64 CByteArray::skip(PUInt64 maxSize)
{
PUInt64 nSkip = maxSize;
if (maxSize >= d_ptr->m_buffer.size()){
nSkip = d_ptr->m_buffer.size();
d_ptr->m_buffer.clear();
return nSkip;
}else{
d_ptr->m_buffer.erase(0, maxSize);
return nSkip;
}
}
int CByteArray::writeBytes(const char*, PUInt32)
{
assert(0);
return 0;
}
int CByteArray::readBytes(char*, PUInt32)
{
assert(0);
return 0;
}
CByteArray& CByteArray::append(const char* data, PUInt32 size)
{
d_ptr->m_buffer.append(data, size);
return *this;
}
CByteArray& CByteArray::append(const char* data)
{
if (!data){
return *this;
}
int len = strlen(data);
return append(data, len);
}
void CByteArray::clear() {
d_ptr->m_buffer.clear();
}
3.CDataStream設計實現(xiàn)
設計要點:
1) 支持所有支持IDataBuffer的實現(xiàn)類
2)支持C++的基本數(shù)據(jù)類型序列化和反序列化,重載operator<<和operator>>
3) 支持自定義數(shù)據(jù)類型的序列化和反序列化,如結構體或類
4)反序列化時,可以忽略指定的字節(jié)
5)反序列化可變字節(jié)長度時,可以獲取未序列化的數(shù)據(jù)長度,用戶獲取長度申請空間并反序列化到指定的內(nèi)存中
具體實現(xiàn):
ShortWaveToolFun.h
#ifndef _SHORT_WAVE_TOOL_FUNC_H_
#define _SHORT_WAVE_TOOL_FUNC_H_
#include <string>
#include "DataType.h"
#include <functional>
#include <memory>
#include <string.h>
#define WORK_MSG_ID(link, type, sub) ( (link<<16) | (type<<8) | sub )
#define GET_WORK_TYPE(id) ((id >> 8) & 0xff)
#define GET_WORK_CMD(id) (id & 0xff)
#define GET_WORK_B_TYPE(id) ((id >> 16) & 0xff)
#define MEMBER_OFF_SET(type, member) ((size_t) &(static_cast<type*>(0))->member)
#define CONTAINER_OF(ptr, type, member) ({ \
const typeof(((type*)0)->member) * __mptr = (ptr); \
(type*)((char*)__mptr - MEMBER_OFF_SET(type,member)); })
template <typename T, class = typename std::enable_if<sizeof(T)%2==0>::type>
T reverseNumber(T value)
{
static_assert(sizeof(T)%2==0, "T type is invalid!!!");
union X{
T val1;
char val2[sizeof(T)];
};
int i = 0;
int size = sizeof(T);
char temp = 0;
X x;
x.val1 = value;
for (i = 0; i < sizeof(T)/2; i++){
temp = x.val2[i];
x.val2[i] = x.val2[size-i-1];
x.val2[size-i-1] = temp;
}
return x.val1;
}
template <typename T, class = typename std::enable_if<sizeof(T)<=sizeof(PUInt64)>::type>
PUInt64 transTToPUInt64(T value)
{
static_assert(sizeof(T)<=sizeof(PUInt64), "T type is invalid!!!");
union X{
T val1;
PUInt64 val2;
};
X x;
x.val2 = 0; //將高位置0
x.val1 = value;
return x.val2;
}
template <typename T, class = typename std::enable_if<sizeof(T)<=sizeof(PUInt64)>::type>
T transPUInt64ToT(PUInt64 value)
{
static_assert(sizeof(T)<=sizeof(PUInt64), "T type is invalid!!!");
union X{
T val1;
PUInt64 val2;
};
X x;
x.val2 = value;
return x.val1;
}
#endif
DataStream.h
#ifndef _DATA_STREAM_H_
#define _DATA_STREAM_H_
#include "DataType.h"
class IDataBuffer;
class CByteArray;
class CDataStream
{
public:
explicit CDataStream(IDataBuffer* pBuffer);
~CDataStream();
bool bigEndian() const;
void setBigEndian(bool newBigEndian);
CDataStream& operator<<(bool i);
CDataStream& operator<<(PInt8 i);
CDataStream& operator<<(PUInt8 i);
CDataStream& operator<<(PInt16 i);
CDataStream& operator<<(PUInt16 i);
CDataStream& operator<<(PInt32 i);
CDataStream& operator<<(PUInt32 i);
CDataStream& operator<<(PInt64 i);
CDataStream& operator<<(PUInt64 i);
CDataStream& operator<<(PFloat32 i);
CDataStream& operator<<(PFloat64 i);
CDataStream& operator<<(const char* i);
CDataStream& operator<<(const CByteArray& data);
template <typename T>
CDataStream& operator<<(const T& t)
{
(*this) << t;
return *this;
}
CDataStream& operator>>(bool& i);
CDataStream& operator>>(PInt8& i);
CDataStream& operator>>(PUInt8& i);
CDataStream& operator>>(PInt16& i);
CDataStream& operator>>(PUInt16& i);
CDataStream& operator>>(PInt32& i);
CDataStream& operator>>(PUInt32& i);
CDataStream& operator>>(PInt64& i);
CDataStream& operator>>(PUInt64& i);
CDataStream& operator>>(PFloat32& i);
CDataStream& operator>>(PFloat64& i);
CDataStream& operator>>(char*& i);
CDataStream& operator>>(CByteArray& data);
template <typename T>
CDataStream& operator>>(T& t)
{
(*this) >> t;
return *this;
}
int writeRawData(const char* data, PUInt32 size);
int readRawData(char* data, PUInt32 size);
bool atEnd() const;
int bytesAvailable() const;
int skipRawData(int len);
private:
bool m_bigEndian;
IDataBuffer* m_buffer;
};
#endif
DataStream.cpp
#include "IDataBuffer.h"
#include "DataStream.h"
#include "ShortWaveToolFun.h"
#include <string.h>
#include "ByteArray.h"
CDataStream::CDataStream(IDataBuffer* pBuffer)
: m_bigEndian(false)
, m_buffer(pBuffer)
{
}
CDataStream::~CDataStream()
{
}
bool CDataStream::bigEndian() const
{
return m_bigEndian;
}
void CDataStream::setBigEndian(bool newBigEndian)
{
m_bigEndian = newBigEndian;
}
CDataStream& CDataStream::operator<<(bool i)
{
*this << (PInt8)i;
return *this;
}
CDataStream& CDataStream::operator<<(PInt8 i)
{
PInt8 ch = i;
m_buffer->writeRawData(&ch, sizeof(ch));
return *this;
}
CDataStream& CDataStream::operator<<(PUInt8 i)
{
PInt8 ch = i;
m_buffer->writeRawData(&ch, sizeof(ch));
return *this;
}
CDataStream& CDataStream::operator<<(PInt16 i)
{
PInt16 temp = i;
if (m_bigEndian){
temp = reverseNumber(i);
}
m_buffer->writeRawData((char*)&temp, sizeof(temp));
return *this;
}
CDataStream& CDataStream::operator<<(PUInt16 i)
{
PUInt16 temp = i;
if (m_bigEndian){
temp = reverseNumber(i);
}
m_buffer->writeRawData((char*)&temp, sizeof(temp));
return *this;
}
CDataStream& CDataStream::operator<<(PInt32 i)
{
PInt32 temp = i;
if (m_bigEndian){
temp = reverseNumber(i);
}
m_buffer->writeRawData((char*)&temp, sizeof(temp));
return *this;
}
CDataStream& CDataStream::operator<<(PUInt32 i)
{
PUInt32 temp = i;
if (m_bigEndian){
temp = reverseNumber(i);
}
m_buffer->writeRawData((char*)&temp, sizeof(temp));
return *this;
}
CDataStream& CDataStream::operator<<(PInt64 i)
{
PInt64 temp = i;
if (m_bigEndian){
temp = reverseNumber(i);
}
m_buffer->writeRawData((char*)&temp, sizeof(temp));
return *this;
}
CDataStream& CDataStream::operator<<(PUInt64 i)
{
PUInt64 temp = i;
if (m_bigEndian){
temp = reverseNumber(i);
}
m_buffer->writeRawData((char*)&temp, sizeof(temp));
return *this;
}
CDataStream& CDataStream::operator<<(PFloat32 i)
{
PFloat32 temp = i;
if (m_bigEndian){
union {
float val1;
PUInt32 val2;
} x;
x.val1 = i;
x.val2 = reverseNumber(x.val2);
m_buffer->writeRawData((char*)&x.val2, sizeof(i));
}else{
m_buffer->writeRawData((char*)&temp, sizeof(i));
}
return *this;
}
CDataStream& CDataStream::operator<<(PFloat64 i)
{
PFloat64 temp = i;
if (m_bigEndian){
union {
double val1;
PUInt64 val2;
} x;
x.val1 = i;
x.val2 = reverseNumber(x.val2);
m_buffer->writeRawData((char*)&x.val2, sizeof(i));
}else{
m_buffer->writeRawData((char*)&temp, sizeof(i));
}
return *this;
}
CDataStream& CDataStream::operator<<(const char* i)
{
if (!i) {
*this << (PUInt32)0;
return *this;
}
int len = strlen(i) + 1;
*this << (PUInt32)len;
m_buffer->writeRawData(i, len);
return *this;
}
CDataStream& CDataStream::operator>>(bool& i)
{
PInt8 v;
*this >> v;
i = !!v;
return *this;
}
CDataStream& CDataStream::operator>>(PInt8& i)
{
m_buffer->readRawData(&i, 1);
return *this;
}
CDataStream& CDataStream::operator>>(PUInt8& i)
{
m_buffer->readRawData(reinterpret_cast<char *>(&i), sizeof(i));
return *this;
}
CDataStream& CDataStream::operator>>(PInt16& i)
{
m_buffer->readRawData((char *)(&i), sizeof(i));
if (m_bigEndian){
i = reverseNumber(i);
}
return *this;
}
CDataStream& CDataStream::operator>>(PUInt16& i)
{
m_buffer->readRawData(reinterpret_cast<char *>(&i), sizeof(i));
if (m_bigEndian){
i = reverseNumber(i);
}
return *this;
}
CDataStream& CDataStream::operator>>(PInt32& i)
{
m_buffer->readRawData((char *)(&i), sizeof(i));
return *this;
}
CDataStream& CDataStream::operator>>(PUInt32& i)
{
m_buffer->readRawData(reinterpret_cast<char *>(&i), sizeof(i));
if (m_bigEndian){
i = reverseNumber(i);
}
return *this;
}
CDataStream& CDataStream::operator>>(PInt64& i)
{
m_buffer->readRawData((char *)(&i), sizeof(i));
if (m_bigEndian){
i = reverseNumber(i);
}
return *this;
}
CDataStream& CDataStream::operator>>(PUInt64& i)
{
m_buffer->readRawData(reinterpret_cast<char *>(&i), sizeof(i));
if (m_bigEndian){
i = reverseNumber(i);
}
return *this;
}
CDataStream& CDataStream::operator>>(PFloat32& i)
{
i = 0.0f;
m_buffer->readRawData(reinterpret_cast<char *>(&i), sizeof(i));
if (m_bigEndian){
union {
float val1;
PUInt32 val2;
} x;
x.val2 = reverseNumber(*reinterpret_cast<PUInt32 *>(&i));
i = x.val1;
}
return *this;
}
CDataStream& CDataStream::operator>>(PFloat64& i)
{
i = 0.0f;
m_buffer->readRawData(reinterpret_cast<char *>(&i), sizeof(i));
if (m_bigEndian){
union {
double val1;
PUInt64 val2;
} x;
x.val2 = reverseNumber(*reinterpret_cast<PUInt64 *>(&i));
i = x.val1;
}
return *this;
}
CDataStream& CDataStream::operator>>(char*& i)
{
PUInt32 len = 0;
*this >> len;
if (len == 0)
return *this;
m_buffer->readRawData(i, len);
i[len] = '\0';
return *this;
}
CDataStream& CDataStream::operator<<(const CByteArray& data)
{
m_buffer->writeRawData(data.data(), data.size());
return *this;
}
CDataStream& CDataStream::operator>>(CByteArray& data)
{
PUInt32 len = 0;
*this >> len;
if (len == 0)
return *this;
std::unique_ptr<char[]> pBuffer(new char[len]);
m_buffer->readRawData(pBuffer.get(), len);
data.append(pBuffer.get(), len);
return *this;
}
int CDataStream::writeRawData(const char* data, PUInt32 size)
{
return m_buffer->writeRawData(data, size);
}
int CDataStream::readRawData(char* data, PUInt32 size)
{
return m_buffer->readRawData(data, size);
}
bool CDataStream::atEnd() const
{
return m_buffer->bytesAvailable() <= 0;
}
int CDataStream::bytesAvailable() const
{
return m_buffer->bytesAvailable();
}
int CDataStream::skipRawData(int len)
{
return m_buffer->skip(len);
}4.使用實例
結構體定義:
//傳輸?shù)恼麄€報文
typedef struct _stShortWavePacket
{
//共用
PUInt32 contextID;
void* pAppData; //正文數(shù)據(jù)
PUInt16 frameIndex;
stObjectFlag srcObjMark;
stObjectFlag destObjMark;
PUInt8 isMustResponse;
PUInt8 responseStatus;
public:
_stShortWavePacket() {
}
}stShortWavePacket;
using stChannelWorkParamData = KVData<PUInt16, PUInt64>;
using ChannelWorkParamDataContainer = std::vector<std::shared_ptr<stChannelWorkParamData>>;
//結構體定義
template <bool isHaveType>
struct stChannelWorkParam
{
PUInt16 signalType; //類別
ChannelWorkParamDataContainer workParamData;
public:
stChannelWorkParam() {
signalType = 0;
}
stChannelWorkParam(const stChannelWorkParam&) = delete;
stChannelWorkParam& operator=(const stChannelWorkParam&) = delete;
PUInt16 getDataSize() {
PUInt16 size = isHaveType ? 2 : 0;
for (auto& it : workParamData) {
size += it->getSize();
}
return size;
}
std::string toString() const {
return std::string("type:") + numConvertString(signalType);
}
friend CDataStream& operator<<(CDataStream& dataStream, const stChannelWorkParam& data)
{
if (isHaveType) {
dataStream << data.signalType;
}
for (auto& it : data.workParamData) {
it->serializeData(dataStream);
}
return dataStream;
}
friend CDataStream& operator>>(CDataStream& dataStream, stChannelWorkParam& data)
{
if (isHaveType) {
dataStream >> data.signalType;
}
while (dataStream.bytesAvailable() > 6) {
std::shared_ptr<stChannelWorkParamData> pData = std::make_shared<stChannelWorkParamData>();
pData->unserializeData(dataStream);
data.workParamData.push_back(pData);
}
return dataStream;
}
};
using stSendChannelWorkParam = stChannelWorkParam<true>;
using stUploadRegisterStatus = stChannelWorkParam<false>;序列化數(shù)據(jù):
template <PUInt32 type, typename stType>
int encodeData(char* data, int& len, const stShortWavePacket* pPacket)
{
PUInt16 crcCode = 0;
CByteArray byArray;
CDataStream dataStream(&byArray);
stType* pMessage = reinterpret_cast<stType*>(pPacket->pAppData);
if (pMessage == nullptr)
return 1;
//[1]
dataStream.writeRawData((const char*)m_startFlag, sizeof(m_startFlag));
dataStream << getFrameNo();
dataStream << pPacket->srcObjMark.data;
dataStream << pPacket->destObjMark.data;
dataStream << pPacket->isMustResponse;
dataStream << (PUInt8)GET_WORK_TYPE(type);
dataStream << pMessage->getDataSize();
//[2]
dataStream << (*pMessage);
crcCode = getCRCCode(byArray.data(), byArray.size());
dataStream << crcCode;
dataStream.writeRawData((const char*)m_endFlag, sizeof(m_endFlag));
len = byArray.size();
memcpy(data, byArray.data(), len);
return 0;
}接收到網(wǎng)絡數(shù)據(jù),反序列化數(shù)據(jù):
template <PUInt32 type, typename stType>
int parseData(const char* data, int len, stShortWavePacket* pPacket)
{
CByteArray byArray(data, len);
CDataStream dataStream(&byArray);
PUInt16 length = 0;
pPacket->contextID = type;
dataStream.skipRawData(4); //忽略4個字節(jié)
dataStream >> pPacket->frameIndex;
m_frameNo = pPacket->frameIndex;
dataStream >> pPacket->srcObjMark.data;
dataStream >> pPacket->destObjMark.data;
dataStream >> pPacket->isMustResponse; //是否應答
dataStream.skipRawData(1); //忽略功能指令,1個字節(jié)
dataStream >> length;
if (length != stType::getDataSize())
return -1;
std::unique_ptr<stType> pAppData = std::make_unique<stType>();
dataStream >> (*pAppData);
pPacket->pAppData = pAppData.release();
return 0;
}到此這篇關于C++實現(xiàn)數(shù)據(jù)的序列化和反序列化詳解的文章就介紹到這了,更多相關C++數(shù)據(jù)序列化和反序列化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Visual?C++?6.0添加一個對話框的實現(xiàn)步驟
VC6.0是微軟公司推出的一款集成開發(fā)環(huán)境,本文主要介紹了Visual?C++?6.0添加一個對話框的實現(xiàn)步驟,具有一定的參考價值,感興趣的可以了解一下2024-06-06
C++?RBTree紅黑樹的性質(zhì)與實現(xiàn)
紅黑樹是一種二叉搜索樹,但在每個結點上增加一個存儲位表示結點的顏色,可以是Red或Black;通過對任何一條從根到葉子的路徑上各個結點著色方式的限制,紅黑樹確保沒有一條路徑會比其他路徑長出倆倍,因而是平衡的2023-03-03
linux下C語言中的mkdir函數(shù)與rmdir函數(shù)
以下是對C語言中的mkdir函數(shù)與rmdir函數(shù)進行了詳細的分析介紹,需要的朋友可以過來參考下2013-08-08
C++ std::Set<std::pair>的實現(xiàn)示例
本文主要介紹了C++ std::Set<std::pair>的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2025-10-10

