Qt智能指针

一、Qt智能指针体系

1.QPointer(弱指针)

// QPointer只能用于QObject及其派生类QPointer<QPushButton> button = new QPushButton("Click me");
// 检查指针是否有效if (!button.isNull()) {    button->setText("Clicked");}
// 当对象被删除时,QPointer自动置为nullptrdelete button.data();  // button.isNull()现在返回true

2.QSharedPointer(共享指针)

// 引用计数智能指针QSharedPointer<MyObject> obj1 = QSharedPointer<MyObject>::create();QSharedPointer<MyObject> obj2 = obj1;  // 引用计数+1
// 自定义删除器QSharedPointer<FILE> file(fopen("test.txt", "r"), fclose);
// 从裸指针转换(谨慎使用)MyObject* rawPtr = new MyObject();QSharedPointer<MyObject> sharedPtr(rawPtr);

3.QWeakPointer(弱引用指针)

QSharedPointer<MyObject> shared = QSharedPointer<MyObject>::create();QWeakPointer<MyObject> weak = shared;
// 提升为强引用QSharedPointer<MyObject> strong = weak.toStrongRef();if (!strong.isNull()) {    // 对象仍然存在}

4.QScopedPointer(作用域指针)

// 超出作用域自动删除{    QScopedPointer<MyClass> ptr(new MyClass());    ptr->doSomething();} // 这里自动删除
// 自定义删除器QScopedPointer<FILE, QScopedPointerPodDeleter> file(fopen("test.txt", "r"));

5.QSharedDataPointer(隐式共享指针)

class SharedData : public QSharedData {public:    SharedData() : value(0) {}    SharedData(const SharedData &other) : QSharedData(other), value(other.value) {}
    int value;};
