Qt智能指针

Qt智能指针

Qt中常用的智能指针主要包括:QPointerQSharedPointerQScopedPointerQSharedDataPointerQWeakPointer

QPointer 是一个模板类,专用于指向 QObject 或其派生类的对象。它是一个弱引用指针,当所指对象被删除时,将自动设置为 nullptr

主要特性

  • 仅用于 QObject 派生类

  • 不拥有对象的所有权

  • 自动检测对象删除

  • 线程安全(在对象所在线程)

    #pragma once

    #include <QtWidgets/QMainWindow>
    #include "ui_QtPointerTest.h"
    #include <qlabel.h>
    #include <qpointer.h>
    #include <qstring.h>
    #include <qtimer.h>

    #ifndef LoacalStr
    #define LocalStr(str) QString::fromLocal8Bit((str))
    #endif // !LoacalStr

    class QtPointerTest : public QMainWindow
    {
    Q_OBJECT

    public:
    QtPointerTest(QWidget parent = nullptr);
    ~QtPointerTest();
    private slots:
    void onTimeOut();
    private:
    Ui::QtPointerTestClass ui;
    QPointer<QLabel> mp_lb;
    QLabel
    mp_lb2;

    QTimer* mp_timer;
    };

    #include "QtPointerTest.h"
    #include <qboxlayout.h>

    QtPointerTest::QtPointerTest(QWidget parent)
    : QMainWindow(parent), mp_lb(nullptr)
    {
    ui.setupUi(this);
    mp_timer = new QTimer(this);
    connect(mp_timer, &QTimer::timeout,this,&QtPointerTest::onTimeOut);
    mp_timer->start(2000);
    //ui.centralWidget
    QHBoxLayout
    hLy = new QHBoxLayout(ui.centralWidget);
    ui.centralWidget->setLayout(hLy);

    mp_lb = new QLabel(LoacalStr("测试QPointer 是否会自动置空!"), ui.centralWidget);

    mp_lb2 = new QLabel(LoacalStr("检测中。。。。"), ui.centralWidget);
    hLy->addWidget(mp_lb);
    hLy->addWidget(mp_lb2);
    }
    void QtPointerTest::onTimeOut() {
    if(mp_lb)
    delete mp_lb;
    if (!mp_lb) {
    mp_lb2->setText(LoacalStr("QPointer 为空!"));
    }
    else
    mp_lb2->setText(LoacalStr("QPointer 未被置空!"));
    }
    QtPointerTest::~QtPointerTest()
    {}

QSharedPointer 是一个模板类,实现了共享所有权机制。

  • 线程安全
  • 适用场景:多线程共享数据

QScopedPointer 是一个模板类,实现了独享所有权机制。

  • 线程安全
  • 适用场景:局部作用域资源管理

QWeakPointer 是一个模板类,提供弱引用功能。

QSharedDataPointer 用于实现**隐式共享(Implicit Sharing)写时复制(Copy-on-Write, COW)**机制,其核心目标是兼顾值语义的安全性、引用语义的高效性,并通过减少不必要的深拷贝开销来提升程序性能。

  • 管理对象 :必须是 QSharedData 子类
  • 拷贝行为:浅拷贝(仅复制指针 + 增加引用计数)
  • 写操作:自动 detach(引用 > 1 时执行深拷贝)

底层原理

  • QSharedData基类:提供线程安全的引用计数,所有共享数据类必须继承自它
  • detach()操作 :写时复制的核心动作,当执行非const成员函数且引用计数 > 1时,自动创建独立的数据副本

工作流程

  1. 首次创建对象:分配数据块,引用计数 = 1
  2. 拷贝对象:仅复制指针,引用计数 + 1(浅拷贝,高效)
  3. 修改对象
    • 若引用计数 = 1:直接修改原数据,无额外开销
    • 若引用计数 > 1:执行 detach(),深拷贝数据块,引用计数相应调整,修改新副本
  4. 对象销毁:引用计数 - 1,计数 = 0 时释放数据块

步骤 1:编写共享数据类(继承 QSharedData)

这个类是私有数据载体,所有变量都放这里,自动管理引用计数。

复制代码
#pragma once
#include <QSharedData>
#include <QString>

