欢迎光临
我们一直在努力

Qt之QScopedPointer、QScopeGuard、QScopedValueRollback使用及源码解读

目录

1.QScopedPointer

1.1.简介

2.2.核心成员函数

1.3.数组专用:QScopedArrayPointer

1.4.自定义删除器(进阶)

1.5.源码解读

1.5.1.核心组件:删除器 (Deleter)

1.5.2.核心类:QScopedPointer

1.5.3.数组专用:QScopedArrayPointer

1.5.4.全局运算符重载

2.QScopeGuard

2.1.简介

2.2.核心成员

2.3.使用示例

2.4.QScopeGuard vs 其他方案对比

2.5.源码解读

2.5.1.前置声明

2.5.2.核心类 QScopeGuard

2.5.3.工厂函数(用户唯一使用入口)

3.QScopedValueRollback

3.1.简介

3.2.源码解读

3.3.设计亮点

3.4.使用示例

4.总结


1.QScopedPointer

1.1.简介

   QScopedPointer 是 Qt 提供的独占式智能指针,基于 RAII(资源获取即初始化) 实现,核心作用是托管动态分配的内存,离开作用域时自动释放,无需手动调用 delete,从根源避免内存泄漏。

C++ std::unique_ptr的使用及源码分析

        它禁止拷贝,保证同一时间只有一个智能指针拥有资源所有权,功能等价于 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) 交换两个智能指针的资源

示例:

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[] 释放,且支持下标访问 []。

#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(),否则会崩溃:

#include <QWidget>
#include <QScopedPointer>

void testQObject() {
// 托管QWidget,自动调用 deleteLater()
QScopedPointer<QWidget, QScopedPointerDeleteLater> widget(new QWidget());
// 离开作用域 → 安全异步销毁
}

3.场景 2:malloc 内存释放

void testMalloc() {
// 托管malloc分配的内存
QScopedPointer<void, QScopedPointerPodDeleter> mem(malloc(100));
// 离开作用域 → 自动 free()
}

1.5.源码解读

以Qt5.15.2为例,源码如下:

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

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

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

struct QScopedPointerPodDeleter {
static inline void cleanup(void *pointer) { if (pointer) free(pointer); }
};

  • 用于 malloc 分配的 POD 类型内存,调用 C 库 free 释放。
  • 无类型检查,因为接收 void* 通用指针。

4.Qt 对象删除器 QScopedPointerObjectDeleteLater

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.核心成员与构造 / 析构

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.指针模拟运算符

// 解引用:获取对象引用
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.安全布尔转换(关键设计)

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,专门用于动态数组,默认使用数组删除器:

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 技术):

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++ 标准库规范:

// 比较两个智能指针
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 还是因异常退出,都能保证清理代码被执行,从根源避免资源泄漏。它是编写异常安全和资源安全代码的关键工具。

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.状态回滚(事务性操作)

#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 异步操作安全

#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.异常安全注意事项

#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.源码解读

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.前置声明

template <typename F> class QScopeGuard;
template <typename F> QScopeGuard<F> qScopeGuard(F f);

  • 提前声明模板类和模板工厂函数,让类可以把工厂函数声明为友元。
  • F:代表任意可调用对象(lambda 表达式、普通函数、仿函数、std::function 等)。

2.5.2.核心类 QScopeGuard

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.工厂函数(用户唯一使用入口)

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.源码解读

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:基础自动回滚

#include <QScopedValueRollback>

void test() {
int status = 1; // 初始值

{
// 保存原值1,立即将status改为2
QScopedValueRollback<int> guard(status, 2);
// 临时使用 status = 2
}
// 离开作用域 ✅ status 自动恢复为 1
}

示例 2:手动提交修改(取消回滚)

void testCommit() {
int status = 1;

{
QScopedValueRollback<int> guard(status, 2);
guard.commit(); // 提交当前值,取消回滚
}
// 离开作用域 ✅ status 保持 2,不恢复
}

示例 3:先保存,后手动修改

void testManualSet() {
int value = 100;
{
QScopedValueRollback<int> guard(value); // 仅保存原值100
value = 200; // 手动修改
}
// 自动恢复为 100
}

4.总结

工具定位功能
QScopedPointer 独占智能指针 自动释放动态内存
QScopeGuard 通用作用域守卫 执行任意自定义清理 / 回滚逻辑
QScopedValueRollback 专用变量回滚工具 仅自动恢复变量的原始值
  • 专用 RAII 工具:专门用于临时修改变量 + 自动恢复原值;
  • 核心机制:构造时备份原值,析构时自动回滚,异常安全;
  • 灵活控制:commit() 可手动提交修改,取消回滚;
  • 极简零开销:代码短小、无性能损耗,是 Qt 状态管理的必备工具。
赞(0)
未经允许不得转载:171主机测评 » Qt之QScopedPointer、QScopeGuard、QScopedValueRollback使用及源码解读
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址