C++中主要用New和Delete来分配和释放内存,但是有时候我们常常忘记Delete而造成内存泄露。为了避免这个问题,C++引入了智能指针模板类来管理动态内存。C++中智能指针有四种:auto_ptr,unique_ptr、share_ptr、weak_ptr。
1、auto_ptr(C++98的方案,C++11已放弃)
- auto_ptr要求对它拥有的指针完全占用,两个auto_ptr不能同时拥有同一个普通指针
- 例如:
auto_ptr<string> p1(new string("hello"));
auto_ptr<sting> p2;
p2 = p1; //不会报错,但是p2会剥夺p1的拥有权
std::cout << *p1 << std::endl; //报错
- 由于auto_ptr复制时会剥夺原来指针的拥有权,导致原来的指针失效,再操作原来的指针时会出错,所以C++11中已经不再使用auto_ptr。
2、unique_ptr(替换auto_ptr)
- unique_ptr同样是独占式占有指针,为了保证安全,它不允许普通的拷贝和复制,保证了同一时间只有一个智能指针指向该对象;所以当unique_ptr被销毁时,他指向的对象也会被销毁。
- 当我们定义一个unique_ptr时,需要使用new来为其直接初始化。
3、share_ptr
- share_ptr是共享式智能指针,允许多个智能指针指向同一个对象;
- 默认创建的share_ptr是一个空指针,可以用new和make_ptr初始化,但是不能直接初始化;例如:
share_ptr<int> p1(new int(3)); //正确
share_ptr<int> p2 = make_ptr<int>(4);//正确
share_ptr<int> p3 = new int(5); //错误
- 每个share_ptr都有一个关联的引用计数器。当我们拷贝一个share_ptr时引用计数会递增;当我们给它赋新值或销毁它时,引用计数会递减。
- 当引用计数减为0时,share_ptr指向的对象会自动销毁,share_ptr指针变为空。
4、weak_ptr
- weak_ptr是一种弱指针,指向share_ptr指向的对象,不能直接访问对象。
- weak_ptr只能通过一个share_ptr或另一个weak_ptr对象来构造,它的构造和析构不会引起引用计数的变化。
- share_ptr可以直接赋值给weak_ptr,weak_ptr可以通过调用lock()函数来获得share_ptr。
- weak_ptr的引入主要是为了协助share_ptr工作,主要是解决share_ptr相互引用时的死锁问题。
– 如果说两个share_ptr相互引用,那么这两个指针的引用计数永远都不可能下降为0,资源也就永远不会释放,造成死锁。而将一个share_ptr换成weak_ptr就可以解决死锁问题。例:
class A
{
share_ptr<B> pb_;
};
class B
{
share_ptr<A> pa_;
};
void fun()
{
share_ptr<A> pa(new A());
share_ptr<B> pb(new B());
pa->pb_ = pb;
pb->pa_ = pa;
cout << pa.use_count() << endl;
cout << pb.use_count() << endl;
}
int main()
{
fun();
return 0;
}
可以看到fun中pa,pb相互引用,引用计数都是2。跳出fun函数时,pa,pb引用计数减1最终都为1,资源仍然都未被释放(析构函数未被调用)造成死锁。
- 如果把其中一个类中的share_ptr改成weak_ptr就能解决这个问题。
我们把类A里的share_ptr pb_改为weak_ptr pb_,这样pb一开始的引用只有1,当fun函数结束时,pa引用计数为1,pb引用计数为0,B资源得到释放,同时又使pa的引用计数减为0,A资源得到释放。