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;
}
相关推荐
顾温4 小时前
default——C#/C++
java·c++·c#
凉茶钱4 小时前
【c语言】动态内存管理:malloc,calloc,realloc,柔性数组
c语言·c++·vscode·柔性数组
脏脏a4 小时前
【C++模版】泛型编程:代码复用的终极利器
开发语言·c++·c++模版
island13144 小时前
【C++仿Muduo库#3】Server 服务器模块实现上
服务器·开发语言·c++
散峰而望4 小时前
【算法竞赛】C/C++ 的输入输出你真的玩会了吗?
c语言·开发语言·数据结构·c++·算法·github
小龙报4 小时前
【C语言】内存里的 “数字变形记”:整数三码、大小端与浮点数存储真相
c语言·开发语言·c++·创业创新·学习方法·visual studio
刃神太酷啦4 小时前
扒透 STL 底层!map/set 如何封装红黑树?迭代器逻辑 + 键值限制全手撕----《Hello C++ Wrold!》(23)--(C/C++)
java·c语言·javascript·数据结构·c++·算法·leetcode
2301_789015624 小时前
C++:继承
c语言·开发语言·c++
星河耀银海4 小时前
C++ 运算符重载:自定义类型的运算扩展
android·java·c++
feng_blog66884 小时前
C++线程池|解决死锁、崩溃、丢任务所有痛点
java·开发语言·c++