C++中的智能指针与Qt中的智能指针
C++ 03
auto_ptr
auto_ptr
在C++11标准中已经被废弃。
先看一个例子:
1 |
|
输出:
1 | constructor:6 |
可以看到,auto_ptr会在它自己被释放时自动析构它持有的对象。
它有一些已知的缺陷:
无法接受数组分配对象
1
auto_ptr<int[]> p1(new int[4]); // 编译错误
赋值给另一个auto_ptr时会转移所有权并将右值自动指针重置为null指针。基于这个原因,它无法在STL中使用。然后,它就被废弃了。
C++ 11
unique_ptr
unique_ptr
是从 C++ 11 开始,定义在 unique_ptr
不能指向一个对象,不能进行复制操作只能进行移动操作。它是auto_ptr的替代者。
它解决了auto_ptr
的一些缺陷,支持数组,可以在STL中使用。
1 | unique_ptr<int> p1(new int(5)); |
但可以用move转移所有权。
1 | unique_ptr<int> p1(new int(5)); |
什么时候用
当有可能出现异常安全时
1
2
3
4
5{
A* a = new A();
a->do_something(); // 可能会发生异常
delete a;
}你不想要自己delete的时候
shared_ptr
shared_ptr
也是从C++11开始引入的。它代表的是共享所有权,即可以多个shared_ptr共享同一块内存。
shared_ptr内部是利用引用计数来实现内存的自动管理,每当复制一个shared_ptr,引用计数会+1。当一个shared_ptr离开作用域时,引用计数会-1。当引用计数为0的时候,则delete内存。
1 | shared_ptr<int> p1 = make_shared<int>(); |
什么时候使用
- 当有可能多个对象同时管理同一个内存时。通常情况下,你都需要考虑你是否真的需要shared_ptr,还是unique_ptr已经满足你的需求。
weak_ptr
weak_ptr
是shared_ptr的一个辅助,它并不真正持有对象。
1 | auto sp = make_shared<int>(42); |
可以通过它观测shared_ptr的引用计数。
1 | { |
可以通过lock函数将它转成shared_ptr可以拿到具体对象的值。
1 | { |
什么时候使用
解决shared_ptr双向引用的问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using namespace std;
struct B;
struct A { shared_ptr<B> b; };
struct B { shared_ptr<A> a; }; // 此处应改成 struct B { weak_ptr<A> a; };
weak_ptr<A> wa;
weak_ptr<B> wb;
void observe()
{
cout << wa.use_count() << " " << wb.use_count() << endl;
}
int main()
{
{
auto pa = make_shared<A>();
auto pb = make_shared<B>();
wa = pa; wb = pb;
pa->b = pb; pb->a = pa;
observe();
}
observe();
}例如上面的例子,输出是
1
22 2
1 1引用计数始终不会变成0
我们把B结构体中的改成weak_ptr再试一下。输出变成了
1
21 2
0 0
Qt
QPointer
严格意义上讲,QPointer
并不算是智能指针。智能指针是为了自动释放内存资源而设计的,而QPointer只是它持有的对象被销毁后,它将自动置空。且它只使用于QObject的实例。
举个例子:
1 |
|
先输出一个具体的内存地址,再输出false。
如果换成QPointer呢?
1 | QPointer<FB> fb = new FB(fa); |
输出的就是true了,因为它会自动置空。
当然,你可以用QPointer的isNull()方法更优雅的判空。
QPointer曾计划被废弃,但为了支持老代码被保留了下来。
QScopedPointer
QScopedPointer
保证当当前范围消失时指向的对象将被删除。这个智能指针只能在本作用域里使用,不希望被转让,因为它的拷贝构造和赋值操作都是私有的。
1 |
|
输出:
1 | foo |
QScopedArrayPointer
QScopedArrayPointer
继承自QScopedPointer
,唯一的不同是它专门处理数组,离开作用域后,它会使用delete []
来删除对象。
1 | void foo() |
QSharedPointer
它与C++11中的shared_ptr
类似,可以多个QSharedPointer共享一块内存,线程安全,也支持自定义deleter,同样使用引用计数的方式来实现内存的自动管理。
我们可以使用new的方式来创建它:
1 | QSharedPointer<FA> fa(new FA); |
也可以用它的静态函数create(), create的参数是对象的构造函数的参数:
1 | auto fa = QSharedPointer<FA>::create(); |
可以通过data()拿到裸指针。
1 | auto naked_ptr = fa.data(); |
也可以自定义删除器。附一个完整例子:
1 |
|
输出:
1 | foo |
可以安全的放到容器中:
1 | typedef QSharedPointer<FA> FAPtr; |
QWeakPointer
与C++11中的weak_ptr/shared_ptr类似,QWeakPointer也是QSharedPointer的辅助。它是一个弱引用,不会改变引用计数。
同样地,可以用它来避免双向引用的问题。(此处不再举例)
QSharedDataPointer
这是为配合 QSharedData 实现隐式共享(写时复制 copy-on-write))而提供的便利工具。
隐式共享的目的是最大化资源使用和最小化拷贝代价。
Qt中众多的类都使用了隐式共享技术,比如QPixmap、QByteArray、QString。
QExplicitlySharedDataPointer
这是为配合 QSharedData 实现显式共享而提供的便利工具。
QExplicitlySharedDataPointer 和 QSharedDataPointer 非常类似,但是它禁用了写时复制功能。
Reference
- https://stackoverflow.com/questions/5026197/what-c-smart-pointer-implementations-are-available
- https://www.geeksforgeeks.org/auto_ptr-unique_ptr-shared_ptr-weak_ptr-2/
- https://cloud.tencent.com/developer/article/1517336
- https://wiki.qt.io/Smart_Pointers
- https://stackoverflow.com/questions/22304118/what-is-the-difference-between-qpointer-qsharedpointer-and-qweakpointer-classes