目录
1.5.3.数组专用:QScopedArrayPointer
[2.4.QScopeGuard vs 其他方案对比](#2.4.QScopeGuard vs 其他方案对比)
[2.5.2.核心类 QScopeGuard](#2.5.2.核心类 QScopeGuard)
1.QScopedPointer
1.1.简介
QScopedPointer 是 Qt 提供的独占式智能指针 ,基于 RAII(资源获取即初始化) 实现,核心作用是托管动态分配的内存,离开作用域时自动释放,无需手动调用 delete,从根源避免内存泄漏。
它禁止拷贝 ,保证同一时间只有一个智能指针拥有资源所有权,功能等价于 C++11 的 std::unique_ptr,是 Qt 开发中管理动态内存的首选工具。
它的核心特点:
- 自动释放:作用域结束 → 析构函数自动释放内存;
- 独占所有权:禁止拷贝 / 赋值,杜绝重复释放、野指针问题;
- 零开销:仅存储一个裸指针,性能和裸指针完全一致;
- 类型安全:编译期检查不完整类型,避免前向声明导致的崩溃;
- 多场景适配 :支持普通对象、数组、
QObject、malloc内存。
2.2.核心成员函数
| 函数 | 作用 |
|---|---|
T* data() / T* get() |
获取裸指针(不释放所有权,仅读取) |
bool isNull() |
判断是否为空指针 |
reset(T* p = nullptr) |
释放旧内存,托管新指针 |
T* take() |
释放所有权,返回裸指针并置空智能指针 |
swap(QScopedPointer& other) |
交换两个智能指针的资源 |
示例:
cpp
void testMemberFunc() {
QScopedPointer<int> ptr(new int(10));
// 1. data():获取裸指针
int* rawPtr = ptr.data();
qDebug() << *rawPtr; // 10
// 2. reset():替换指针,自动释放旧内存
ptr.reset(new int(20)); // 旧值10被delete,托管新值20
// 3. take():释放所有权,智能指针变空
int* takePtr = ptr.take();
qDebug() << ptr.isNull(); // true
delete takePtr; // 手动释放(take后所有权转移给用户)
// 4. swap():交换资源
QScopedPointer<int> ptr1(new int(1));
QScopedPointer<int> ptr2(new int(2));
ptr1.swap(ptr2);
qDebug() << *ptr1 << *ptr2; // 2 1
}
1.3.数组专用:QScopedArrayPointer
QScopedPointer 仅用于单个对象 ,管理动态数组 必须用 QScopedArrayPointer,它会自动调用 delete[] 释放,且支持下标访问 []。
cpp
#include <QScopedPointer>
void testArray() {
// 托管动态数组
QScopedArrayPointer<int> arr(new int[5]);
// 下标访问(和普通数组一致)
for (int i = 0; i < 5; ++i) {
arr[i] = i;
}
// 离开作用域 → 自动 delete[] 释放数组
}
1.4.自定义删除器(进阶)
QScopedPointer 支持自定义资源释放策略,内置 4 种删除器,覆盖所有开发场景:
1.内置删除器一览
| 删除器 | 适用场景 | 释放方式 |
|---|---|---|
QScopedPointerDeleter<T> |
普通对象(默认) | delete |
QScopedPointerArrayDeleter<T> |
动态数组 | delete[] |
QScopedPointerPodDeleter |
malloc 分配的 POD 类型 |
free() |
QScopedPointerDeleteLater |
QObject 子类 |
deleteLater() |
2.场景 1:QObject 异步安全释放
QObject 跨线程销毁必须用 deleteLater(),否则会崩溃:
cpp
#include <QWidget>
#include <QScopedPointer>
void testQObject() {
// 托管QWidget,自动调用 deleteLater()
QScopedPointer<QWidget, QScopedPointerDeleteLater> widget(new QWidget());
// 离开作用域 → 安全异步销毁
}
3.场景 2:malloc 内存释放
cpp
void testMalloc() {
// 托管malloc分配的内存
QScopedPointer<void, QScopedPointerPodDeleter> mem(malloc(100));
// 离开作用域 → 自动 free()
}
1.5.源码解读
以Qt5.15.2为例,源码如下:
cpp
template <typename T>
struct QScopedPointerDeleter
{
static inline void cleanup(T *pointer)
{
// Enforce a complete type.
// If you get a compile error here, read the section on forward declared
// classes in the QScopedPointer documentation.
typedef char IsIncompleteType[ sizeof(T) ? 1 : -1 ];
(void) sizeof(IsIncompleteType);
delete pointer;
}
};
template <typename T>
struct QScopedPointerArrayDeleter
{
static inline void cleanup(T *pointer)
{
// Enforce a complete type.
// If you get a compile error here, read the section on forward declared
// classes in the QScopedPointer documentation.
typedef char IsIncompleteType[ sizeof(T) ? 1 : -1 ];
(void) sizeof(IsIncompleteType);
delete [] pointer;
}
};
struct QScopedPointerPodDeleter
{
static inline void cleanup(void *pointer) { if (pointer) free(pointer); }
};
#ifndef QT_NO_QOBJECT
template <typename T>
struct QScopedPointerObjectDeleteLater
{
static inline void cleanup(T *pointer) { if (pointer) pointer->deleteLater(); }
};
class QObject;
typedef QScopedPointerObjectDeleteLater<QObject> QScopedPointerDeleteLater;
#endif
template <typename T, typename Cleanup = QScopedPointerDeleter<T> >
class QScopedPointer
{
typedef T *QScopedPointer:: *RestrictedBool;
public:
explicit QScopedPointer(T *p = nullptr) Q_DECL_NOTHROW : d(p)
{
}
inline ~QScopedPointer()
{
T *oldD = this->d;
Cleanup::cleanup(oldD);
}
inline T &operator*() const
{
Q_ASSERT(d);
return *d;
}
T *operator->() const Q_DECL_NOTHROW
{
return d;
}
bool operator!() const Q_DECL_NOTHROW
{
return !d;
}
#if defined(Q_QDOC)
inline operator bool() const
{
return isNull() ? nullptr : &QScopedPointer::d;
}
#else
operator RestrictedBool() const Q_DECL_NOTHROW
{
return isNull() ? nullptr : &QScopedPointer::d;
}
#endif
T *data() const Q_DECL_NOTHROW
{
return d;
}
T *get() const Q_DECL_NOTHROW
{
return d;
}
bool isNull() const Q_DECL_NOTHROW
{
return !d;
}
void reset(T *other = nullptr) Q_DECL_NOEXCEPT_EXPR(noexcept(Cleanup::cleanup(std::declval<T *>())))
{
if (d == other)
return;
T *oldD = d;
d = other;
Cleanup::cleanup(oldD);
}
T *take() Q_DECL_NOTHROW
{
T *oldD = d;
d = nullptr;
return oldD;
}
void swap(QScopedPointer<T, Cleanup> &other) Q_DECL_NOTHROW
{
qSwap(d, other.d);
}
typedef T *pointer;
protected:
T *d;
private:
Q_DISABLE_COPY(QScopedPointer)
};
template <class T, class Cleanup>
inline bool operator==(const QScopedPointer<T, Cleanup> &lhs, const QScopedPointer<T, Cleanup> &rhs) Q_DECL_NOTHROW
{
return lhs.data() == rhs.data();
}
template <class T, class Cleanup>
inline bool operator!=(const QScopedPointer<T, Cleanup> &lhs, const QScopedPointer<T, Cleanup> &rhs) Q_DECL_NOTHROW
{
return lhs.data() != rhs.data();
}
template <class T, class Cleanup>
inline bool operator==(const QScopedPointer<T, Cleanup> &lhs, std::nullptr_t) Q_DECL_NOTHROW
{
return lhs.isNull();
}
template <class T, class Cleanup>
inline bool operator==(std::nullptr_t, const QScopedPointer<T, Cleanup> &rhs) Q_DECL_NOTHROW
{
return rhs.isNull();
}
template <class T, class Cleanup>
inline bool operator!=(const QScopedPointer<T, Cleanup> &lhs, std::nullptr_t) Q_DECL_NOTHROW
{
return !lhs.isNull();
}
template <class T, class Cleanup>
inline bool operator!=(std::nullptr_t, const QScopedPointer<T, Cleanup> &rhs) Q_DECL_NOTHROW
{
return !rhs.isNull();
}
template <class T, class Cleanup>
inline void swap(QScopedPointer<T, Cleanup> &p1, QScopedPointer<T, Cleanup> &p2) Q_DECL_NOTHROW
{ p1.swap(p2); }
template <typename T, typename Cleanup = QScopedPointerArrayDeleter<T> >
class QScopedArrayPointer : public QScopedPointer<T, Cleanup>
{
template <typename Ptr>
using if_same_type = typename std::enable_if<std::is_same<typename std::remove_cv<T>::type, Ptr>::value, bool>::type;
public:
inline QScopedArrayPointer() : QScopedPointer<T, Cleanup>(nullptr) {}
template <typename D, if_same_type<D> = true>
explicit QScopedArrayPointer(D *p)
: QScopedPointer<T, Cleanup>(p)
{
}
inline T &operator[](int i)
{
return this->d[i];
}
inline const T &operator[](int i) const
{
return this->d[i];
}
void swap(QScopedArrayPointer &other) Q_DECL_NOTHROW // prevent QScopedPointer <->QScopedArrayPointer swaps
{ QScopedPointer<T, Cleanup>::swap(other); }
private:
explicit inline QScopedArrayPointer(void *) {
// Enforce the same type.
// If you get a compile error here, make sure you declare
// QScopedArrayPointer with the same template type as you pass to the
// constructor. See also the QScopedPointer documentation.
// Storing a scalar array as a pointer to a different type is not
// allowed and results in undefined behavior.
}
Q_DISABLE_COPY(QScopedArrayPointer)
};
template <typename T, typename Cleanup>
inline void swap(QScopedArrayPointer<T, Cleanup> &lhs, QScopedArrayPointer<T, Cleanup> &rhs) Q_DECL_NOTHROW
{ lhs.swap(rhs); }
1.5.1.核心组件:删除器 (Deleter)
删除器是智能指针的资源释放策略,源码定义了 4 种适配不同场景的删除器,这是智能指针能自动释放资源的核心:
1.普通对象删除器 QScopedPointerDeleter
cpp
template <typename T>
struct QScopedPointerDeleter {
static inline void cleanup(T *pointer) {
// 🔴 关键:不完整类型检查(防止前向声明导致未定义行为)
typedef char IsIncompleteType[ sizeof(T) ? 1 : -1 ];
(void) sizeof(IsIncompleteType);
delete pointer; // 释放普通对象
}
};
- 核心作用 :释放单个对象 ,调用
delete - 不完整类型检查 :前向声明的类(
class A;)是不完整类型 ,sizeof(T)无法计算,会触发编译报错(数组长度为 - 1 非法)。避免delete不完整类型导致的内存泄漏 / 崩溃。
2.数组删除器 QScopedPointerArrayDeleter
cpp
template <typename T>
struct QScopedPointerArrayDeleter {
static inline void cleanup(T *pointer) {
typedef char IsIncompleteType[ sizeof(T) ? 1 : -1 ];
(void) sizeof(IsIncompleteType);
delete [] pointer; // 释放数组
}
};
专门用于动态数组 ,调用 delete[],其余逻辑和普通删除器一致。
3.POD 类型删除器 QScopedPointerPodDeleter
cpp
struct QScopedPointerPodDeleter {
static inline void cleanup(void *pointer) { if (pointer) free(pointer); }
};
- 用于
malloc分配的 POD 类型内存 ,调用 C 库free释放。 - 无类型检查,因为接收
void*通用指针。
4.Qt 对象删除器 QScopedPointerObjectDeleteLater
cpp
template <typename T>
struct QScopedPointerObjectDeleteLater {
static inline void cleanup(T *pointer) { if (pointer) pointer->deleteLater(); }
};
专门用于 QObject 子类,调用 Qt 的 deleteLater()(异步安全销毁,适配跨线程场景)。
1.5.2.核心类:QScopedPointer
独占智能指针的主体实现,模板参数:
T:托管的对象类型Cleanup:删除器(默认使用普通对象删除器)
1.核心成员与构造 / 析构
cpp
template <typename T, typename Cleanup = QScopedPointerDeleter<T> >
class QScopedPointer {
protected:
T *d; // 🔑 托管的裸指针(唯一的资源持有者)
public:
// 构造函数:explicit 禁止隐式转换
explicit QScopedPointer(T *p = nullptr) Q_DECL_NOTHROW : d(p) {}
// 析构函数:RAII 核心!自动释放资源
inline ~QScopedPointer() {
T *oldD = this->d;
Cleanup::cleanup(oldD); // 调用删除器释放资源
}
private:
Q_DISABLE_COPY(QScopedPointer) // 🔴 禁止拷贝!保证独占性
};
- 独占性保证 :
Q_DISABLE_COPY宏禁用拷贝构造函数 + 拷贝赋值运算符,资源只能被一个智能指针持有。 - RAII :构造时托管指针,析构时自动释放,无需手动调用
delete。
2.指针模拟运算符
cpp
// 解引用:获取对象引用
inline T &operator*() const { Q_ASSERT(d); return *d; }
// 箭头运算符:访问对象成员(模拟裸指针)
T *operator->() const Q_DECL_NOTHROW { return d; }
// 判空:!ptr
bool operator!() const Q_DECL_NOTHROW { return !d; }
让智能指针的用法和裸指针完全一致,降低使用成本。
3.安全布尔转换(关键设计)
cpp
typedef T *QScopedPointer:: *RestrictedBool;
operator RestrictedBool() const Q_DECL_NOTHROW {
return isNull() ? nullptr : &QScopedPointer::d;
}
- 为什么不用
operator bool()? 直接重载bool会触发隐式类型转换 (比如if(ptr == 1)非法通过编译)。 - 安全布尔惯用法 :用成员指针类型
RestrictedBool做转换,仅支持合法的布尔判断(if(ptr)),无隐式转换,绝对安全。
4.核心操作方法
| 方法 | 作用 |
|---|---|
data()/get() |
获取裸指针(不释放所有权) |
isNull() |
判断是否为空指针 |
reset(T* p) |
释放旧资源,托管新指针 |
take() |
释放所有权,返回裸指针并置空 |
swap() |
交换两个智能指针的资源 |
1.5.3.数组专用:QScopedArrayPointer
继承自 QScopedPointer,专门用于动态数组,默认使用数组删除器:
cpp
template <typename T, typename Cleanup = QScopedPointerArrayDeleter<T> >
class QScopedArrayPointer : public QScopedPointer<T, Cleanup>
{
template <typename Ptr>
using if_same_type = typename std::enable_if<std::is_same<typename std::remove_cv<T>::type, Ptr>::value, bool>::type;
public:
inline QScopedArrayPointer() : QScopedPointer<T, Cleanup>(nullptr) {}
template <typename D, if_same_type<D> = true>
explicit QScopedArrayPointer(D *p)
: QScopedPointer<T, Cleanup>(p)
{
}
inline T &operator[](int i)
{
return this->d[i];
}
inline const T &operator[](int i) const
{
return this->d[i];
}
void swap(QScopedArrayPointer &other) Q_DECL_NOTHROW // prevent QScopedPointer <->QScopedArrayPointer swaps
{ QScopedPointer<T, Cleanup>::swap(other); }
private:
explicit inline QScopedArrayPointer(void *) {
// Enforce the same type.
// If you get a compile error here, make sure you declare
// QScopedArrayPointer with the same template type as you pass to the
// constructor. See also the QScopedPointer documentation.
// Storing a scalar array as a pointer to a different type is not
// allowed and results in undefined behavior.
}
Q_DISABLE_COPY(QScopedArrayPointer)
};
template <typename T, typename Cleanup>
inline void swap(QScopedArrayPointer<T, Cleanup> &lhs, QScopedArrayPointer<T, Cleanup> &rhs) Q_DECL_NOTHROW
{ lhs.swap(rhs); }
- 新增
operator[]:支持数组下标访问,和普通数组用法一致。 - 严格类型检查:禁止不同类型的数组指针赋值,避免未定义行为。
编译期类型安全检查(SFINAE 技术):
cpp
template <typename Ptr>
using if_same_type = typename std::enable_if<
std::is_same<typename std::remove_cv<T>::type, Ptr>::value,
bool
>::type;
作用:强制构造时的指针类型与模板类型完全一致
std::remove_cv<T>:忽略const/volatile修饰std::is_same:判断类型是否完全相同std::enable_if:类型不匹配则直接编译报错(SFINAE 机制)- 杜绝:
QScopedArrayPointer<int> arr(new char[10])这种非法类型转换
1.5.4.全局运算符重载
提供指针比较、空指针判断、交换功能,符合 C++ 标准库规范:
cpp
// 比较两个智能指针
bool operator==(const QScopedPointer& lhs, const QScopedPointer& rhs);
// 与 nullptr 比较
bool operator==(const QScopedPointer& lhs, std::nullptr_t);
// 通用交换
void swap(QScopedPointer& p1, QScopedPointer& p2);
2.QScopeGuard
2.1.简介
QScopeGuard 是 Qt 提供的作用域守卫 工具,基于 RAII(资源获取即初始化) 实现,核心作用是在作用域结束时自动执行预定义的清理逻辑 ,无论函数是正常退出、提前 return 还是因异常退出,都能保证清理代码被执行,从根源避免资源泄漏。它是编写异常安全 和资源安全代码的关键工具。
cpp
template <typename F>
class QScopeGuard {
public:
~QScopeGuard() { if (m_invoke) m_func(); } // 析构时执行清理函数
void dismiss() { m_invoke = false; } // 取消执行
private:
F m_func; // 存储清理函数(通常是lambda)
bool m_invoke = true; // 执行标记
};
- RAII 核心:将清理逻辑绑定到局部对象的析构函数,作用域结束时自动触发
- 执行控制 :通过
m_invoke标记控制是否执行清理,支持dismiss()取消 - 移动语义:支持移动构造,禁止拷贝,保证唯一所有权
特性有:
- 自动执行:作用域结束(包括异常退出)时自动执行清理逻辑
- 可取消 :通过
dismiss()取消执行(适用于成功完成时无需清理的场景) - 轻量级:无额外开销,仅存储函数对象和布尔标记
- 通用性:支持任意可调用对象(lambda、函数指针、函数对象)
2.2.核心成员
| 函数 | 作用 | 适用场景 |
|---|---|---|
~QScopeGuard() |
析构时执行清理函数 | 自动触发,无需手动调用 |
dismiss() |
取消清理函数执行 | 操作成功完成时,避免重复清理 |
| 移动构造 | 转移所有权 | 函数返回时传递 guard 对象 |
2.3.使用示例
1.状态回滚(事务性操作)
cpp
#include <QScopeGuard>
#include <QString>
#include <QDebug>
void updateData(QString& data) {
QString oldData = data; // 保存原始状态
// 失败时自动回滚
auto rollbackGuard = qScopeGuard([&data, oldData]{
qDebug() << "回滚数据";
data = oldData;
});
// 模拟数据更新
data += "_updated";
// 模拟操作失败
bool success = false;
if (!success) {
qDebug() << "操作失败";
return; // 自动回滚
}
// 操作成功,取消回滚
rollbackGuard.dismiss();
qDebug() << "操作成功,数据已更新";
}
2.QObject 异步操作安全
cpp
#include <QScopeGuard>
#include <QWidget>
#include <QDebug>
void createWidget() {
QWidget* widget = new QWidget();
// 确保无论如何都能安全删除
auto deleteGuard = qScopeGuard([widget]{
qDebug() << "删除widget";
widget->deleteLater();
});
// 模拟操作
if (!widget->isVisible()) {
qDebug() << "widget不可见";
return; // 自动执行deleteLater()
}
// 成功使用,取消删除(假设widget被加入父对象)
deleteGuard.dismiss();
}
3.异常安全注意事项
cpp
#include <QScopeGuard>
#include <QDebug>
void exceptionSafeCode() {
auto guard = qScopeGuard([]{
qDebug() << "清理函数执行(即使发生异常)";
// 清理函数应避免抛出异常!
});
try {
// 可能抛出异常的代码
throw std::runtime_error("出错了");
} catch (...) {
qDebug() << "捕获异常";
// 异常退出时,guard 仍会执行清理
}
}
警告 :清理函数绝对不能抛出异常,否则可能导致程序崩溃Qt
2.4.QScopeGuard vs 其他方案对比
| 方案 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|
| QScopeGuard | 自动执行、可取消、轻量级 | Qt 特定 | Qt 项目的所有资源管理场景 |
| std::scope_exit | 标准库、跨平台 | C++20+、部分编译器不支持 | 纯 C++ 项目,需要标准兼容 |
| goto 清理 | 兼容旧代码 | 可读性差、易出错 | 无 C++11 支持的极端场景 |
| try/finally | Java 风格、直观 | C++ 无 finally,需模拟 | 不推荐,C++ 中 RAII 更优 |
2.5.源码解读
cpp
template <typename F> class QScopeGuard;
template <typename F> QScopeGuard<F> qScopeGuard(F f);
template <typename F>
class QScopeGuard
{
public:
QScopeGuard(QScopeGuard &&other) Q_DECL_NOEXCEPT
: m_func(std::move(other.m_func))
, m_invoke(other.m_invoke)
{
other.dismiss();
}
~QScopeGuard()
{
if (m_invoke)
m_func();
}
void dismiss() Q_DECL_NOEXCEPT
{
m_invoke = false;
}
private:
explicit QScopeGuard(F f) Q_DECL_NOEXCEPT
: m_func(std::move(f))
{
}
Q_DISABLE_COPY(QScopeGuard)
F m_func;
bool m_invoke = true;
friend QScopeGuard qScopeGuard<F>(F);
};
template <typename F>
QScopeGuard<F> qScopeGuard(F f)
{
return QScopeGuard<F>(std::move(f));
}
QScopeGuard作用域守卫(Scope Guard) ,是 RAII 设计模式的终极通用化方案 。它的核心作用:无论作用域是正常退出、return 跳出,还是发生异常崩溃,都会在离开作用域时自动执行你绑定的自定义函数,用于统一处理清理、回滚、释放逻辑,让代码异常安全且简洁。
它是无侵入、通用型的工具,不用为每种资源单独写 RAII 类。
2.5.1.前置声明
cpp
template <typename F> class QScopeGuard;
template <typename F> QScopeGuard<F> qScopeGuard(F f);
- 提前声明模板类和模板工厂函数,让类可以把工厂函数声明为友元。
F:代表任意可调用对象(lambda 表达式、普通函数、仿函数、std::function 等)。
2.5.2.核心类 QScopeGuard
cpp
template <typename F>
class QScopeGuard
{
public:
// 移动构造函数:支持转移守卫的所有权
QScopeGuard(QScopeGuard &&other) Q_DECL_NOEXCEPT
: m_func(std::move(other.m_func)) // 移动转移清理函数
, m_invoke(other.m_invoke) // 复制执行标记
{
other.dismiss(); // 原守卫置为不执行,避免重复调用
}
// 析构函数:核心!作用域退出时自动调用
~QScopeGuard()
{
if (m_invoke) // 仅当标记为true时,执行清理函数
m_func(); // 调用用户绑定的自定义逻辑
}
// 手动取消:取消执行清理函数
void dismiss() Q_DECL_NOEXCEPT
{
m_invoke = false;
}
private:
// 私有构造函数:禁止直接创建对象,必须通过工厂函数
explicit QScopeGuard(F f) Q_DECL_NOEXCEPT
: m_func(std::move(f)) // 初始化清理函数(移动优化)
{
}
Q_DISABLE_COPY(QScopeGuard) // 禁止拷贝!仅支持移动
F m_func; // 存储用户绑定的可调用对象
bool m_invoke = true; // 执行标记:默认true(需要执行)
// 友元:让工厂函数可以访问私有构造函数
friend QScopeGuard qScopeGuard<F>(F);
};
-
私有构造函数
- 禁止用户直接
QScopeGuard<...> guard(...)创建对象; - 必须通过工厂函数创建,自动推导模板参数(用户无需手写复杂的 lambda 类型)。
- 禁止用户直接
-
禁止拷贝,仅允许移动
Q_DISABLE_COPY:禁用拷贝构造 / 赋值,保证一个守卫只能管理一个清理逻辑;- 提供移动构造:支持转移守卫的所有权(比如返回值、传递给其他函数)。
-
析构函数(核心)
- C++ 保证:栈上对象离开作用域时,析构函数一定会被执行(即使发生异常,栈展开也会调用);
- 通过
m_invoke标记判断是否执行清理函数。
-
dismiss () 手动取消
- 业务逻辑成功完成后,调用此方法取消清理(比如文件写入成功,不用删除临时文件)。
2.5.3.工厂函数(用户唯一使用入口)
cpp
template <typename F>
QScopeGuard<F> qScopeGuard(F f)
{
return QScopeGuard<F>(std::move(f));
}
- 核心价值 :自动推导模板参数
F,用户无需关心模板类型; - 用法极简:
auto guard = qScopeGuard([]{ ... });; - 内部调用私有构造函数,创建并返回守卫对象。
3.QScopedValueRollback
3.1.简介
这是 Qt 基于 RAII 原则 实现的专用变量值自动回滚工具 ,核心作用非常专一:临时修改变量的值,当离开当前作用域时,自动将变量恢复为修改前的原始值;也可以手动提交修改,取消回滚。
它是极简、专用、零开销的工具,专门解决临时状态修改 + 自动恢复的场景。
与之前的 QScopeGuard(通用清理)、QScopedPointer(内存管理)不同:QScopedValueRollback 是专用工具 → 只做变量值的自动回滚。
3.2.源码解读
cpp
template <typename T>
class QScopedValueRollback
{
public:
// 构造1:仅保存变量原值,不修改当前值
explicit QScopedValueRollback(T &var) :
varRef(var), // 引用绑定目标变量
oldValue(var) // 保存变量的初始值
{
}
// 构造2:保存原值 + 立即将变量设置为新值
explicit QScopedValueRollback(T &var, T value) :
varRef(var), // 引用绑定目标变量
oldValue(var) // 保存变量的初始值
{
varRef = qMove(value); // 立即修改变量为新值(移动优化)
}
// 析构函数:核心!作用域退出时自动回滚
~QScopedValueRollback()
{
varRef = qMove(oldValue); // 恢复为原始值
}
// 提交修改:取消自动回滚,保留当前值
void commit()
{
oldValue = varRef; // 将旧值更新为当前值,回滚时无变化
}
private:
T& varRef; // 🔑 引用:直接绑定目标变量(操作原变量)
T oldValue; // 存储变量的原始值(回滚的依据)
Q_DISABLE_COPY(QScopedValueRollback) // 禁止拷贝,保证独占管理
};
-
核心成员变量
T& varRef:引用类型,直接绑定要管理的变量,所有修改都作用于原变量,无副本开销;T oldValue:在构造时备份变量的初始值,是自动回滚的核心依据;Q_DISABLE_COPY:禁用拷贝,确保一个实例只管理一个变量,避免重复回滚。
-
两个构造函数
- 构造 1 :只绑定变量、保存原值,不修改变量,适合后续手动修改变量;
- 构造 2:一步完成「保存原值 + 设置新值」,是最常用的写法。
-
析构函数(自动回滚) C++ 保证栈上对象离开作用域必执行析构 (无论正常退出、
return提前退出、抛出异常),因此变量一定会被恢复为原始值,绝对异常安全。 -
commit () 手动提交 调用后,会将备份的旧值更新为变量的当前值 ,析构时赋值后变量值不变,相当于确认修改,取消回滚。
3.3.设计亮点
- 极致专用 专为变量临时修改 + 自动恢复设计,代码极简,没有冗余逻辑。
- 异常安全栈展开机制保证析构必执行,即使代码崩溃,变量也能恢复原值。
- 零性能开销 仅存储一个引用 + 一个变量副本,效率和手动操作完全一致。
- 引用绑定直接操作原变量,不产生多余副本,语义清晰。
- 灵活控制 自动回滚 +
commit()手动提交,覆盖所有业务场景。
3.4.使用示例
示例 1:基础自动回滚
cpp
#include <QScopedValueRollback>
void test() {
int status = 1; // 初始值
{
// 保存原值1,立即将status改为2
QScopedValueRollback<int> guard(status, 2);
// 临时使用 status = 2
}
// 离开作用域 ✅ status 自动恢复为 1
}
示例 2:手动提交修改(取消回滚)
cpp
void testCommit() {
int status = 1;
{
QScopedValueRollback<int> guard(status, 2);
guard.commit(); // 提交当前值,取消回滚
}
// 离开作用域 ✅ status 保持 2,不恢复
}
示例 3:先保存,后手动修改
cpp
void testManualSet() {
int value = 100;
{
QScopedValueRollback<int> guard(value); // 仅保存原值100
value = 200; // 手动修改
}
// 自动恢复为 100
}
4.总结
| 工具 | 定位 | 功能 |
|---|---|---|
QScopedPointer |
独占智能指针 | 自动释放动态内存 |
QScopeGuard |
通用作用域守卫 | 执行任意自定义清理 / 回滚逻辑 |
QScopedValueRollback |
专用变量回滚工具 | 仅自动恢复变量的原始值 |
- 专用 RAII 工具 :专门用于临时修改变量 + 自动恢复原值;
- 核心机制:构造时备份原值,析构时自动回滚,异常安全;
- 灵活控制 :
commit()可手动提交修改,取消回滚; - 极简零开销:代码短小、无性能损耗,是 Qt 状态管理的必备工具。