头文件 #include < memory >
智能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象。
两种智能指针:
- shared_ptr 允许多个指针指向同一个对象。
- unique_ptr 独占所指向的对象。
weak_ptr:伴随类,它是一种弱引用,指向shared_ptr所管理的对象。
1. shared_ptr
shared_ptr<T> sp //空智能指针,可以指向类型为T的对象
unique_ptr<T> up
shared_ptr独有的操作
操作 | 含义 |
---|---|
make_shared(args) | 返回一个shared_ptr,指向一个动态分配的类型为T的对象。使用args初始化此对象 |
shared_ptrp(q) | p是shared_ptr q的拷贝;此操作会递增q中的计数器。q中的指针必须能转换为T* |
p = q | p和q都是shared_ptr,所保存的指针必须能相互转换。此操作会递减p的引用计数,递增q的引用计数 |
1.1 make_shared函数
最安全的分配和使用动态内存的方法是调用一个名为make_shared的标准库函数。
此函数在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr。
//指向一个值为42的int的shared_ptr
shared_ptr<int> p3 = make_shared<int>(42);
1.2 shared_ptr的拷贝和赋值
可以认为每个shared_ptr都有一个关联的计数器,通常称其为引用计数。
无论何时我们拷贝一个shared_ptr,计数器都会递增。
当我们给shared_ptr赋予一个新值或是shared_ptr被销毁(例如一个局部的shared_ptr离开其作用域)时,计数器就会递减。
一旦一个shared_ptr的计数器变为 0,它就会自动释放自己所管理的对象。
1.3 shared_ptr自动销毁所管理的对象
当指向一个对象的最后一个shared_ptr被销毁时,shared_ptr类会自动销毁此对象。
它是通过另一个特殊的成员函数–析构函数完成销毁工作的。
shared_ptr的析构函数会递减它所指向的对象的引用计数。如果引用计数变为 0,shared_ptr的析构函数就会销毁对象,并释放它所占用的内存。
1.5 使用了动态生存期的资源的类
程序使用动态内存处于以下三种原因之一:
1. 程序不知道自己需要使用多少对象
2. 程序不知道所需对象的准确类型
3. 程序需要在多个对象间共享数据
1.6 shared_ptr和new结合使用
不能讲一个内置指针隐式转换为一个智能指针,必须使用直接初始化形式。
shared_ptr<int> p1 = new int(1024); //错误,必须使用直接初始化形式
shared_ptr<int> p2(new int(1024)); //正确,使用了直接初始化形式
不要混合使用智能指针和普通指针
当将一个shared_ptr绑定到一个普通指针时,我们就将内存的管理责任交给了这个shared_ptr。一旦这样做了,我们就不应该再使用内置指针来访问shared_ptr所指向的内存了。
2. 智能指针和异常
一个简单地确保资源被释放的方法是使用智能指针。
如果使用智能指针,即使程序块过早结束,智能指针类也能确保在内存不再需要时将其释放:
void f()
{
shared_ptr<int> sp(new int(42)); //分配一个新对象
//这段代码抛出一个异常,且在f中未被捕获
} //在函数结束时shared_ptr自动释放内存
如果使用内置指针管理内存,且在new之后在对应的delete之前发生了异常,则内存不会被释放:
void f()
{
int *ip = new int(42); //动态分配一个新对象
//这段代码抛出一个异常,且在f中未被捕获
delete ip; //在退出之前释放内存
}
3. unique_ptr
与shared_ptr不同,某个时刻只能有一个unique_ptr指向一个给定对象。
当我们定义一个unique_ptr时,需要将其绑定到一个new返回的指针上。类似shared_ptr,初始化unique_ptr必须采用直接初始化形式。
unique_ptr<double> p1; //可以指向double的unique_ptr
unique_ptr<int> p2(new int(42)); //p2指向一个值为42的int
unique_ptr不支持拷贝与赋值
unique_ptr<string> p1(new string("Stegosaurus"));
unique_ptr<string> p2(p1); //错误,unique_ptr不支持拷贝
unique_ptr<string> p3;
p3 = p2; //错误,unique_ptr不支持赋值
操作 | 含义 |
---|---|
u.release() | u放弃对只指针的控制权,返回指针,并将u置为空 |
u.reset() | 释放u指向的对象 |
u.reset(q) | 如果提供了内置指针q,令u指向这个对象;否则将u置为空 |
不能拷贝或赋值unique_ptr,但可以通过调用release或reset将指针的所有权从一个unique_ptr转移给另一个unique_ptr:
//将所有权从p1转移给p2
unique_ptr<string> p2(p1.release()); //release将p1置为空
unique_ptr<string> p3(new string("Trex"));
//将所有权从p3转移给p2
p2.reset(p3.release()); //reset释放了p2原来指向的内存
release成员返回unique_ptr当前保存的指针并将其置为空。
reset成员接受一个可选的指针参数,令unique_ptr重新指向给定的指针。
3.1 传递unique_ptr参数和返回unique_ptr
不能拷贝unique_ptr的规则有一个例外:我们可以拷贝或赋值一个将要被销毁的unique_ptr。
例,从函数返回一个unique_ptr:
unique_ptr<int> clone(int p)
{
//正确
return unique_ptr<int>(new int(p));
}
返回一个局部对象的拷贝:
unique_ptr<int> clone(int p)
{
unique_ptr<int> ret(new int(p));
//...
return ret;
}
3. weak_ptr
weak_ptr是一种不控制所指向对象生存期的智能指针,它指向由一个shared_ptr管理的对象。
将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。
一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。即使有weak_ptr指向对象,对象还是会被释放。
操作 | 含义 |
---|---|
weak_ptr w | 空weak_ptr可以指向类型为T的对象 |
weak_ptr w(sp) | 与shared_ptr sp 指向相同对象的weak_ptr。T必须能转换为sp指向的对象 |
w = p | p可以是一个shared_ptr或一个weak_ptr |
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 |
由于对象可能不存在,所以不能使用weak_ptr直接访问对象,而必须调用lock。
此函数检查weak_ptr指向的对象是否仍存在。如果存在,lock返回一个指向共享对象的shared_ptr。
if (shared_ptr<int> np = wp.lock())
{
//如果np不为空,则条件成立
//在if中,np与p共享对象
}