首页 > 其他分享 >深入理解Qt的隐式共享机制

深入理解Qt的隐式共享机制

时间:2024-07-02 17:30:27浏览次数:23  
标签:Qt 隐式 QString sb 共享 sa 内存

在Qt中,一个关键的性能优化特性是其数据结构的隐式共享机制,这在Qt的文档和API中常被称为“隐式共享”或“写时复制(Copy-On-Write, COW)”。本文将详细介绍这一机制,并通过QString类的实现代码和相应的反汇编代码来阐释其工作原理。

隐式共享的定义和优点

隐式共享是一种内存管理策略,它允许多个对象共享相同的数据副本,直到某个对象需要修改这些数据时才进行实际的数据复制。这种策略的优点包括:

  • 减少内存使用:多个对象可以共享同一个数据副本,而不是每个对象都持有一个数据副本。
  • 提高性能:减少数据复制的需要,从而降低程序的总体资源消耗。
  • 代码简化:开发者不需要关心数据如何被共享或复制,所有这些都由Qt框架自动管理。

隐式共享在Qt中的实现

在Qt中,很多基本数据类型,如QString、QList、QByteArray等,都实现了隐式共享。以下是QString类中赋值操作符的源码,可以直观了解隐式共享机制的实现:

QString &QString::operator=(const QString &other) noexcept
{
    other.d->ref.ref();
    if (!d->ref.deref())
        Data::deallocate(d);
    d = other.d;
    return *this;
}

代码解释

  1. other.d->ref.ref();

增加源对象(other)数据的引用计数。
因为在赋值后,目标对象和源对象都引用了相同的内存数据,所以对应的引用计数需要增加。

  1. if (!d->ref.deref()) Data::deallocate(d);

通过deref()函数递减目标对象(d)的引用计数,并返回递减后是否仍有引用(如果还有其他引用,则返回true;如果此次操作后引用计数变为0,则返回false)。如果返回false,说明没有其他QString对象再引用这块数据了,那就需要调用Data::deallocate()来释放这块内存。
因为目标对象在赋值后将指向新的内存数据,那么原来的数据引用计数需要递减,如果递减后变成0,那就说明没有对象在使用这块内存,需要释放,否则会导致内存泄漏。

反汇编代码示例说明

源码

QString sa = "hello";
QString sb = sa;
sb[0] = 'H';

反汇编

    QString sa = "hello";
00007FF65A72461B  lea         rdx,[__xt_z+214h (07FF65A729CB4h)]  
00007FF65A724622  lea         rcx,[sa]  
00007FF65A724627  call        qword ptr [__imp_QString::QString (07FF65A72F170h)]  
00007FF65A72462D  nop  
    QString sb = sa;
00007FF65A72462E  lea         rdx,[sa]  
00007FF65A724633  lea         rcx,[sb]  
00007FF65A724638  call        qword ptr [__imp_QString::QString (07FF65A72F188h)]  
00007FF65A72463E  nop  
    sb[0] = 'H';
00007FF65A72463F  xor         r8d,r8d  
00007FF65A724642  lea         rdx,[rsp+88h]  
00007FF65A72464A  lea         rcx,[sb]  
00007FF65A72464F  call        qword ptr [__imp_QString::operator[] (07FF65A72F178h)]  
00007FF65A724655  mov         dl,48h  
00007FF65A724657  mov         rcx,rax  
00007FF65A72465A  call        qword ptr [__imp_QCharRef::operator= (07FF65A72F168h)]

详细说明

  1. 在执行QString sb = sa;代码是,反汇编代码可以看出,先将sasb两个对象的地址加载到两个寄存器中,然后调用QString的拷贝构造函数。
  2. 在执行sb[0] = 'H';时,反汇编代码的最后一行代码,调用了QCharRef::operator=()赋值操作符,那么我们需要知道该赋值操作符具体做了什么:
inline QCharRef &operator=(QChar c)
{
    using namespace QtPrivate::DeprecatedRefClassBehavior;
    if (Q_UNLIKELY(i >= s.d->size)) {
#ifdef QT_DEBUG
        warn(WarningType::OutOfRange, EmittingClass::QCharRef);
#endif
        s.resize(i + 1, QLatin1Char(' '));
    } else {
#ifdef QT_DEBUG
        if (Q_UNLIKELY(!s.isDetached()))
            warn(WarningType::DelayedDetach, EmittingClass::QCharRef);
#endif
        s.detach();
    }
    s.d->data()[i] = c.unicode();
    return *this;
}

