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;
}
相关推荐
零号全栈寒江独钓1 小时前
基于c/c++实现linux/windows跨平台获取ntp网络时间戳
linux·c语言·c++·windows
CSCN新手听安2 小时前
【linux】高级IO,以ET模式运行的epoll版本的TCP服务器实现reactor反应堆
linux·运维·服务器·c++·高级io·epoll·reactor反应堆
雾岛听蓝3 小时前
Qt操作指南:窗口组成与菜单栏
开发语言·经验分享·笔记·qt
松☆3 小时前
C++ 算法竞赛题解:P13569 [CCPC 2024 重庆站] osu!mania —— 浮点数精度陷阱与 `eps` 的深度解析
开发语言·c++·算法
(Charon)4 小时前
【C++/Qt】C++/Qt 实现 TCP Server:支持启动监听、消息收发、日志保存
c++·qt·tcp/ip
并不喜欢吃鱼4 小时前
从零开始C++----七.继承及相关模型和底层(上篇)
开发语言·c++
tankeven5 小时前
HJ182 画展布置
c++·算法
W23035765735 小时前
【改进版】C++ 固定线程池实现:基于调用者运行的拒绝策略优化
开发语言·c++·线程池
谭欣辰6 小时前
C++ 控制台跑酷小游戏
c++·游戏
周末也要写八哥6 小时前
C++实际开发之泛型编程(模版编程)
java·开发语言·c++