ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Qt C++11 智能指针详解

Qt C++11 智能指针详解 C11 智能指针详解1. 核心概念RAII智能指针基于RAIIResource Acquisition Is Initialization把堆内存的生命周期绑定到栈对象自动释放避免 new/delete 配对失误与内存泄漏。C11 引入三种智能指针位于 指针所有权开销用途std::unique_ptrT独占零开销默认首选单一所有者std::shared_ptrT共享原子引用计数多个所有者共享std::weak_ptrT不持有弱引用打破循环引用观察者2. std::unique_ptr — 独占所有权零开销抽象大小等同裸指针默认情况下推荐默认使用。#include memory // 创建方式 1直接构造 std::unique_ptrSensor p1(new Sensor(0x01)); // 创建方式 2推荐std::make_uniqueC145.14.2 MSVC2017 支持 auto p2 std::make_uniqueSensor(0x02); // 解引用 p2-readData(); (*p2).reset(); // 所有权转移只能移动不能拷贝 auto p3 std::move(p2); // p2 变 nullptr // auto p4 p3; // ❌ 编译错误禁止拷贝 // 释放并取得裸指针之后必须手动 delete Sensor *raw p3.release(); // 重置 p3.reset(); // 析构并置空 p3.reset(new Sensor(0x03));数组版本auto arr std::make_uniquedouble[](64); // 64 个 double arr[0] 1.0; arr[1] 2.0;自定义删除器// 管理文件句柄 std::unique_ptrFILE, decltype(fclose) fp(fopen(data.csv, r), fclose); // Qt 对象使用 deleteLater 避免信号槽崩溃 auto widget std::unique_ptrQWidget, void(*)(QWidget*)( new QWidget, [](QWidget *w) { w-deleteLater(); } );3. std::shared_ptr — 共享所有权引用计数管理多个 shared_ptr 可指向同一对象最后一个析构时释放。// 创建方式 1 std::shared_ptrSensor p1(new Sensor(0x01)); // 创建方式 2推荐std::make_shared // 一次性分配对象 控制块省一次内存分配 auto p2 std::make_sharedSensor(0x02); // 共享引用计数 1 auto p3 p2; qDebug() p3.use_count(); // 2 // 拷贝 auto p4 p3; // 引用计数 3 qDebug() p4.use_count(); // 3 // 重置引用计数 -1归 0 时析构 p4.reset(); qDebug() p3.use_count(); // 2make_shared vs 直接 new// 推荐一次分配缓存友好 auto p std::make_sharedSensor(0x01); // 不推荐两次分配对象 控制块且无 weak_ptr 安全问题 std::shared_ptrSensor p(new Sensor(0x01));自定义删除器// 数组C17 才支持 shared_ptrT[]C11 用删除器 std::shared_ptrdouble arr( new double[64], std::default_deletedouble[]() ); // Qt 对象 auto btn std::shared_ptrQPushButton( new QPushButton(OK), [](QPushButton *b) { b-deleteLater(); } );4. std::weak_ptr — 不持有所有权只观察 shared_ptr 管理的对象不增加引用计数用于打破循环引用。auto sp std::make_sharedSensor(0x01); std::weak_ptrSensor wp sp; // 不增加计数 // 安全访问lock() 返回 shared_ptr对象已死则返回空 if (auto locked wp.lock()) { locked-readData(); // 安全使用 } else { qDebug() 对象已被销毁; } // 检查是否过期 if (wp.expired()) { ... } // 使用计数 qDebug() wp.use_count();经典场景打破循环引用class Node { public: std::shared_ptrNode next; std::weak_ptrNode prev; // ❌ 若用 shared_ptr环上对象永不释放 }; // 父子关系父强持有子子弱引用父 class Parent; class Child { public: std::weak_ptrParent parent; // 弱引用父 }; class Parent { public: std::vectorstd::shared_ptrChild children; // 强持有子 };5. Qt 智能指针 vs STL 智能指针Qt 自带智能指针但C11 起官方推荐用 STL 版本Qt 文档已明确Qt 指针STL 等价建议QScopedPointerTstd::unique_ptrT✅ 用std::unique_ptrQSharedPointerTstd::shared_ptrT✅ 用std::shared_ptrQWeakPointerTstd::weak_ptrT✅ 用std::weak_ptrQPointerT无直接等价仅限 QObject✅ Qt 专用保留QPointer — Qt 特有只能用于 QObject 派生类被delete时自动置空但本身不管理生命周期QPushButton *raw new QPushButton; QPointerQPushButton p raw; delete raw; // p 现在自动为 nullptr不会悬空 if (p) { ... } // 安全检查// 实战信号槽中保存对方指针 QPointerSensor m_sensor; // 成员变量 void onTimeout() { if (!m_sensor) return; // 对方可能已被销毁 m_sensor-readData(); }6. 实战选择决策树是否管理 QObject有 parent 机制 ├─ 是 → 让 parent 接管或用 QPointerT 当观察者 └─ 否 → 需要共享吗 ├─ 否 → std::unique_ptrT 首选 ├─ 是 → std::shared_ptrT 多所有者 └─ 出现循环引用 → 配合 std::weak_ptr7. 常见陷阱陷阱 1用裸指针构造多个 shared_ptr → 双重释放Sensor *raw new Sensor(0x01); std::shared_ptrSensor p1(raw); std::shared_ptrSensor p2(raw); // ❌ 两个独立控制块双重释放 // ✅ auto p1 std::make_sharedSensor(0x01); // auto p2 p1;陷阱 2循环引用 → 内存泄漏class A { std::shared_ptrB b; // A 持 B }; class B { std::shared_ptrA a; // B 持 A → 永不释放 }; // ✅ 其中一个改用 std::weak_ptr陷阱 3this 直接交给 shared_ptrclass Widget { void registerSelf() { // ❌ 把 this 包装成 shared_ptr外部析构时本对象被双重销毁 manager.registerObj(std::shared_ptrWidget(this)); } }; // ✅ 让类继承 std::enable_shared_from_this class Widget : public std::enable_shared_from_thisWidget { void registerSelf() { manager.registerObj(shared_from_this()); } }; // 前提Widget 必须已经由 shared_ptr 管理陷阱 4make_shared 与 weak_ptr 共存的延迟释放struct Big { char data[1024 * 1024]; // 1MB }; { auto sp std::make_sharedBig(); // 对象控制块同一块内存 std::weak_ptrBig wp sp; sp.reset(); // 引用计数归 0但控制块还在wp 没死 // 此时 Big 对象内存未释放直到 wp 析构 } // ✅ 若 Big 很大且 wp 长寿应分开 std::shared_ptrBig(new Big)陷阱 5智能指针管理数组却当单对象用std::unique_ptrSensor p(new Sensor[10]); // ❌ 调用 delete 而非 delete[] // ✅ std::unique_ptrSensor[] p(new Sensor[10]); // 或 auto p std::make_uniqueSensor[](10);8. Qt 项目实战示例class SensorManager { public: void addSensor(quint8 id) { // 独占持有使用 deleteLater 删除器 auto sensor std::unique_ptrSensor, void(*)(Sensor*)( new Sensor(id), [](Sensor *s) { s-deleteLater(); } ); m_sensors.push_back(std::move(sensor)); } std::shared_ptrPressureBuffer getBuffer() { // 缓存返回 shared_ptr多个视图共享 if (!m_buffer) { m_buffer std::make_sharedPressureBuffer(); } return m_buffer; } // 信号槽中安全持有对方 QPointerQWidget m_viewWidget; std::vectorstd::unique_ptrSensor, void(*)(Sensor*) m_sensors; std::shared_ptrPressureBuffer m_buffer; };9. 核心原则速记默认选 std::unique_ptr零开销、表达力强需要共享才用 std::shared_ptr避免滥用导致引用计数遍布优先 std::make_unique / std::make_shared少一次分配、异常安全循环引用必配 std::weak_ptrQObject 优先让 parent 管理生命周期跨线程观察用 QPointer不要用裸指针初始化多个智能指针自定义删除器配合 Qt deleteLater避免信号槽中悬空移动语义优于拷贝auto p2 std::move(p1) 比 auto p2 p1 高效
返回列表