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.4.QScopeGuard vs 其他方案对比)

2.5.源码解读

2.5.1.前置声明

[2.5.2.核心类 QScopeGuard](#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 开发中管理动态内存的首选工具。

它的核心特点:

  • 自动释放:作用域结束 → 析构函数自动释放内存;
  • 独占所有权:禁止拷贝 / 赋值,杜绝重复释放、野指针问题;
  • 零开销:仅存储一个裸指针,性能和裸指针完全一致;
  • 类型安全:编译期检查不完整类型,避免前向声明导致的崩溃;
  • 多场景适配 :支持普通对象、数组、QObjectmalloc 内存。

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 状态管理的必备工具。
相关推荐
fox_lht1 小时前
13.3.测试的组织方式
开发语言·后端·rust
thisiszdy2 小时前
<C++> 多线程基础
c++
·白小白2 小时前
C++ STL 容器 list 底层结构详解
开发语言·c++·list
RSTJ_16252 小时前
PYTHON+AI LLM DAY SIXTY-SIX
服务器·开发语言·python
Chase_______2 小时前
【Java基础 | 11】异常处理进阶:throw、throws、自定义异常与异常链讲清楚
java·开发语言·python
BirdenT2 小时前
20260604紫题训练
c++·算法
tg:;2 小时前
Catkin 常用命令
开发语言·c++·算法
Cx330❀2 小时前
【Linux网络】一文吃透 TCP Socket 编程
linux·运维·服务器·开发语言·网络·tcp/ip
暖阳华笺2 小时前
【高频考点】回溯(暴力搜索)
数据结构·c++·算法·回溯法