1.shared_ptr
1.1 shared_ptr介绍
shared_ptr主要用于托管动态分配的内存。
在程序中动态分配了一块内存,这块内存可以是变量可以是对象,为了避免内存泄露,我们必须在整个程序的所有可能跑到的分支,保证这块内存不用了之后可以得到正确的释放。
普通指针使用起来麻烦,而且使用不当还很有可能出现程序崩溃,比如指针未释放导致内存泄漏、踩空指针或者指针重复释放。
智能指针可以有效地避免这个问题。
1.2 shared_ptr底层实现
shared_ptr指向同一个对象的时候,会共享一个共享计数,如上面的示例程序,计数一开始到了3,当sp1调用reset()后,计数-1,直到三个shared_ptr都被reset了,计数归零,对象销毁。
1.3 shared_ptr使用方法
- 初始化
std::shared_ptr<int> p1; //不传入任何实参
std::shared_ptr<int> p2(nullptr); //传入空指针 nullptr
std::shared_ptr
简单解释一下:这里new了一个Z类型地对象(我们先称之为Z(1)
),并且用智能指针sp1来托管这个对象。
我们要调用Z(1)这个对象,只需要和普通指针一样用sp1->
即可,使用完也不用释放,程序销毁地时候会自动调用Z的析构函数。
可以使用如下的方法将对象Z(1)托管给多个智能指针:
// 拷贝构造函数,sp1 sp2 sp3共享同一个指针和指针计数
std::shared_ptr<Z> sp2 = sp1;
std::shared_ptr<Z> sp3(sp1);
// 移动构造函数,sp1指向的对象被移动给了sp4,sp1变为空智能指针,计数清零
std::shared_ptr<Z> sp4(std::move(sp1));
std::shared_ptr<Z> sp5 = std::make_shared<Z>();
- get()
可以用get()
函数获取Z(1)这个对象的地址(普通指针):
Z* p = sp1.get(); - reset()
可以用reset()
重置sp1托管的对象,如果此前sp1指向的对象没有其他智能指针托管,那么该对象会直接被释放:
sp1.reset(new Z(2)); - use_count()
用于获取当前智能指针指向的对象的所有智能指针个数。 - unique()
判断当前是否还有其他的智能指针指向该智能指针指向的对象。
1.4 使用案例
- 示例程序
#include <iostream>
#include <memory>
class Z
{
public:
int i;
Z(int z) :i(z) {}
~Z() { std::cout << i << " destructed\n"; }
};
int main()
{
std::shared_ptr<Z> sp1(new Z(1));
std::shared_ptr<Z> sp2(sp1);
std::shared_ptr<Z> sp3 = sp1;
std::cout << "sp1->i=" << sp1->i << ",sp2->i=" << sp2->i << ",sp3->i=" << sp3->i << "\n";
Z* p = sp1.get();
std::cout << "p->i=" << p->i << "\n";
sp1.reset(new Z(2));
std::cout << "sp1->i=" << sp1->i << "\n";
sp2.reset(new Z(3));
std::cout << "sp2->i=" << sp2->i << "\n";
sp3.reset(new Z(4));
std::cout << "sp3->i=" << sp3->i << "\n";
return 0;
}
- 输出结果
sp1->i=1,sp2->i=1,sp3->i=1
p->i=1
sp1->i=2
sp2->i=3
1 destructed
sp3->i=4
4 destructed
3 destructed
2 destructed
- 说明
在sp1、sp2和sp3都被reset给了其他的对象之后,原来的Z(1)就被释放掉了。
剩余的对象则是在退出程序的时候释放。
1.5 注意事项
- 不能使用如下的方式初始化,sp1和sp2初始化的时候共享计数并不会变成2,而是都记成1,这样sp1销毁之后指针p已经销毁了,sp2销毁的时候会导致指针重复释放,导致程序崩溃。
Z* p = new Z(1);
std::shared_ptr<Z> sp1(p), sp2(p);
- 可以自己定义堆内存的释放函数,这样对象在析构的时候就会优先调用我们定义的析构函数。
std::shared_ptr<Z> sp1(new Z(1), std::default_delete<Z>()); // 使用默认的delete函数
void deleteZ(Z* p) {
delete p;
}
std::shared_ptr<Z> sp2(new Z(2), deleteZ);
2. unique_ptr
2.1 unique_ptr介绍
unique_ptr的功能基本和shared_ptr相同,也是用于管理堆内存的智能指针,但是不允许多个unique_ptr指向同一个对象。
2.2 unique_ptr使用方法
- 初始化
std::unique_ptr<Z> up1;
std::unique_ptr<Z> up2(nullptr);
std::unique_ptr<Z> up1(new Z(1));
std::unique_ptr<Z> up2(up1); // 错误,不能使用两个unique_ptr指向同一个对象
std::unique_ptr<Z> up3(std::move(up1)); // 正确,可以是用移动构造函数,up1被释放为空指针
- get()
可以用get()
函数获取Z(1)这个对象的地址(普通指针):
Z* p = up1.get(); - reset()
可以用reset()
重置sp1托管的对象,如果此前sp1指向的对象没有其他智能指针托管,那么该对象会直接被释放:
up1.reset(new Z(2)); - release()
释放当前的智能指针,但是不释放该智能指针指向的堆内存。 - get_deleter()
获取当前智能指针的销毁函数。