七.前言
在前面一节,我们简单实现了三种类型资源的”指针对象”。其实在c++11的标准库中已经为我们准备了这样的指针对象——智能指针,分别是:shared_ptr , unique_ptr(取代了auto_ptr) , weak_ptr。下面我们简单来介绍一下这三类智能指针的特点和适用情况。 |
八.shared_ptr智能指针
shared_ptr指针适用的就是前一节所说的第3类资源指针。 shared_ptr是模板类,所以可以通过任意的指针类型(这里介绍使用的是动态分配的指针,但可以不是动态分配创建的,后面会介绍)来初始化。 shared_ptr对象可以进行拷贝,赋值;当一个对象A拷贝B或者B赋值给A,其根本都是将B的资源共享给A,然后将该资源的引用次数增加;当引用该资源的最后一个shared_ptr对象销毁的时候,会自动把该资源释放掉。下面看shared_ptr的相关操作: |
函数 | 介绍 |
---|
shared_ptr<T>sp(T* res) | 将资源res交给sp来管理,资源不使用时会自动释放。 |
shared_ptr<T>sp = make_spared(params) | 用params参数构造T类型的对象并且交给sp来管理。 |
p | p可以作为条件判断,若p指向一个对象,则为true,否则为false。 |
*p | 对shared_ptr<T>对象解引用,得到的是由p管理的T类型对象的引用。 |
p->mem | 等价于(*p).mem |
p.get() | 返回p中保存的T*指针 |
swap(p,q) | 交换p,q各自内部的T*指针,注意p,q类型要相同 |
p.swap(q) | 同上 |
p = q | p,q必须都是shared_ptr指针,并且各自管理的指针类型能相互转换。此操作会递减p的引用次数,递增q的引用次数;若p的引用次数变为0,则其管理的原内存会自动释放。 |
shared_ptr<T>sp(q) | 使得sp引用q所占资源。 |
p.reset() , p.reset(q) | 放弃p对资源的引用,若为唯一引用,还会释放内存。若传有参数q,则让p引用q所占资源。 |
p.use_count() | 返回与p共享的智能指针数量;可能很慢,主要用于调试。 |
p.unique() | 若p.use_count()为1,返回true,否则返回false |
我们用下面这段简单的代码来理解shared_ptr智能指针的使用: |
int main()
{
{
shared_ptr<string> p(new string("hello world"));
{
shared_ptr<string> q(p);
cout << p.use_count() << endl;
cout << *q << endl;
*q = *q + "!";
}
cout << p.use_count() << endl;
cout << *p << endl; >> "hello world!"
}
return 0;
}
事实上,在不是那种临界资源的情况下,我们管理内存资源大多是使用shared_ptr智能指针。而引用计数实现的有使用者保留,无使用者才释放内存似乎完美地解决了内存释放问题。然而shared_ptr也有缺陷,不过我们留到后面来讲。 |
九.unique_ptr智能指针
unique_ptr指针适用的就是前一节所说的第2类资源指针。 unique_ptr是模板类,所以可以通过任意的指针类型(这里介绍使用的是动态分配的指针,但可以不是动态分配创建的,后面会介绍)来初始化。 unique_ptr对象不可以进行拷贝,赋值;只能通过相关函数让unique_ptr让出的支配权然后其他unique_ptr接收。这样就保证了支配权的唯一性。下面看unique_ptr的相关操作: |
函数 | 介绍 |
---|
unique_ptr<T>up(T* res) | 将资源res交给up来管理,资源不使用时会自动释放。 |
p | p可以作为条件判断,若p指向一个对象,则为true,否则为false。 |
*p | 对shared_ptr<T>对象解引用,得到的是由p管理的T类型对象的引用。 |
p->mem | 等价于(*p).mem。 |
p.get() | 返回p中保存的T*指针。 |
swap(p,q) | 交换p,q各自内部的T*指针,注意p,q类型要相同。 |
p.swap(q) | 同上。 |
p = q | 这是无效操作,unique_ptr不能进行赋值。 |
unique_ptr<T>sp(q) | 这是无效操作,unique_ptr不能进行拷贝。 |
p = nullptr | 释放p指向的对象,并将p置为控。 |
p.release() | p放弃对指针的控制权,返回资源指针,并将p置为空。 |
p.reset(T* q) | 释放p指向的对象,并将p置为空,若传入参数q,则让p指向q该指针。 |
p.reset(q.release()) | 释放掉p指向的对象,q将自身指向对象的控制权转移给p,然后q自身置为空。 |
我们用下面这段简单的代码来理解unique_ptr智能指针的使用: |
int main()
{
{
unique_ptr<string> q;
{
unique_ptr<string> p(new string("hello world"));
cout << *p << endl;
q.reset(p.release());
}
cout << *q << endl;
}
return 0;
}
特别的,unique_ptr智能指针还可以用于管理动态数组: |
int main()
{
unique_ptr<int[]> up(new int[10]);
for(int i = 0 ; i < 10 ; i ++) up[i] = i;
for(int i = 0 ; i < 10 ; i ++)
printf("%d",up[i]);
return 0;
}
一般来说,我们不常用unique_ptr智能指针来管理动态内存。而是管理类似于数据库连接对象等资源。 有时我们的资源内存不一定是通过动态分配,而且释放有时候也不仅仅是delete 指针而已。所以,我们以后在使用unique_ptr指针的时候还需要传入删除器。这个,我们后面会详细讲到。 |
十.weak_ptr智能指针
weak_ptr 是一种不控制所指向对象生命期的智能指针,它指向一个由 shared_ptr 管理的对象。将一个 weak_ptr 绑定到一个 shared_ptr 不会改变 shared_ptr 的引用次数。一旦最后一个指向对象的shared_ptr 被销毁,对象就会被释放;即使这个时候存在 weak_ptr 指向了该对象,对象还是会被释放。所以我们可以把 weak_ptr 看作是一种”弱引用”。 |
函数 | 介绍 |
---|
weak_ptr<T>wp | 空 weak_ptr 可以指向类型为T的对象 |
weak_ptr<T>wp(sp) | 与shared_ptr sp指向相同的对象,T必须可以转化为sp指向对象的类型。 |
w = p | p 可以是weak_ptr或者shared_ptr。赋值后w,p共享对象。但是w不干涉对象声明周期。 |
w.reset() | m置为空。 |
w.use_count() | 与m共享资源的shared_ptr个数。 |
w.expired() | 若w.use_count()为0,返回true,否则返回false。 |
w.lock() | 如果expired为true,返回一个空的shared_ptr;否则返回指向w对象的shared_ptr。 |
正是因为weak_ptr是一种”弱引用”,所以,很有可能在使用过程中某个weak_ptr被支配的shared_ptr给销毁了;所以我们在使用weak_ptr之前需要调用w.lock()来判断weak_ptr所指向的内存是否被释放了。 |
基本上,智能指针的基本操作就讲到这里,下一节会介绍智能指针的删除器以及相关注意事项。 |