
QSharedPointer 是 Qt 提供的一个共享所有权的智能指针 ,用于自动管理动态分配对象的生命周期。它通过引用计数(Reference Counting)机制实现:当最后一个指向对象的 QSharedPointer 被销毁或重置时,对象会被自动 delete。这在 C++ 开发中能有效避免内存泄漏,尤其适合需要多模块共享资源的场景(如流媒体中的缓冲区、视频帧对象等)。
一、核心机制:引用计数
-
强引用计数:记录当前有多少个 QSharedPointer 指向同一个对象。
-
弱引用计数(通过 QWeakPointer):不增加强引用计数,仅观察对象是否存在(用于解决循环引用)。
-
当强引用计数降为 0 时,对象被自动释放。
二、基本用法
1. 创建 QSharedPointer
QSharedPointer 通常通过以下方式创建:
(1)从原始指针构造(需确保指针唯一由 QSharedPointer 管理)
cpp
#include <QSharedPointer>
#include <QDebug>
class MyClass {
public:
MyClass(int v) : value(v) { qDebug() << "MyClass constructed"; }
~MyClass() { qDebug() << "MyClass destroyed"; }
int value;
};
int main() {
// 从原始指针构造(注意:原始指针必须是通过 new 分配的)
QSharedPointer<MyClass> ptr(new MyClass(10));
return 0;
}
// 输出:
// MyClass constructed
// MyClass destroyed(main 结束时 ptr 析构,引用计数归零)
(2)使用 QSharedPointer::create()(Qt 5.10+ 推荐)
更安全的方式(避免原始指针暴露):
cpp
auto ptr = QSharedPointer<MyClass>::create(20); // 等价于 new MyClass(20),但更安全
(3)拷贝构造/赋值(共享所有权)
cpp
QSharedPointer<MyClass> ptr1(new MyClass(30));
QSharedPointer<MyClass> ptr2 = ptr1; // 拷贝构造,引用计数+1(变为2)
ptr1.reset(); // ptr1 放弃所有权,引用计数-1(变为1)
// 此时 ptr2 仍指向对象,main 结束时 ptr2 析构,对象被释放
2. 访问对象成员
通过 operator->或 operator*访问对象:
cpp
QSharedPointer<MyClass> ptr = QSharedPointer<MyClass>::create(40);
qDebug() << ptr->value; // 输出 40(等价于 (*ptr).value)
3. 重置(放弃所有权)
-
reset():释放当前对象的所有权(若引用计数归零则删除对象),并置空。 -
reset(T* other):释放当前对象,接管新对象的所有权。
cpp
QSharedPointer<MyClass> ptr(new MyClass(50));
ptr.reset(); // 原对象被删除(输出 "MyClass destroyed"),ptr 变为 nullptr
ptr.reset(new MyClass(60)); // 接管新对象(输出 "MyClass constructed")
4. 判断有效性
-
isNull()/isEmpty():判断是否为空(无对象)。 -
operator bool():隐式转换为 bool(非空时为 true)。
cpp
QSharedPointer<MyClass> ptr;
if (ptr.isNull()) {
qDebug() << "ptr is null";
}
if (ptr) { // 等价于 !ptr.isNull()
qDebug() << "ptr is not null";
}
5. 获取原始指针
data()或get():返回原始指针(谨慎使用,避免外部手动 delete)。
cpp
MyClass* rawPtr = ptr.data();
rawPtr->value = 100; // 直接修改对象(不推荐,破坏封装)
三、高级用法
1. 自定义删除器(Deleter)
默认情况下,QSharedPointer 使用 delete释放对象。若对象通过其他方式分配(如 malloc、fopen打开的文件),可自定义删除器。
**示例:管理 FILE资源*
cpp
#include <cstdio>
// 自定义删除器:关闭文件
void fileCloser(FILE* fp) {
if (fp) fclose(fp);
qDebug() << "File closed";
}
int main() {
// 用 lambda 作为删除器(更灵活)
QSharedPointer<FILE> filePtr(fopen("test.txt", "r"), [](FILE* fp) {
if (fp) fclose(fp);
qDebug() << "Custom deleter: File closed";
});
if (filePtr) {
char buf[100];
fgets(buf, sizeof(buf), filePtr.data());
qDebug() << "Read:" << buf;
}
// 离开作用域时,filePtr 析构,调用自定义删除器关闭文件
return 0;
}
2. 与 QWeakPointer 配合(解决循环引用)
当两个对象互相持有 QSharedPointer 时,会导致循环引用(引用计数无法归零,内存泄漏)。此时需用 QWeakPointer 打破循环(弱引用不增加强引用计数)。
示例:循环引用问题
cpp
class B;
class A {
public:
QSharedPointer<B> bPtr;
~A() { qDebug() << "A destroyed"; }
};
class B {
public:
QSharedPointer<A> aPtr; // 循环引用!A 和 B 互相强引用
~B() { qDebug() << "B destroyed"; }
};
int main() {
QSharedPointer<A> a(new A);
QSharedPointer<B> b(new B);
a->bPtr = b; // a 强引用 b(b 的引用计数=2)
b->aPtr = a; // b 强引用 a(a 的引用计数=2)
// 离开作用域时,a 和 b 的引用计数各减1(变为1),不会触发析构!
return 0;
}
// 输出:(无析构信息,内存泄漏)
解决方案:用 QWeakPointer 替代一方的强引用
cpp
class B;
class A {
public:
QSharedPointer<B> bPtr;
~A() { qDebug() << "A destroyed"; }
};
class B {
public:
QWeakPointer<A> aPtr; // 弱引用,不增加 a 的强引用计数
~B() { qDebug() << "B destroyed"; }
};
int main() {
QSharedPointer<A> a(new A);
QSharedPointer<B> b(new B);
a->bPtr = b; // b 的引用计数=2(a->bPtr 和 b 本身)
b->aPtr = a; // a 的引用计数仍为1(仅 a 本身)
// 离开作用域时:
// a 析构 → a 的引用计数-1(变为0)→ 调用 A 的析构 → a->bPtr 析构 → b 的引用计数-1(变为1)
// b 析构 → b 的引用计数-1(变为0)→ 调用 B 的析构 → b->aPtr 是弱引用,无影响
return 0;
}
// 输出:
// A destroyed
// B destroyed
四、注意事项
-
避免混合管理:不要用多个独立智能指针(如 QSharedPointer 和 std::shared_ptr)管理同一原始指针,会导致重复释放。
-
线程安全 :QSharedPointer 的引用计数操作是线程安全的(原子操作),但对象本身的访问仍需同步(如多线程读写对象成员)。
-
与 QObject 的配合 :QObject 自带父子内存管理机制(
deleteLater),若用 QSharedPointer 管理 QObject 派生类,需注意:-
避免同时设置父对象和 QSharedPointer(可能导致双重释放)。
-
推荐用 QSharedPointer 管理无父对象的 QObject,或用
QObject::setParent(nullptr)解除父子关系。
-
-
不支持数组 :QSharedPointer 默认用
delete释放对象,若需管理数组(new T[]),应使用QSharedPointer<T[]>(Qt 5.6+),它会调用delete[]。
五、总结
QSharedPointer 是 Qt 中管理动态对象的核心工具,通过引用计数自动释放资源,适合多模块共享对象的场景。关键要点:
-
优先用
QSharedPointer<T>::create()构造,避免原始指针暴露。 -
复杂资源管理用自定义删除器。
-
循环引用用 QWeakPointer 解决。
-
注意线程安全和与 QObject 的配合。
