QT智能指针

1.智能指针的概念

智能指针是一种自动化内存管理工具,通过RAII(Resource Acquisition Is Initialization)技术实现。它们在构造时获取资源,在析构时自动释放资源,从而避免内存泄漏。

Qt 家族的指针类型

智能指针类型 等效STL类型 所有权语义 线程安全 适用场景
QScopedPointer std::unique_ptr 独占所有权 作用域内资源管理
QSharedPointer std::shared_ptr 共享所有权 多所有者共享资源
QWeakPointer std::weak_ptr 弱引用 打破循环引用
QPointer 观察者模式 QObject对象观察
QScopedArrayPointer std::unique_ptr<T[]>` 独占数组 动态数组管理
QSharedDataPointer 隐式共享 值类型数据共享

各类智能指针详解

QPointer-QObject守护指针

特点:

  • 专门用于QObject及其派生类

  • 不管理生命周期,只提供安全访问

  • 指向对象被删除时自动置为nullptr

// 创建和使用
QPointer<QLabel> label = new QLabel("Hello World");
label->setAlignment(Qt::AlignCenter);

// 安全检查
if (!label.isNull()) {
    label->show();
}

// 对象被删除时自动检测
delete label.data();  // 或者 label->deleteLater();
if (label.isNull()) {
    qDebug() << "Label已被删除,指针自动置空";
}

如果在...部分将该对象delete掉,则label会自动置为nullptr,而不是一个悬挂(dangling)的野指针。

QScopedPointer(std::unique_ptr)

特点:

  • 独占所有权,不可复制
  • 离开作用域自动释放资源
  • 轻量级,零开销抽象

在没有考虑智能指针时:

 void myFunction(bool useSubClass)
 {
     MyClass *p = useSubClass ? new MyClass() : new MySubClass;
     QIODevice *device = handsOverOwnership();

     if (m_value > 3) {
         delete p;
         delete device;
         return;
     }

     try {
         process(device);
     }
     catch (...) {
         delete p;
         delete device;
         throw;
     }

     delete p;
     delete device;
 }

需要在异常处理语句中多次书写delete语句,稍有不慎就会导致资源泄露。

采用智能指针后,我们就可以将这些异常处理语句简化:

 void myFunction(bool useSubClass)
 {
     QScopedPointer<MyClass> p(useSubClass ? new MyClass() : new MySubClass);
     QScopedPointer<QIODevice> device(handsOverOwnership());

     if (m_value > 3)
         return;

     process(device);
 }
// 基本用法
void processData() {
    QScopedPointer<Database> db(new Database());
    if (!db->connect()) {
        return; // 自动释放db
    }
    db->processQuery("SELECT * FROM table");
} // 自动调用db的析构函数

// 自定义删除器
QScopedPointer<FILE, decltype(&fclose)> file(fopen("data.txt", "r"), fclose);

// 数组版本
QScopedArrayPointer<int> buffer(new int[1024]);
std::fill(buffer.get(), buffer.get() + 1024, 0);

QSharedPointer(std::shared_ptr)

实现的是引用计数型的智能指针 ,可以被自由地拷贝和赋值,在任意的地方共享它,当没有代码使用(引用计数为0)它时才删除被包装的动态分配的对象。

特点:

  • 引用计数共享所有权
  • 线程安全(引用计数操作)
  • 支持自定义删除器
// 创建共享指针
QSharedPointer<NetworkRequest> createRequest(const QString &url) {
    return QSharedPointer<NetworkRequest>::create(url);
}

// 共享使用
void processRequest(QSharedPointer<NetworkRequest> request) {
    if (request->isValid()) {
        m_activeRequests.append(request); // 共享所有权
        request->execute();
    }
}

// 自定义删除器
QSharedPointer<Socket> socket(socketCreate(), socketDelete);

// 类型转换
QSharedPointer<Derived> derived = derivedObject;
QSharedPointer<Base> base = qSharedPointerCast<Base>(derived);

QWeakPointer(std::weak_ptr)

QWeakPointer 是为配合 QSharedPointer 而引入的一种智能指针,它更像是 QSharedPointer 的一个助手(因为它不具有普通指针的行为,没有重载operator*和->)。它的最大作用在于协助 QSharedPointer 工作,像一个旁观者一样来观测资源的使用情况。
特点:

  • 不增加引用计数
  • 解决循环引用问题
  • 必须与QSharedPointer配合使用
class Controller {
public:
    void setView(QSharedPointer<View> view) {
        m_weakView = view; // 弱引用,不增加计数
    }
    
    void updateView() {
        if (auto view = m_weakView.toStrongRef()) {
            view->refresh(); // 安全访问
        }
    }
    
private:
    QWeakPointer<View> m_weakView;
};

// 循环引用示例
class Parent;
class Child {
public:
    QSharedPointer<Parent> parent; // 强引用→循环引用
};

class Parent {
public:
    QSharedPointer<Child> child;
};

// 解决方案:使用弱引用
class Child {
public:
    QWeakPointer<Parent> parent; // 弱引用打破循环
};

智能指针的高级用法

const正确性详解

void SmartWorker::doWork(const SmartDataPtr& data) 中:

  • const 修饰的是 SmartDataPtr(智能指针本身),而不是它指向的SmartData对象
  • 意味着:你不能修改智能指针本身(比如让它指向另一个对象),但可以修改它指向的对象
const SmartDataPtr& data // 等同于 const QSharedPointer<SmartData>& data

// 1. 智能指针本身为const,对象可修改
void modifyObject(const QSharedPointer<Data>& data) {
    data->setValue(42); // ✓ 可以修改对象
    // data.reset();    // ✗ 不能修改指针本身
}

// 2. 指向const对象的智能指针
void readOnly(const QSharedPointer<const Data>& data) {
    int value = data->value(); // ✓ 可以读取
    // data->setValue(42);     // ✗ 不能修改
}

// 3. const智能指针指向const对象
void readOnlyPointer(const QSharedPointer<const Data>& const data) {
    // data.reset();           // ✗ 不能修改指针
    // data->setValue(42);     // ✗ 不能修改对象
}

与Qt容器结合

// 对象列表管理
QList<QSharedPointer<Employee>> employees;

void addEmployee(const QString& name) {
    employees.append(QSharedPointer<Employee>::create(name));
}

// 自动清理
employees.clear(); // 所有Employee对象自动释放

// 映射表使用
QMap<QString, QSharedPointer<Config>> configs;
configs.insert("database", QSharedPointer<DatabaseConfig>::create());

多线程环境使用

// 跨线程传递共享资源
class Worker : public QObject {
    Q_OBJECT
public:
    Worker(QSharedPointer<ThreadSafeData> data) : m_data(data) {}
    
public slots:
    void process() {
        QMutexLocker locker(&m_data->mutex);
        m_data->value += 1;
        emit resultReady(m_data->value);
    }
    
signals:
    void resultReady(int value);
    
private:
    QSharedPointer<ThreadSafeData> m_data;
};

// 在主线程中
QSharedPointer<ThreadSafeData> data(new ThreadSafeData);
Worker* worker = new Worker(data);
worker->moveToThread(workerThread);
connect(workerThread, &QThread::started, worker, &Worker::process);

最佳实践与常见陷阱

  • 默认选择:优先使用QScopedPointer

  • 共享资源:需要多所有者时使用QSharedPointer

  • 观察模式:对QObject使用QPointer

  • 循环引用:使用QWeakPointer打破循环

  • 数组管理:使用QScopedArrayPointer

// 陷阱1:混合使用裸指针和智能指针
void problematic() {
    Data* rawPtr = new Data();
    QSharedPointer<Data> smartPtr(rawPtr);
    // ...
    delete rawPtr; // ✗ 双重释放!
}

// 解决方案:始终使用智能指针管理生命周期
void correct() {
    QSharedPointer<Data> smartPtr(new Data());
    // 或者使用make函数
    auto smartPtr = QSharedPointer<Data>::create();
}

// 陷阱2:循环引用
class A {
    QSharedPointer<B> b;
};
class B {
    QSharedPointer<A> a; // 循环引用
};

// 解决方案:使用弱引用
class B {
    QWeakPointer<A> a; // 弱引用打破循环
};

常用 API 详解与技巧

QScopedPointer 常用 API

// 创建和初始化
QScopedPointer<T> ptr(new T()); // 从裸指针创建

// 访问对象
T* rawPtr = ptr.data();          // 获取裸指针(不释放所有权)
T& ref = *ptr;                   // 解引用访问对象
T* operator->()                  // 箭头运算符访问成员

// 释放所有权
T* rawPtr = ptr.take();          // 释放所有权,返回裸指针

// 重置
ptr.reset();                     // 释放当前对象
ptr.reset(new T());              // 释放旧对象,管理新对象

// 交换
ptr.swap(otherPtr);              // 交换两个智能指针的内容

QSharedPointer 常用 API

// 创建和初始化
QSharedPointer<T> ptr(new T()); // 从裸指针创建
QSharedPointer<T> ptr = QSharedPointer<T>::create(args...); // 推荐:使用create方法

// 检查状态
bool isNull = ptr.isNull();      // 是否为空指针
bool isvalid = ptr;              // 隐式转换,检查有效性
int refCount = ptr.use_count();  // 获取引用计数(调试用)

// 访问对象
T* rawPtr = ptr.data();          // 获取裸指针(不释放所有权)
T& ref = *ptr;                   // 解引用访问对象
T* operator->()                  // 箭头运算符访问成员

// 重置和交换
ptr.reset();                     // 释放当前对象,变为空指针
ptr.reset(new T());              // 释放旧对象,管理新对象
ptr.swap(otherPtr);              // 交换两个智能指针的内容

// 类型转换
QSharedPointer<Base> basePtr = qSharedPointerCast<Base>(derivedPtr);
QSharedPointer<Derived> derivedPtr = qSharedPointerDynamicCast<Derived>(basePtr);

QWeakPointer 常用 API

// 创建和初始化
QWeakPointer<T> weakPtr;         // 空弱指针
QWeakPointer<T> weakPtr(sharedPtr); // 从共享指针创建

// 检查状态
bool isNull = weakPtr.isNull();   // 是否为空
bool expired = weakPtr.isExpired(); // 指向的对象是否已被删除

// 转换为强引用
QSharedPointer<T> sharedPtr = weakPtr.toStrongRef(); // 转换为强引用
QSharedPointer<T> sharedPtr = weakPtr.lock();        // 同toStrongRef()

// 重置
weakPtr.reset();                  // 重置为空指针
weakPtr.clear();                  // 同reset()

基本别名定义

// 自定义结构体
struct MyData {
    int id;
    QString name;
    QVariant value;
    
    // 可选:添加一些便利方法
    QString toString() const {
        return QString("MyData{id: %1, name: %2}").arg(id).arg(name);
    }
};

// 为智能指针定义别名(通常在头文件中)
using MyDataPtr = QSharedPointer<MyData>;
using MyDataConstPtr = QSharedPointer<const MyData>;
using MyDataWeakPtr = QWeakPointer<MyData>;
using MyDataScopedPtr = QScopedPointer<MyData>;

// 对于需要数组的情况
using MyDataArrayPtr = QScopedArrayPointer<MyData>;

与QVariant和元对象系统集成

// 注册智能指针到元对象系统(如果需要)
Q_DECLARE_METATYPE(MyDataPtr)
Q_DECLARE_METATYPE(MyDataConstPtr)

// 在初始化代码中注册
qRegisterMetaType<MyDataPtr>("MyDataPtr");
qRegisterMetaType<MyDataConstPtr>("MyDataConstPtr");

// 现在可以在信号槽中使用
class DataProcessor : public QObject {
    Q_OBJECT
public slots:
    void processData(MyDataConstPtr data) {
        if (data) {
            qDebug() << "Processing:" << data->toString();
        }
    }
};

// 连接信号槽
DataManager manager;
DataProcessor processor;
QObject::connect(&manager, &DataManager::dataAdded, 
                 &processor, &DataProcessor::processData);

自定义删除器的高级用法

// 自定义删除器函数
void customDataDeleter(MyData* data) {
    qDebug() << "Deleting MyData with id:" << data->id;
    delete data;
}

// 使用自定义删除器
MyDataPtr dataWithCustomDeleter(static_cast<MyData*>(new MyData()), 
                                customDataDeleter);

// Lambda删除器
MyDataPtr dataWithLambdaDeleter(new MyData(), 
                               [](MyData* data) {
                                   qDebug() << "Lambda deleter for id:" << data->id;
                                   delete data;
                               });

// 成员函数删除器
class DataCleaner {
public:
    void cleanup(MyData* data) {
        qDebug() << "Cleaner deleting data with id:" << data->id;
        delete data;
    }
};

DataCleaner cleaner;
MyDataPtr dataWithMemberDeleter(new MyData(),
                               std::bind(&DataCleaner::cleanup, &cleaner, 
                                         std::placeholders::_1));

总结

Qt智能指针与C++11智能指针对比

特性 Qt智能指针 C++11智能指针 说明
共享所有权指针 QSharedPointer<T> std::shared_ptr<T> 两者功能类似,都使用引用计数
独占所有权指针 QScopedPointer<T> std::unique_ptr<T> 两者功能类似,但API略有不同
弱引用指针 QWeakPointer<T> std::weak_ptr<T> 两者功能类似,用于打破循环引用
对象感知弱指针 QPointer<T> 无直接等价物 Qt特有,专门用于QObject及其派生类
创建方式 QSharedPointer<T>::create(args) std::make_shared<T>(args) 两者都提供工厂函数创建对象
多态类型转换 dynamicCast<T>(), staticCast<T>() std::dynamic_pointer_cast<T>(), std::static_pointer_cast<T>() 功能类似,但Qt使用成员函数形式
QObject集成 支持自动父对象管理 无直接支持 Qt智能指针可与QObject父子关系协同工作
线程安全性 引用计数线程安全 引用计数线程安全 两者都保证引用计数操作的原子性
自定义删除器 支持 支持 两者都允许指定自定义删除逻辑
与QVariant集成 支持 不支持 Qt智能指针可存储在QVariant中
空指针检查 isNull()方法 直接转换为bool Qt提供显式方法检查是否为空
对象跟踪 QPointer自动跟踪QObject生命周期 无直接等价物 Qt特有功能,当QObject被删除时自动置空

代码示例对比

创建智能指针

// Qt方式
auto qtPtr = QSharedPointer<MyClass>::create(args);

// C++11方式
auto stdPtr = std::make_shared<MyClass>(args);

类型转换

// Qt方式
QSharedPointer<Derived> derived = base.dynamicCast<Derived>();

// C++11方式
std::shared_ptr<Derived> derived = std::dynamic_pointer_cast<Derived>(base);

弱指针使用
// Qt方式
QWeakPointer weak = shared;
if (!weak.isNull()) {
QSharedPointer strong = weak.toStrongRef();
// 使用strong
}

// C++11方式
std::weak_ptr weak = shared;
if (auto strong = weak.lock()) {
// 使用strong
}

QObject专用弱指针

// Qt特有方式
QPointer<QWidget> widgetPtr = new QWidget;
// 即使对象在其他地方被删除,widgetPtr会自动变为null
if (!widgetPtr.isNull()) {
    widgetPtr->show();
}

// C++11无直接等价物,需要手动管理
posted @ 2025-09-02 18:07  一楼二栋  阅读(40)  评论(0)    收藏  举报