我们看到上面源码中if (Q_UNLIKELY(i >= s.d->size))判断下标有没有超出字符串大小,如果超出了,则需要调用resize重新开辟一块内存,如果没有超出,则调用detach()来分离数据,在分离的时候会判断当前引用计数,如果计数大于1,则会创建新的副本数据进行修改。

标签:Qt,隐式,QString,sb,共享,sa,内存
From: https://blog.csdn.net/LeoLei8060/article/details/140132244

相关文章

  • 效率升级,从NAS开始:存储、查找、共享一键完成
    在数字化时代,数据已经成为企业和个人最宝贵的资产之一。随着信息量的爆炸性增长,如何高效地存储、查找和共享数据成为了一个亟待解决的问题。铁威马F4-424作为一款高性能的NAS设备,以其卓越的性能和丰富的功能,为用户提供了一站式的数据管理解决方案,让效率飞升成为可能。一、大容......
  • steam游戏商城怎么共享游戏给好友?最详细的操作方法介绍
    在Steam平台上共享游戏给好友,实际上是通过Steam的家庭图书馆共享功能实现的。这允许你在一个家庭内与最多五位家庭成员共享你的游戏库,但他们必须使用同一台电脑。请注意,你不能直接将游戏共享给不在同一物理位置的好友。以下是启用家庭图书馆共享的步骤:1.登录Steam:首先,确保你......
  • Qt/C++开发经验小技巧296-300
    使用QDir::setCurrent设置当前目录后,会影响程序中的所有相对目录的执行,导致可能的意外发生,一般相对目录都默认是可执行文件所在目录,所以如果程序中为了特殊处理临时调用了QDir::setCurrent设置过相对目录,建议处理完成以后立即切换回来。QDir::setCurrent("f:/");QImageimg(":......
  • Qt QTableView设置自适应行高、列宽、行样式
    1、QTableView设置自适应行高ui->tableViewMonitor->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);//自动设置行高2、QTableView设置自适应列宽ui->tableViewMonitor->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents)......
  • Qt QTableWidget resizeRowsToContents非常慢
    QTableWidget是Qt框架中的一个表格控件,用于显示和编辑表格数据。resizeRowsToContents是QTableWidget的一个函数,用于自动调整表格行的高度以适应内容。该函数在某些情况下可能会导致性能问题,特别是当表格中的行数较多或者表格中的内容较复杂时。这是因为该函数需要遍......
  • QT6.7.2 MSVC源码编译 静态库 动态库
    QT6.7.2MSVC源码编译静态库动态库也可以参考官方的文档https://doc.qt.io/qt-6/build-sources.html环境搭建为了操作更有可复制性,这里在虚拟机中采用全新安装的系统进行配置。系统镜像为:en-us_windows_10_enterprise_ltsc_2021_x64_dvd_d289cf96_2.iso安装VisualStudio......
  • 逆向通达信 x 逆向微信 x 逆向Qt
    本篇在博客园地址https://www.cnblogs.com/bbqzsl/p/18252961本篇内容包括:win32窗口嵌入QtUI。反斗玩转signal-slot。最后 通达信x微信xQt 做手术。 QtAlienWidget是一种广义的DirectUI。在UI技术中,DirectUI和AlienWidget的概念有所重叠,但具体实现和应用场景有所......
  • Qt:6.QWidget属性介绍(windowTitle、windowIcon、windowOpacity)以及QRC机制
    一、windowTitle属性-窗口标题:1.1windowTitle属性介绍:在Qt中,windowTitle属性是QWidget类提供的一个属性,用于设置和获取窗口的标题文本。它通常用于设置顶级窗口的标题栏显示内容。1.2设置窗口标题——setWindowTitle():widget->setWindowTitle("这是窗口标题");1......
  • C++实现简化版Qt的QObject(3):增加父子关系、属性系统
    前几天写了文章:C++实现一个简单的Qt信号槽机制C++实现简化版Qt信号槽机制(2):增加内存安全保障之后感觉还不够过瘾,Qt中的QObject体系里还有不少功能特性没有实现。为了提高QObject的还原度,今天我们将父子关系、属性系统等功能也一并实现。接口设计首先,我们设计一下我们的......
  • QT 读取Json 配置文件
    {QStringProjectPath="";QStringConfigPath=".json";constchar*PathEnvVar=getenv("");if(PathEnvVar!=nullptr){ProjectPath=PathEnvVar;ConfigPath=ProjectPath+"/.json&quo......