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
if (!weak.isNull()) {
QSharedPointer
// 使用strong
}
// C++11方式
std::weak_ptr
if (auto strong = weak.lock()) {
// 使用strong
}
QObject专用弱指针
// Qt特有方式
QPointer<QWidget> widgetPtr = new QWidget;
// 即使对象在其他地方被删除,widgetPtr会自动变为null
if (!widgetPtr.isNull()) {
widgetPtr->show();
}
// C++11无直接等价物,需要手动管理

浙公网安备 33010602011771号