class MyClass {public:    MyClass() : data(new SharedData) {}
    void setValue(int val) {        // 写时复制(Copy-On-Write)        data->value = val;    }
private:    QSharedDataPointer<SharedData> data;};

二、Qt对象树与父子关系指针

1.QObject父子关系

QWidget* parent = new QWidget();QPushButton* child1 = new QPushButton("Button 1", parent);QPushButton* child2 = new QPushButton("Button 2", parent);
// 删除父对象时,自动删除所有子对象delete parent;  // child1和child2也被自动删除
// 查找子对象QObject* found = parent->findChild<QPushButton*>("Button 1");

2.对象所有权转移

// 方法1:在构造函数中指定父对象QObject* child = new QObject(parent);
// 方法2:使用setParentQObject* child = new QObject();child->setParent(parent);
// 方法3:使用QWidget的布局系统QWidget* widget = new QWidget();QVBoxLayout* layout = new QVBoxLayout(widget);QPushButton* button = new QPushButton();layout->addWidget(button);  // 所有权转移给widget

三、信号槽中的指针管理

1.安全的跨线程指针传递

// 在主线程创建对象Worker* worker = new Worker();
// 移动到工作线程worker->moveToThread(workerThread);
// 使用QPointer确保安全访问QPointer<Worker> safeWorker = worker;
connect(worker, &Worker::finished, [safeWorker]() {    if (safeWorker) {        // 安全地使用worker    }});

2.使用智能指针连接信号槽

QSharedPointer<QObject> obj = QSharedPointer<QObject>::create();
// 注意:如果使用lambda捕获shared_ptr,会延长对象生命周期connect(sender, &Sender::signal, [obj]() {    // obj会一直存在直到连接断开});
// 更好的方式:使用弱指针QWeakPointer<QObject> weakObj = obj;connect(sender, &Sender::signal, [weakObj]() {    QSharedPointer<QObject> strong = weakObj.toStrongRef();    if (strong) {        // 对象还存在    }});

四、内存管理最佳实践

1.常见陷阱与解决方案

// 陷阱1:悬挂指针void problematic() {    QObject* obj = new QObject();    // ... 使用obj    delete obj;    // 后续代码可能意外访问已删除的obj}
// 解决方案:使用智能指针void solution() {    QScopedPointer<QObject> obj(new QObject());    // 自动管理生命周期}
// 陷阱2:循环引用class A {    QSharedPointer<B> b;};class B {    QSharedPointer<A> a;  // 循环引用!};
// 解决方案:使用弱指针class B {    QWeakPointer<A> a;  // 打破循环};

2.自定义删除器示例

// 为第三方库资源创建自定义删除器struct SqliteDeleter {    void operator()(sqlite3* db) {        if (db) sqlite3_close(db);    }};
using SqlitePtr = QScopedPointer<sqlite3, SqliteDeleter>;SqlitePtr database(sqlite3_open("database.db"));

五、调试与检测工具

1.内存泄漏检测

#define QT_DEBUG_POINTERS
class MyClass : public QObject {    Q_OBJECTpublic:    MyClass(QObject* parent = nullptr) : QObject(parent) {        qDebug() << "MyClass created at" << this;    }
    ~MyClass() {        qDebug() << "MyClass destroyed at" << this;    }};
// 在.pro文件中添加:DEFINES += QT_DEBUG_POINTERS

2.使用valgrind检测

# 运行内存检测valgrind --tool=memcheck --leak-check=full ./your-qt-app
# 使用Qt特定选项export QT_DEBUG_PLUGINS=1valgrind ./your-qt-app

六、性能优化建议

  1. 避免频繁分配/释放:使用对象池或预分配

  2. 选择合适的指针类型:根据使用场景选择

  3. 注意信号槽连接:及时断开不需要的连接

  4. 使用移动语义:C++11的移动构造减少拷贝

【总结】

Qt提供了丰富的指针管理工具,关键在于根据具体场景选择合适的指针类型:

  • QObject派生类:优先使用父子关系管理

  • 资源共享:使用QSharedPointer

  • 独占资源:使用QScopedPointer或std::unique_ptr

  • 观察者模式:使用QPointer

  • 隐式共享数据:使用QSharedDataPointer

七、Qt智能指针完整实例应用

1.项目源码

1.1 qpointer_example.h

#ifndef QPOINTER_EXAMPLE_H#define QPOINTER_EXAMPLE_H
#include <QObject>#include <QPointer>#include <QPushButton>#include <QWidget>
class QPointerExample{public:    static void demonstrate(QWidget *parent);};
// 测试类class TestButton : public QPushButton{    Q_OBJECTpublic:    explicit TestButton(const QString &text, QWidget *parent = nullptr)        : QPushButton(text, parent)    {        setObjectName("TestButton_" + text);    }
    ~TestButton()    {        // 析构函数中输出信息    }};
#endif // QPOINTER_EXAMPLE_H

1.2 qpointer_example.cpp

#include "qpointer_example.h"#include <QDebug>#include <QTimer>#include <QApplication>
void QPointerExample::demonstrate(QWidget *parent){    Q_UNUSED(parent);
    // 使用qDebug输出,会被自定义消息处理器捕获    auto log = [](const QString &msg) {        qDebug() << msg;    };
    log("【示例1】创建QPointer并检查有效性");    QPointer<TestButton> button = new TestButton("点击我", parent);
    if (!button.isNull()) {        log(QString("按钮创建成功,对象名: %1").arg(button->objectName()));        log(QString("按钮文本: %1").arg(button->text()));    }
    log("\n【示例2】QPointer自动检测对象删除");    QPointer<TestButton> button2 = new TestButton("临时按钮", parent);    log(QString("创建后检查: button2.isNull() = %1").arg(button2.isNull() ? "true" : "false"));
    // 删除对象    delete button2.data();    log(QString("删除后检查: button2.isNull() = %1").arg(button2.isNull() ? "true" : "false"));    log("✓ QPointer自动检测到对象已被删除,自动置为nullptr");
    log("\n【示例3】QPointer在多线程环境中的安全性");    QPointer<TestButton> safeButton = new TestButton("安全按钮", parent);
    // 模拟异步操作    QTimer::singleShot(100, [safeButton, log]() {        if (!safeButton.isNull()) {            log("异步操作中:对象仍然存在,可以安全使用");            safeButton->setText("已更新");        } else {            log("异步操作中:对象已被删除,QPointer保护了我们");        }    });
    log("\n【示例4】QPointer与普通指针的对比");    TestButton *rawPtr = new TestButton("裸指针按钮", parent);    QPointer<TestButton> smartPtr = new TestButton("智能指针按钮", parent);
    delete rawPtr;    delete smartPtr.data();
    // 危险:裸指针不会自动变为nullptr    log(QString("裸指针删除后: rawPtr = %1 (危险!可能指向已删除的内存)").arg(rawPtr ? "非空" : "空"));    log(QString("QPointer删除后: smartPtr.isNull() = %1 (安全!)").arg(smartPtr.isNull() ? "true" : "false"));
    log("\n【示例5】QPointer在容器中的使用");    QList<QPointer<TestButton>> buttonList;    for (int i = 0; i < 3; ++i) {        buttonList.append(new TestButton(QString("按钮 %1").arg(i + 1), parent));    }
    log(QString("创建了 %1 个按钮").arg(buttonList.size()));
    // 删除部分按钮    delete buttonList[1].data();
    // 安全遍历    int validCount = 0;    for (const QPointer<TestButton> &btn : buttonList) {        if (!btn.isNull()) {            validCount++;            log(QString("有效按钮: %1").arg(btn->text()));        }    }    log(QString("有效按钮数量: %1").arg(validCount));
    log("\n【重要提示】");    log("✓ QPointer只能用于QObject及其派生类");    log("✓ QPointer不会延长对象生命周期");    log("✓ QPointer主要用于观察者模式,避免悬挂指针");    log("✓ 适合用于缓存、回调等场景");}

1.3 qsharedpointer_example.h

#ifndef QSHAREDPOINTER_EXAMPLE_H#define QSHAREDPOINTER_EXAMPLE_H
#include <QObject>#include <QSharedPointer>#include <QDebug>
class QWidget;
class QSharedPointerExample{public:    static void demonstrate(QWidget *parent);};
// 测试类 - 非QObject派生类class MyData{public:    MyData(int id = 0) : m_id(id), m_value(0) {        qDebug() << QString("MyData[%1] 构造函数被调用").arg(m_id);    }
    ~MyData() {        qDebug() << QString("MyData[%1] 析构函数被调用").arg(m_id);    }
    void setValue(int value) { m_value = value; }    int getValue() const { return m_value; }    int getId() const { return m_id; }
private:    int m_id;    int m_value;};
// QObject派生类class MyObject : public QObject{    Q_OBJECTpublic:    explicit MyObject(int id, QObject *parent = nullptr)        : QObject(parent), m_id(id) {        qDebug() << QString("MyObject[%1] 构造函数被调用").arg(m_id);    }
    ~MyObject() {        qDebug() << QString("MyObject[%1] 析构函数被调用").arg(m_id);    }
    int getId() const { return m_id; }
private:    int m_id;};
#endif // QSHAREDPOINTER_EXAMPLE_H

1.4 qsharedpointer_example.cpp

#include "qsharedpointer_example.h"#include <QDebug>#include <QList>#include <QTimer>
void QSharedPointerExample::demonstrate(QWidget *parent){    Q_UNUSED(parent);
    auto log = [](const QString &msg) {        qDebug() << msg.toUtf8().constData();    };
    log("【示例1】基本使用 - 引用计数");    {        QSharedPointer<MyData> ptr1 = QSharedPointer<MyData>::create(1);        log("创建ptr1");
        QSharedPointer<MyData> ptr2 = ptr1;        log("ptr2 = ptr1,引用计数增加");
        QSharedPointer<MyData> ptr3 = ptr2;        log("ptr3 = ptr2,引用计数再次增加");        log("注意:Qt 6中QSharedPointer不提供use_count()方法");
        log("离开作用域,所有指针销毁,对象自动删除");    }    log("对象已自动删除(析构函数被调用)");
    log("\n【示例2】从裸指针创建(谨慎使用)");    {        MyData *rawPtr = new MyData(2);        QSharedPointer<MyData> sharedPtr(rawPtr);        log("从裸指针创建QSharedPointer");
        // 注意:不要手动delete rawPtr,QSharedPointer会管理        // delete rawPtr; // 错误!会导致双重删除    }
    log("\n【示例3】自定义删除器");    {        // 模拟文件句柄        struct FileHandle {            QString filename;            FileHandle(const QString &name) : filename(name) {                qDebug() << QString("打开文件: %1").arg(filename);            }            ~FileHandle() {                qDebug() << QString("关闭文件: %1").arg(filename);            }        };
        auto fileDeleter = [](FileHandle *handle) {            if (handle) {                qDebug() << QString("使用自定义删除器关闭: %1").arg(handle->filename);                delete handle;            }        };
        QSharedPointer<FileHandle> file(            new FileHandle("test.txt"),            fileDeleter        );        log("文件句柄由QSharedPointer管理");    }    log("文件自动关闭");
    log("\n【示例4】在容器中使用");    {        QList<QSharedPointer<MyData>> dataList;
        QSharedPointer<MyData> shared = QSharedPointer<MyData>::create(3);        dataList.append(shared);        dataList.append(shared);        dataList.append(shared);
        log("列表中有3个元素,都共享同一个对象(引用计数为3)");
        QSharedPointer<MyData> another = QSharedPointer<MyData>::create(4);        dataList.append(another);
        log("添加新对象后,列表中有4个元素,前3个共享,第4个独立");    }
    log("\n【示例5】QObject派生类的使用");    {        QSharedPointer<MyObject> obj1 = QSharedPointer<MyObject>::create(5);        QSharedPointer<MyObject> obj2 = obj1;
        log("obj1和obj2共享同一个对象(引用计数为2)");
        // QObject可以设置父对象        QSharedPointer<MyObject> obj3 = QSharedPointer<MyObject>::create(6, obj1.data());        log("obj3的父对象是obj1");    }
    log("\n【示例6】检查指针有效性");    {        QSharedPointer<MyData> ptr = QSharedPointer<MyData>::create(7);
        if (ptr) {            log("指针有效,可以使用");            ptr->setValue(100);            log(QString("设置值: %1").arg(ptr->getValue()));        }
        ptr.clear();
        if (ptr.isNull()) {            log("指针已清空,isNull()返回true");        }    }
    log("\n【示例7】弱引用检查(与QWeakPointer配合)");    {        QSharedPointer<MyData> strong = QSharedPointer<MyData>::create(8);        log("创建强引用");
        // 创建弱引用(不影响引用计数)        // 这里只是演示,实际使用见QWeakPointer示例        log("可以创建QWeakPointer来观察对象而不增加引用计数");    }
    log("\n【重要提示】");    log("✓ QSharedPointer使用引用计数管理内存");    log("✓ 最后一个引用销毁时,对象自动删除");    log("✓ 适合多个地方共享同一个对象");    log("✓ 线程安全的引用计数(Qt 5.14+)");    log("✓ 注意避免循环引用(使用QWeakPointer解决)");}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宋康

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值