智能指针auto_ptr、unique_ptr、scoped_ptr、shared_ptr、weak_ptr
我们在写代码的时候经常会发生内存泄露情况,如忘记释放堆上指针、在释放前抛出了异常、多线程情况下的问题,这些都是我们不愿意发生的,所以C++有一些用于管理内存的智能指针供我们使用,可以有效防止内存泄露的情况发生。
智能指针总体概述:
- auto_ptr ——不推荐使用
- shared_ptr ——引用计数智能指针,共享所有权
- weak_ptr ——shared_ptr的观察者,只进行引用而不改变引用计数,用来解决shared_ptr的循环引用问题
- unique_ptr ——管理单个堆内存对象,独享所有权,不允许赋值和拷贝,不能用于管理数组对象
本质:
将指针封装为类对象成员,并在析构函数里删除指针指向的内存。
不同:
- auto_ptr、unique_ptr、scoped_ptr马上删除。
- shared_ptr计数为0删除。
- weak_ptr不删除。
1. auto_ptr
C++98标准下的智能指针
1.1 作用
对作用域内的动态分配对象的自动释放
- 基本类型
作为对比:
1.2 缺陷
1.两个auto_ptr
不能同时拥有同一个对象:
2.auto_ptr
不能管理数组指针
#include <memory>
using namespace std;
int main(){
int*pa=new int[10];
auto_ptr<int>ap(pa);
}
3.auto_ptr
被拷贝或被赋值后,失去对原指针的管理.这种情况被称为指针所有权传递。
拷贝的情况
4.auto_ptr
不能作为容器对象,因为STL容器中的元素经常要支持拷贝,赋值等操作。
2. unique_ptr
- 作用
C++11下用来代替auto_ptr
,不能复制与赋值。
3. scoped_ptr
- 作用
与unique_ptr
相似,在本作用域中使用,不能复制与赋值。
4. shared_ptr
C++11新添加的智能指针
- 多个
shared_ptr
可以共享同一个对象,对象的最末一个拥有者有责任销毁对象,并清理与该对象相关的所有资源。 - 一般而言,
shared_ptr
并非线程安全
#include <iostream>
#include <memory>
#include <string>
using namespace std;
void fun(){
shared_ptr<string> pa(new string("CHN"));
shared_ptr<string> pb(new string("USA"));
cout << "*pa " << *pa << endl;//CHN
cout << "pa.use_count " << pa.use_count() << endl;//1
cout << "*pb " << *pb << endl;//USA
cout << "pb.use_count " << pb.use_count() << endl;//1
pa = pb;
cout << *pa << endl;//USA
cout << "pa.use_count " << pa.use_count() << endl;//2:pa和pb指向同一个资源USA了,该资源的计数为2,所以pb、pb都输出2
cout << "pb.use_count " << pb.use_count() << endl;//2
pa.reset();
pb.reset();
cout << "pa.use_count " << pa.use_count() << endl;//0
cout << "pb.use_count " << pb.use_count() << endl;//0
}
void main()
{
fun();
system("pause");
}
5. weak_ptr
weak_ptr
是一种用于解决shared_ptr
相互引用时产生死锁问题的智能指针。如果有两个shared_ptr
相互引用,那么这两个shared_ptr
指针的引用计数永远不会下降为0,资源永远不会释放。weak_ptr
是对对象的一种弱引用,它不会增加对象的use_count
,weak_ptr
和shared_ptr
可以相互转化,shared_ptr
可以直接赋值给weak_ptr
,weak_ptr
也可以通过调用lock
函数来获得shared_ptr
。
看两个share_ptr
互相引用导致释放内存失败的例子:
#include <iostream>
#include <memory>
#include <string>
using namespace std;
class B;
class A {
public:
shared_ptr<B> pb_;
~A() {
cout << "A delete\n";
}
};
class B {
public:
shared_ptr<A> pa_;
~B() {
cout << "B delete\n";
}
};
void fun() {
shared_ptr<B> pb(new B());
cout << "pb.use_count " << pb.use_count() << endl;//1
shared_ptr<A> pa(new A());
cout << "pa.use_count " << pa.use_count() << endl;//1
pb->pa_ = pa;
cout << "pb.use_count " << pb.use_count() << endl;//1
cout << "pa.use_count " << pa.use_count() << endl;//2
pa->pb_ = pb;
cout << "pb.use_count " << pb.use_count() << endl;//2:由于share_ptr是共享资源,所以pb所指向的资源的引用计数也会加1
cout << "pa.use_count " << pa.use_count() << endl;//2
}//程序结束时,没有调用A和B的析构函数
int main() {
fun();
return 0;
}
而使用weak_ptr
:把A中的shared_ptr<B> pb
_改为weak_ptr<B> pb_weak
,这样改为了弱引用,传递时不会增加pb引用计数use_count()
的值,所以最终能够使A、B资源正常释放:
#include <iostream>
#include <memory>
#include <string>
using namespace std;
class B;
class A {
public:
weak_ptr<B> pb_weak;
~A() {
cout << "A delete\n";
}
};
class B {
public:
shared_ptr<A> pa_;
~B() {
cout << "B delete\n";
}
void print() {
cout << "This is B" << endl;
}
};
void fun() {
shared_ptr<B> pb(new B());
cout << "pb.use_count " << pb.use_count() << endl;//1
shared_ptr<A> pa(new A());
cout << "pa.use_count " << pa.use_count() << endl;//1
pb->pa_ = pa;
cout << "pb.use_count " << pb.use_count() << endl;//1
cout << "pa.use_count " << pa.use_count() << endl;//2
pa->pb_weak = pb;
cout << "pb.use_count " << pb.use_count() << endl;//1:弱引用不会增加所指资源的引用计数use_count()的值
cout << "pa.use_count " << pa.use_count() << endl;//2
shared_ptr<B> p = pa->pb_weak.lock();
p->print();//不能通过weak_ptr直接访问对象的方法,须先转化为shared_ptr
cout << "pb.use_count " << pb.use_count() << endl;//2
cout << "pa.use_count " << pa.use_count() << endl;//2
}//函数结束时,正确的情况下,应该调用A和B的析构函数
/*资源B的引用计数一直就只有1,当pb析构时,B的计数减一,变为0,B得到释放,
B释放的同时也会使A的计数减一,同时pa自己析构时也会使资源A的计数减一,那么A的计数为0,A得到释放。
*/
int main() {
fun();
return 0;
}
weak_ptr
接受shared_ptr
类型的变量赋值,但是反过来是行不通的,需要使用lock函数。weak_ptr
设计之初就是为了服务于shared_ptr
的,所以不增加引用计数就是它的核心功能。
weak_ptr
好处:
- 打破递归的依赖关系
- 使用一个共享的资源但是不要所有权,不添加引用计数
- 避免悬空指针。