定义
为了实现指针自动回收的对象,表现和指针一样,实际上它利用了栈的机制,每一个智能指针都是一个模板类,调用智能指针实际上是创建了一个智能指针的对象,对象生命周期到达尽头的时候,会自动调用智能指针的析构函数,在析构函数里,释放掉它管理的内存,从而避免手动delete。
Java里有类似实现(jdk1.2以后有strong,soft,weak,phantom引用)
C++11之前有auto_ptr,但是与STL不兼容,C++11时被弃用
shared_ptr是引用计数的智能指针,而unique_ptr不是。这意味着,可以有多个shared_ptr实例指向同一块动态分配的内存,当最后一个shared_ptr离开作用域时,才会释放这块内存。shared_ptr也是线程安全的。另一方面,unique_ptr意味着所有权。单个unique_ptr离开作用域时,会立即释放底层内存。
默认的智能指针应该是unique_ptr。只有需要共享资源时,才使用shared_ptr,weak_ptr是shared_ptr的弱引用,不增加其引用计数器。
指针 | 简要描述 |
---|---|
shared_ptr | 允许多个指针指向同一个对象 |
unique_ptr | 独占所指向的对象 |
weak_ptr | shared_ptr的弱引用 |
share_ptr
允许多个shared_ptr指向同一个对象,使用引用计数器来判断是否需要释放。访问时像C++一般的指针一样。
最安全和高效的方法是调用make_shared库函数,该函数会在堆中分配一个对象并初始化,最后返回指向此对象的share_ptr实例。如果你不想使用make_ptr,也可以先明确new出一个对象,然后把其原始指针传递给share_ptr的构造函数。
可以*pInt
也可以pInt->
。指针的拷贝p=q会使q的引用计数++,p的–,如下:
#include <iostream>
#include <memory>
using namespace std;
class Example
{
public:
Example() : e(1) { cout << "Example Constructor..." << endl; }
~Example() { cout << "Example Destructor..." << endl; }
int e;
};
int main() {
shared_ptr<Example> pInt(new Example());
shared_ptr<Example> pInt3=make_shard<Example>();
auto pInt4=pInt3;
cout << (*pInt).e << endl;
cout << "pInt引用计数: " << pInt.use_count() << endl;
shared_ptr<Example> pInt2 = pInt;
cout << "pInt引用计数: " << pInt.use_count() << endl;
cout << "pInt2引用计数: " << pInt2.use_count() << endl;
cout << "pInt3引用计数: " << pInt3.use_count() << endl;
cout << "pInt4引用计数: " << pInt4.use_count() << endl;
pInt3=pInt;
cout << "pInt引用计数: " << pInt.use_count() << endl;
cout << "pInt2引用计数: " << pInt2.use_count() << endl;
cout << "pInt3引用计数: " << pInt3.use_count() << endl;
cout << "pInt4引用计数: " << pInt4.use_count() << endl;
}
Example Constructor...
Example Constructor...
1
pInt引用计数: 1
pInt引用计数: 2
pInt2引用计数: 2
pInt3引用计数: 2
pInt4引用计数: 2
pInt引用计数: 3
pInt2引用计数: 3
pInt3引用计数: 3
pInt4引用计数: 1
Example Destructor...
Example Destructor...
shared_ptr提供了两个函数来检查其共享的引用计数值,分别是unique()和use_count()。
在前面,我们已经多次使用过use_count()函数,该函数返回当前指针的引用计数值。值得注意的是use_count()函数可能效率很低,应该只把它用于测试或调试。
unique()函数用来测试该shared_ptr是否是原始指针唯一拥有者,也就是use_count()的返回值为1时返回true,否则返回false。
unique_ptr
与shared_ptr不同,某一时刻,只能有一个unique_ptr指向一个给定的对象。因此,当unique_ptr被销毁,它所指的对象也会被销毁。unique_ptr没有类似make_shared函数只能通过unique_ptr<int> pInt(new int(5));
无法复制构造和赋值。但是可以从函数中返回。也可以同过指针操作。类似auto_ptr
可以进行移动构造和移动赋值操作,通过std::move()函数。
int main() {
unique_ptr<int> pInt(new int(5));
unique_ptr<int> pInt2 = std::move(pInt); // 转移所有权
//cout << *pInt << endl; // 出错,pInt为空
cout << *pInt2 << endl;
unique_ptr<int> pInt3(std::move(pInt2));
}
常见场景:
- 为动态申请的资源提供异常安全保证
void Func() { unique_ptr<int> p(new int(5)); // ...(可能会抛出异常) }
- 返回函数内动态申请资源的所有权
unique_ptr<int> Func(int p) { unique_ptr<int> pInt(new int(p)); return pInt; // 返回unique_ptr } int main() { int p = 5; unique_ptr<int> ret = Func(p); cout << *ret << endl; // 函数结束后,自动释放资源 }
- 在容器中保存指针
int main() { vector<unique_ptr<int>> vec; unique_ptr<int> p(new int(5)); vec.push_back(std::move(p)); // 使用移动语义 }
- 管理动态数组
int main() { unique_ptr<int[]> p(new int[5] {1, 2, 3, 4, 5}); p[0] = 0; // 重载了operator[] }
- 作为auto_ptr的代替品
weak_ptr
使用计数器的shared_ptr很容易就产生循环引用的问题。这时如果用一个weak_ptr来指向shared_ptr不会增加他的引用计数器。可以使用lock()函数来安全地获取一个weak_ptr的一个shared_ptr的引用,如果此时shared_ptr已被reset,则返回一个空的shared_ptr,赋值语句不成功。也可以安全地通过expired()函数来判断是否过期。
使用时不能通过shared_ptr传统指针操作,但是可以通过lock()来获取他的share_ptr()。可用来解决循环引用的问题。
class ClassB;
class ClassA
{
public:
ClassA() { cout << "ClassA Constructor..." << endl; }
~ClassA() { cout << "ClassA Destructor..." << endl; }
weak_ptr<ClassB> pb; // 在A中引用B
};
class ClassB
{
public:
ClassB() { cout << "ClassB Constructor..." << endl; }
~ClassB() { cout << "ClassB Destructor..." << endl; }
weak_ptr<ClassA> pa; // 在B中引用A
};
int main() {
shared_ptr<ClassA> spa = make_shared<ClassA>();
shared_ptr<ClassB> spb = make_shared<ClassB>();
spa->pb = spb;
spb->pa = spa;
// 函数结束,思考一下:spa和spb会释放资源么?
}
ClassA Constructor...
ClassB Constructor...
ClassA Destructor...
ClassB Destructor...
Program ended with exit code: 0