相对于new,智能指针可以自动释放申请的堆空间;C++11提供了两种智能指针类型来管理动态对象。一种是shared_ptr,它允许多个指针指向同一对象;另一种是unique_ptr,它则独占所指向的对象。
1.shared_ptr类
创建一个可以指向T型对象的智能指针: shared_ptr < T > p;
默认初始化的智能指针中保存着一个空指针。
1)shared_ptr的独有操作
shared_ptr 操作 | 解释 |
---|---|
make_shared(args) | 返回一个shared_ptr,指向一个动态分配的类型为T的对象,使用args初始化此对象。 |
shared_ptr p(q) | p是shared_ptr的拷贝,此操作会递增q中的计数器。q中的指针必须能转换为T* |
p=q | 此操作会递减p的引用计数,递增q的引用计数;若p的引用计数变为0,则将释放其管理的内存 |
p.use_count() | 返回与p共享对象的智能指针数量,主要用于调试 |
p.unique() | 若p.use_count()为1,返回true,否则返回false |
最安全的分配和使用动态内存的方法是调用make_shared的库函数,此函数在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr。此函数定义在< momery>中。
//不传递参数,对象默认初始化
shared_ptr<int> p = make_shared<int>();
auto p = make_shared<int>(43);
2)shared_ptr的拷贝与赋值
当进行拷贝和赋值操作时,每个shared_ptr都会记录有多少个其他shared_ptr指向相同的对象:
auto p = make_shared<int>(42);//p指向的对象只有p一个引用者
auto q(p);//p和q指向相同的对象,此对象有两个引用者
可以认为每个shared_ptr都有一个关联的计数器,通常称其为引用计数。一旦这个计数器为0,它就会释放自己所管理的对象及所占的内存。
auto r = make_shared<int>(42);//r指向的对象只有一个引用者
r=q ; //给r赋值,令它指向另一个地址
//递增q指向的对象的引用计数
//递减r原来指向的对象的引用计数
//r原来指向的对象已没有引用者,会自动释放
拷贝使得对象的引用计数增加1,赋值使得原对象引用计数减1,当计数为0时,自动释放内存。后来指向的对象引用计数加1,指向后来的对象。
3)使用动态内存的三个原因之一:
- 不知道自己需要使用多少对象
- 不知道所需对象的准确类型
- 需要在多个对象间的共享数据
2.shared_ptr和new结合使用
一般我们可以使用new返回的指针来初始化智能指针
shared_ptr<int> p2 (new int(1024));//正确,必须使用直接初始化形式
shared_ptr<int> p2 =new int(1024);//错误,不是直接初始化形式
#include <iostream>
#include <memory>
int main()
{
int a = 10;
std::shared_ptr<int> ptra = std::make_shared<int>(a);
std::shared_ptr<int> ptra2(ptra); //copy
std::cout << ptra.use_count() << std::endl;
int b = 20;
int *pb = &a;
//std::shared_ptr<int> ptrb = pb; //error
std::shared_ptr<int> ptrb = std::make_shared<int>(b);
ptra2 = ptrb; //assign
pb = ptrb.get(); //获取原始指针
std::cout << ptra.use_count() << std::endl;
std::cout << ptrb.use_count() << std::endl;
system("pause");
return 0;
}
注意:
- 不要混合使用普通指针和智能指针
- 也不要使用get函数初始化另一个智能指针或为智能指针赋值
- 注意避免循环引用,shared_ptr的一个最大的陷阱是循环引用,循环引用会导致堆内存无法正确释放,导致内存泄漏。
3.unique_ptr类
一个unique_ptr拥有它所指向的对象。与shared_ptr不同,某个时刻只能有一个unique_ptr指向一个给定对象。当unique_ptr被销毁时,它所指向的对象也被销毁。
unique_ptr不能赋值和拷贝
unique_ptr 操作 | 解释 |
---|---|
u.release() | u放弃对指针的控制权,返回指针,并将u置空 |
u.reset() | 释放u指向的对象 |
u.reset(q) | 令u指向的p所指的对象 |
u=nullptr | 释放u指向的对象,将u置为空 |
#include <iostream>
#include <memory>
int main()
{
std::unique_ptr<int> uptr(new int(10)); //绑定动态对象
//std::unique_ptr<int> uptr2 = uptr; //不能赋值
//std::unique_ptr<int> uptr2(uptr); //不能拷贝
uptr2.release(); //释放所有权
system("pause");
return 0;
}
参考: