Qt自定义序列化

本文介绍了如何在Qt中使用QDataStream类进行自定义序列化,特别是针对包含复杂数据结构如QMap的情况。通过重载结构体的序列化和反序列化函数,确保序列化顺序与反序列化一致,并在序列化QMap时添加附加信息记录其元素数量,以实现正确地读写二进制数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文转载自http://c.360webcache.com/c?m=50ecbbdfdc5c3633e26b54f264753e3c&q=QMap%E5%BA%8F%E5%88%97%E5%8C%96&u=https%3A%2F%2Fwww.cnblogs.com%2Fkanyun%2Fp%2F13093662.html

QDateStream类

在Qt中,提供了QDataStream类为我们的程序提供了读写二进制数据的能力。
QDataStream类实现了序列化C++的基本数据类型的功能,比如char,short,int,char* 等等,不但如此还可以直接序列化 QMap ,QList之类的容器(需要保证容器内的元素是基本类型元素)。但是往往程序中包含了复杂的数据结构,此时就不能直接进行序列化了。因此我们需要将复杂数据类型分解成独立的基本数据类型分别进行序列化。

示例

typedef struct ProjectInfo {
   
	QString projectName;
	QString imgPath;
	QString annotationMeta;
	QString createTime;
	int currentImgIndex;
	QMap<QString,QList<RectMeta>> markCollection;
} Project;
Q_DECLARE_METATYPE(ProjectInfo);
typedef struct RectMetaInfo{
   
	QString text;
	qreal x;
	qreal y;
	qreal w;
	qreal h;
} RectMeta;
Q_DECLARE_METATYPE(RectMetaInfo);

上述代码包含了两个结构体,

其中一个结构体RectMetaInfo中,全部都是基本类型,

而另外一个结构体ProjectInfo,则包含了基本类型和复杂的容器类型

此时我如果直接序列化结构体ProjectInfo,显然是行不通的,因此我们需要逐步分解序列化。

首先结构体ProjectInfo包含了结构体RectMetaInfo,那么先序列化RectMetaInfo

由于RectMetaInfo中都是基本类型,所以序列化比较简单。需要注意的是序列化的顺序要和反序列化的数据的顺序保持一致

//重载序列化
inline QDataStream &operator<<(QDataStream &output , const RectMetaInfo &metaInfo)
{
   
output << metaInfo.text << metaInfo.x << metaInfo.y << metaInfo.w << metaInfo.h;
return output;
}
//重载反序列化
inline QDataStream &operator>>(QDataStream &input , RectMetaInfo &metaInfo)
{
   
input >> metaInfo.text >> metaInfo.x >> metaInfo.y >> metaInfo.w >> metaInfo.h;
return input;
}

重载了结构体RectMetaInfo的序列化和反序列化,接下来就要重载结构体ProjectInfo。

由于ProjectInfo包含了QMap容器元素,因此我们需要一个将QMap的元素个数保存起来(添加附加信息)

//重载序列化
inline QDataStream &operator<<(QDataStream &output , const ProjectInfo &pj)
{
   
output << pj.projectName << pj.imgPath << pj.annotationMeta << pj.createTime << pj.currentImgIndex;
// 附加信息 QMap<int,int> 可以直接被序列化(QMap能否被直接序列化,要看QMap中的类型是否是基本类型,如果是,就可以直接序列化),第一个参数表示第几个key,第二个参数表示该key下存放的数据的数量
QMap<int,int> collectionPreview;
int k_index = 0;
foreach(QString key,pj.markCollection.keys()){
   
collectionPreview[k_index] = pj.markCollection[key].size();
k_index++;
}
// 序列化QMap时需要注意,头文件中需要引入 QDataStream 依赖,否则此处报错
output << collectionPreview;
foreach(QString key,pj.markCollection.keys()){
   
output << key;
QList<RectMetaInfo> rectMetas = pj.markCollection[key];
if (rectMetas.size() == 0) continue;
for(RectMetaInfo rectMetaInfo : rectMetas){
   
output << rectMetaInfo;
};
}
return output ;
}
### Qt自定义类型的序列化方法 为了使自定义类型能够在Qt中被序列化,需遵循特定的步骤来确保该类型能够正确地保存到数据流并从中恢复。下面是一个完整的例子展示如何实现这一过程。 #### Block 类定义 在 `block.h` 文件中声明一个名为 `Block` 的类,并利用宏 `Q_DECLARE_METATYPE(Block)` 来告知 `QMetaType` 关于此新类型的细节[^2]: ```cpp #ifndef BLOCK_H #define BLOCK_H #include <QString> #include <QDataStream> class Block { public: QString name; int value; Block(const QString &name = "", int value = 0); }; // 声明 Block 类型给 QMetaType 系统知晓 Q_DECLARE_METATYPE(Block) #endif // BLOCK_H ``` #### 实现构造函数和其他必要的成员函数 接着,在 `block.cpp` 文件里提供上述类的具体实现,包括默认构造函数以及其他可能需要用到的方法: ```cpp #include "block.h" Block::Block(const QString &name, int value): name(name), value(value) {} ``` #### 添加序列化支持 为了让 `Block` 对象可以被序列化和反序列化,还需要重载全局作用域内的 `<<` 和 `>>` 运算符以便它们能处理 `Block` 类型的数据。这通常是在 `.cpp` 文件内完成的: ```cpp QDataStream &operator<<(QDataStream &out, const Block &obj){ out << obj.name << obj.value; return out; } QDataStream &operator>>(QDataStream &in, Block &obj){ in >> obj.name >> obj.value; return in; } ``` 以上代码片段展示了怎样向 `QDataStream` 输出 `Block` 对象以及从 `QDataStream` 输入 `Block` 对象的过程。这里假设 `Block` 类只包含两个公共属性——字符串形式的名字 (`name`) 和整数形式的数值 (`value`);对于更复杂的类,则需要相应调整这些操作符以适应其内部结构[^3]。 当一切准备就绪之后,就可以像对待内置基本类型一样轻松地对 `Block` 实例执行序列化/反序列化的操作了。例如,在某个地方你可以这样做: ```cpp void saveToFile(const Block &blk, const QString &fileName){ QFile file(fileName); if (!file.open(QIODevice::WriteOnly)) throw std::runtime_error("Cannot open file for writing"); QDataStream out(&file); out.setVersion(QDataStream::Qt_5_14); // 设置版本号很重要! out << blk; // 使用已定义好的运算符进行序列化 } Block loadFromFile(const QString &fileName){ QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) throw std::runtime_error("Cannot open file for reading"); QDataStream in(&file); in.setVersion(QDataStream::Qt_5_14); Block blk; in >> blk; // 同样使用之前定义的操作符来进行反序列化 return blk; } ``` 这段程序说明了如何将 `Block` 对象存储至文件或将它从文件加载回来。注意每次创建新的 `QDataStream` 实例时都应当指定相同的版本号,这样即使将来库有所更新也不会影响现有二进制格式的一致性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qfl_sdu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值