class EmployeeData : public QSharedData {
public:
// 构造函数(默认/带参)
EmployeeData() : id(0) {}
EmployeeData(int id, const QString& name) : id(id), name(name) {}
// 拷贝构造函数(必须实现,用于detach时深拷贝)
EmployeeData(const EmployeeData& other) : QSharedData(other), id(other.id), name(other.name) {}
~EmployeeData() {} // 无需手动管理计数,基类自动处理

// 数据成员(通常为public,供外层类访问)
int id;
QString name;
};

步骤 2:编写对外接口类(持有 QSharedDataPointer)

这个类是给外部调用的,无需手动写拷贝构造、赋值、析构,智能指针自动处理!

复制代码
#pragma once
#include <QSharedDataPointer>
#include "EmployeeData.h"

class Employee {
public:
// 构造函数(创建新数据块)
Employee() : d(new EmployeeData) {}
Employee(int id, const QString& name) : d(new EmployeeData(id, name)) {}
// 拷贝构造/赋值:自动浅拷贝+计数递增(无需手动实现)
Employee(const Employee& other) = default;
Employee& operator=(const Employee& other) = default;
~Employee() = default; // 自动管理计数,无需手动释放

// 读取接口(const,不触发detach)
int id() const { return d->id; }
QString name() const { return d->name; }

// 写入接口(非const,自动触发detach)
void setId(int id) { d->id = id; }
void setName(const QString& name) { d->name = name; }

private:
// d指针:QSharedDataPointer管理共享数据
QSharedDataPointer<EmployeeData> d;
};
#include <QSharedDataPointer>

#include "EmployeeData.h"

class Employee {
public:
    // 构造函数(创建新数据块)
    Employee() : d(new EmployeeData) {}
Employee(int id, const QString& name) : d(new EmployeeData(id, name)) {}
    // 拷贝构造/赋值:自动浅拷贝+计数递增(无需手动实现)
    Employee(const Employee& other) = default;
Employee& operator=(const Employee& other) = default;
    ~Employee() = default; // 自动管理计数,无需手动释放

    // 读取接口(const,不触发detach)
    int id() const { return d->id; }
    QString name() const { return d->name; }

    // 写入接口(非const,自动触发detach)
    void setId(int id) { d->id = id; }
void setName(const QString& name) { d->name = name; }

private:
    // d指针:QSharedDataPointer管理共享数据
QSharedDataPointer<EmployeeData> d;
};

#include <qstring.h>
#ifndef  LocalStr
#define LocalStr(s) QString::fromLocal8Bit((s))
#endif // ! 


int main() {
// 创建第一个对象
Employee emp1(1001, LocalStr("张三"));
qDebug() << "emp1:" << emp1.id() << emp1.name(); // emp1: 1001 "张三"

// 拷贝对象(浅拷贝,高效)
Employee emp2 = emp1;
qDebug() << "emp2:" << emp2.id() << emp2.name(); // emp2: 1001 "张三"

// 修改emp2(触发detach,深拷贝)
emp2.setId(1002);
emp2.setName(LocalStr("李四"));
qDebug() << LocalStr("is changed: ");
qDebug() << "emp1:" << emp1.id() << emp1.name(); // emp1: 1001 "张三"(不受影响)
qDebug() << "emp2:" << emp2.id() << emp2.name(); // emp2: 1002 "李四"(独立修改)

return 0;
}
相关推荐
liuyao_xianhui2 小时前
优选算法_堆_最后一块石头的重量_C++
java·开发语言·c++·算法·链表
上天_去_做颗惺星 EVE_BLUE2 小时前
Linux Core Dump 测试操作手册
linux·c++·测试工具
羊小猪~~2 小时前
算法/力扣--栈与队列经典题目
开发语言·c++·后端·考研·算法·leetcode·职场和发展
云泽8082 小时前
深入红黑树:SGI-STL 中 map 与 set 的关联容器架构剖析
开发语言·c++·stl底层架构
福楠2 小时前
constexpr 全家桶
c语言·开发语言·c++
REDcker2 小时前
C++ vcpkg:安装、使用、原理与选型
开发语言·c++·windows·操作系统·msvc·vcpkg
feng_you_ying_li2 小时前
set/map的封装,底层为红黑树.
c++
晨非辰2 小时前
Git版本控制速成:提交三板斧/日志透视/远程同步15分钟精通,掌握历史回溯与多人协作安全模型
linux·运维·服务器·c++·人工智能·git·后端
gdizcm2 小时前
linux判断文件类型的多种方法
linux·c++