You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

读取QJsonDocument如何避免额外拷贝?代码中哪些操作是拷贝?

关于QJsonDocument读取时减少拷贝的疑问与优化方案

问题背景

尝试读取QJsonDocument时希望尽量减少数据拷贝次数,看到他人示例代码仅读取操作就存在2-3次拷贝,但无法区分哪些操作是拷贝文档数据,哪些本质是指向文档的引用。自身对相关类的理解可能存在偏差:

  • QJsonDocument类似顶层QJsonObject,提供文档级成员函数,存储整个文档。
  • QJsonObject是有序树形对象,节点对应键,原以为它会拷贝所指向的文档片段,这显然低效。
  • QJsonValueRef类似QJsonObject的引用,但不理解为何不用迭代器替代,且使用它似乎难以避免拷贝QJsonObject内容。

需要读取的JSON示例:

{
    "FirstName": "John",
    "LastName": "Doe",
    "MiddleName": null,
    "Age": 43,

    "Address": {
        "Street": "Downing Street 10",
        "City": "London",
        "Country": "Great Britain"
    },
    "Phone numbers": [
        "+44 1234567",
        "+44 2345678"
    ],
    "Valid":true
}

参考的解决方案代码(行编号用于提问):

QJsonObject jsonObj = jsonDoc.object(); // (1)
QTextStream textStream(stdout);

QJsonValue jsonVal = jsonObj.value(QString("Address")); // (2)
QJsonObject jsonItemObj = jsonVal.toObject(); // (3)

QJsonValue jsonSubVal;

jsonSubVal = jsonItemObj["Street"]; // (4)
textStream << jsonSubVal.toString() << endl; // (5)

jsonSubVal = jsonItemObj["City"];
textStream << jsonSubVal.toString() << endl;

jsonSubVal = jsonItemObj["Country"];
textStream << jsonSubVal.toString() << endl;

疑问:(1)-(5)中哪些操作属于拷贝?有没有办法消除这些拷贝?(注:(5)中仅指jsonSubVal.toString()而非textStream <<操作)


分析与优化方案

哪些操作是拷贝?

首先明确Qt的QJson系列类核心机制:QJsonDocument、QJsonObject、QJsonArray均基于隐式共享(Implicit Sharing,写时复制)的容器类,仅当修改指向的数据时才会触发实际拷贝,单纯读取操作大多只是复制轻量级容器对象(内部为指向数据的指针,开销极小),具体分析如下:

  • 行(1):QJsonObject jsonObj = jsonDoc.object();
    jsonDoc.object()返回QJsonObject,因隐式共享机制,此操作仅复制轻量级对象(内部指针指向原文档数据),未拷贝实际JSON数据,仅后续修改jsonObj时才会触发拷贝。
  • 行(2):QJsonValue jsonVal = jsonObj.value(QString("Address"));
    jsonObj.value()返回QJsonValue,这是一个轻量级封装类型,要么直接存储简单值,要么存储指向复杂类型(如QJsonObject/QJsonArray)的指针,未拷贝Address对象的实际数据,仅获取原数据的引用封装。
  • 行(3):QJsonObject jsonItemObj = jsonVal.toObject();
    同样基于隐式共享,复制的QJsonObject仅为指向原Address数据的轻量级容器,未拷贝实际JSON键值对数据,修改时才会触发拷贝。
  • 行(4):jsonSubVal = jsonItemObj["Street"];
    jsonItemObj["Street"]返回QJsonValueRef(左值场景),赋值给QJsonValue时会转换为轻量级封装,未拷贝Street的字符串数据,仅引用原数据。
  • 行(5):jsonSubVal.toString()
    此操作会拷贝字符串数据,因QString本身为隐式共享类型,toString()返回新的QString。若仅用于输出,该拷贝难以完全避免(Qt未提供直接获取原JSON字符串引用的接口)。

如何消除不必要的拷贝?

虽然上述大部分操作未实际拷贝JSON数据,但可通过引用或链式调用简化代码,减少多余中间对象:

优化后的简洁版本

// 用const引用接收顶层对象,避免轻量级容器复制(开销极小,但更严谨)
const QJsonObject& jsonObj = jsonDoc.object();
QTextStream textStream(stdout);

// 链式调用跳过中间变量
textStream << jsonObj["Address"].toObject()["Street"].toString() << endl;
textStream << jsonObj["Address"].toObject()["City"].toString() << endl;
textStream << jsonObj["Address"].toObject()["Country"].toString() << endl;

更安全的版本(避免键不存在导致的空值问题)

const QJsonObject& jsonObj = jsonDoc.object();
const QJsonValue addressVal = jsonObj.value("Address");
if (addressVal.isObject()) {
    const QJsonObject& addressObj = addressVal.toObject();
    textStream << addressObj.value("Street").toString() << endl;
    textStream << addressObj.value("City").toString() << endl;
    textStream << addressObj.value("Country").toString() << endl;
}

关键优化点:

  • 使用**const QJsonObject&**接收jsonDoc.object()和addressVal.toObject()的返回值,避免轻量级容器对象的复制。
  • 链式调用减少不必要的中间变量创建。
  • 若需多次复用字符串,可存储为const QString&避免多次拷贝:
    const QString& street = addressObj.value("Street").toString();
    textStream << street << endl;
    // 后续复用street时无需再次拷贝
    

关于QJsonValueRef的补充说明

QJsonValueRef并非用来替代迭代器,它是使用operator[]修改QJsonObject值时的代理类:例如jsonObj["Key"] = 123,jsonObj["Key"]返回的QJsonValueRef会直接修改原对象数据,避免拷贝整个QJsonObject。而迭代器用于遍历QJsonObject的键值对,两者用途不同。


内容的提问来源于stack exchange,提问作者Bobby

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 13:44:54