简要介绍一下C++11以后的智能指针,有std::unique_ptr<T>
,std::shared_ptr<T>
,std::weak_ptr<T>
,并且废弃了std::auto_ptr<T>
首先智能指针是一个RAII类模型,用于动态分配内存,其设计思想是将基本类型指针封装为(模板)类对象指针,并在离开作用域时调用析构函数,使用delete删除指针所指向的内存空间。
智能指针的作用是,能够处理内存泄漏问题和空悬指针问题。
对于unique_ptr,实现独占式拥有的概念,同一时间只能有一个智能指针可以指向该对象,因为无法进行拷贝构造和拷贝赋值,但是可以进行移动构造和移动赋值;
对于shared_ptr,实现共享式拥有的概念,即多个智能指针可以指向相同的对象,该对象及相关资源会在其所指对象不再使用之后,自动释放与对象相关的资源;
对于weak_ptr,解决shared_ptr相互引用时,两个指针的引用计数永远不会下降为0,从而导致死锁问题。而weak_ptr是对对象的一种弱引用,可以绑定到shared_ptr,但不会增加对象的引用计数。
1 unique_ptr
每个 unique_ptr 指针指向的堆内存空间的引用计数,都只能为 1,一旦该 unique_ptr 指针放弃对所指堆内存空间的所有权,则该空间会被立即释放回收。
创建unique_ptr
1
2
3
4
5
6
7
8
9// 创建空的unique_ptr
std::unique_ptr<int> p1();
std::unique_ptr<int> p2(nullptr);
// 有指向的
std::unique_ptr<int> p3(new int);
// 创建空的unique_ptr
std::unique_ptr<int> p4(new int);
std::unique_ptr<int> p5(p4);//错误的,没有拷贝构造
std::unique_ptr<int> p5(std::move(p4));//正确的,有移动构造值得一提的是,对于调用移动构造函数的 p4 和 p5 来说,p5 将获取 p4 所指堆空间的所有权,而 p4 将变成空指针(nullptr)。
unique_ptr 可以指向一个数组
1
2
3
4
5
6
7std::unique_ptr<int[]> uptr = std::make_unique<int[]>(10);
for (int i = 0; i < 10; i++) {
uptr[i] = i * i;
}
for (int i = 0; i < 10; i++) {
std::cout << uptr[i] << std::endl;
}C++14新增make_unique
1 | unique_ptr<int> p1 = make_unique<int>(100); |
将unique_ptr转换成shared_ptr类型
转换条件:如果unique_ptr为右值时,他就可以赋值给shared_ptr。
原因:因为shared_ptr包含一个显示构造函数,可用于将右值unique_ptr转换为shared_ptr,shared_ptr将接管原来归unqiue_ptr所拥有的对象。unique_ptr的简单实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43template<typename T>
class UniquePtr
{
public:
UniquePtr(T *pResource = NULL)
: m_pResource(pResource) {}
~UniquePtr(){ del();}
public:
void reset(T* pResource){// 先释放资源(如果持有), 再持有资源
del();
m_pResource = pResource;
}
T* release(){// 返回资源,资源的释放由调用方处理
T* pTemp = m_pResource;
m_pResource = nullptr;
return pTemp;
}
T* get(){// 获取资源,调用方应该只使用不释放,否则会两次delete资源
return m_pResource;
}
public:
operator bool() const{ // 是否持有资源
return m_pResource != nullptr;
}
T& operator * (){
return *m_pResource;
}
T* operator -> (){
return m_pResource;
}
private:
void del(){
if (nullptr == m_pResource) return;
delete m_pResource;
m_pResource = nullptr;
}
private:
UniquePtr(const UniquePtr &) = delete; // 禁用拷贝构造
UniquePtr& operator = (const UniquePtr &) = delete; // 禁用拷贝赋值
private:
T *m_pResource;
};
2 shared_ptr
参考C++ 智能指针详解(二)——shared_ptr与weak_ptr
特点总结如下
- 共享所有权
- 存储成本较裸指针多了个引用计数指针(相关控制块-共享)
- 接口慎用(会导致蔓延问题)
- 线程安全,引用计数增减会减慢多核性能
- 支持拷贝构造,支持移动
shared_ptr使用注意事项
- 避免循环引用,解决办法使用弱引用指针
- 避免对原始指针多于两组的所有权(使用enable_shared_from_this<>避免this被多组所有权)
- 避免在shared引用对象成员中再次使用shared_ptr,这样会导致资源重复释放
shared_ptr内存模型
- API
函数API | 含义 |
---|---|
p.get() | 返回p中保存的指针。小心使用,若智能指针释放了其对象,返回的指针所指向的对象也就消失了 |
p.use_count() | 返回与p共享对象的智能指针数量;可能很慢,注意用于调试 |
p.unique() | 若p.use_count()为1,返回true;否则返回false |
p = q | p和q都是shared_ptr,所保存的指针能相互转换。此操作会递减p的引用计数,递增q的引用计数;若p的引用计数变为0,则将其管理的原内存释放 |
make_shared |
返回一个shared_ptr,指向一个动态分配的类型为T的对象。使用args初始化此对象 |
shared_ptr |
p是share_ptr q的拷贝;此操作会递增q中的计数器。q中的指针类型必须能转换为T* |
shared_ptr |
p从unique_ptr u那里接管了对象的所有权;将u置为空 |
shared_ptr |
p接管了内置指针q所指向的对象的所有权。q必须能转换为T*类型。p将使用可调用的对象d来代替delete |
shared_ptr |
p是share_ptr p2的拷贝;此操作会递增p2中的计数器。p2中的指针类型必须能转换为T*,p将使用可调用的对象d来代替delete |
p.reset() | p置为空。若p是唯一指向其对象的shared_ptr,reset会释放此对象 |
p.reset(q) | 传递了参数内置指针q,会令p指向q |
p.reset(q,d) | 传递了参数d,将会调用d而不是delete来释放q |
p.swap(q)或swap(p,q) | 交换两个智能指针 |
3 weak_ptr
- API
函数API | 含义 |
---|---|
weak_ptr |
空weak_ptr可以指向类型为T的对象 |
weak_ptr |
与shared_ptr sp指向相同对象的weak_ptr。T必须能转换为sp指向的类型 |
w = p | p可以是一个shared_ptr或一个weak_ptr。赋值后w与p共享对象 |
w.reset() | 将w置为空 |
w.use_count() | 与w共享对象的shared_ptr的数量 |
w.expired() | 若w.use_count()为0,返回true,否则返回false |
w.lock() | 如果expired为true,返回一个空shared_ptr;否则返回一个指向w的对象的shared_ptr |
感谢看到这里,在记录中收获成长,道阻且长