Qt智能指针
Qt中常用的智能指针主要包括:QPointer 、QSharedPointer 、QScopedPointer 、QSharedDataPointer 和 QWeakPointer。
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 // !LoacalStrclass QtPointerTest : public QMainWindow
{
Q_OBJECTpublic:
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(浅拷贝,高效)
- 修改对象 :
- 若引用计数 = 1:直接修改原数据,无额外开销
- 若引用计数 > 1:执行
detach(),深拷贝数据块,引用计数相应调整,修改新副本
- 对象销毁:引用计数 - 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;
}