智能指针的作用是管理一个指针,因为存在以下这种情况:申请的空间在函数结束时忘记释放,造成内存泄漏。使用智能指针可以很大程度上的避免这个问题,因为智能指针就是一个类,当超出了类的作用域是,类会自动调用析构函数,析构函数会自动释放资源。所以智能指针的作用原理就是在函数结束时自动释放内存空间,不需要手动释放内存空间。
C++里面的四个智能指针: auto_ptr, shared_ptr, weak_ptr, unique_ptr 。其中auto_ptr已经被c++11弃用。
1.auto_ptr
采用所有权模式。在拷贝和赋值的过程中会剥夺指针对原对象对内存的控制权。转交给对象后,再将原对象指针置为nullptr。
auto_ptr< string> p1 (new string ("I reigned lonely as a cloud.”));
auto_ptr<string> p2;
p2 = p1; //auto_ptr不会报错.
此时不会报错,p2剥夺了p1的所有权,但是当程序运行时访问p1将会报错。所以auto_ptr的缺点是:存在潜在的内存崩溃问题!
2. unique_ptr
unique是专属所有权。由unique_ptr管理的内存,只能被一个对象持有,不支持复制和赋值。
采用所有权模式,还是上面那个例子
禁止了拷贝语义,当需要转移所有权的时候通过,std::move()方法可以实现控制所有权的转移。
//第一种方式
unique_ptr <int> p1(new int(10));
cout<<"p1="<<*p1<<endl;
unique_ptr <int> p2;
/*
p2=p1;//报错
*/
p2=move(p1);
cout<<"p2="<<*p2<<endl;
//第二种方式
auto w=std::make_unique<int>(10);//使用make_unique方法
cout<<*(w.get())<<endl;//使用get()方法获得w所指向的值
/*
auto w2 = w;//错误,不能将w复制给w2。
*/
auto w2=std::move(w);//使用移动语义,将w的所有权转让给w2,
//w此时为nullptr;
cout<<"w="<<((w.get()!=nullptr)?(*w.get()):-1)<<endl;//-1
cout<<"w2="<<((w2.get()!=nullptr)?(*w2.get()):-1)<<endl;//10
w=std::make_unique<int>(20);//重新使用w指针,对其赋值
cout<<*(w.get())<<endl;
3.shared_ptr
shared_ptr实现共享式拥有概念。多个智能指针可以指向相同对象,该对象和其相关资源会在“最后一个引用被销毁”时候释放。在引用计数的机制上提供了可以共享的智能指针。当我们调用release()时,当前指针会释放资源所有权,计数减一。当计数等于0时,该对象没有被引用,可以进行析构,资源会被释放。
//shared_ptr
{
//定义一个shared_ptr指针,代表共享所有权.
//多个shared_ptr可以共享同一内存
auto w1=shared_ptr <int>(new int(20));
{
auto w2=w1;//w2,w1指向同一个对象
cout<<((w1.get()!=nullptr)?(*w1.get()):-1)<<endl;//20
cout<<((w2.get()!=nullptr)?(*w2.get()):-1)<<endl;//20
cout<<w1.use_count()<<endl;//use_count 返回引用计数的个数,2
cout<<w2.use_count()<<endl;//2
}
}
//move用法;
auto w3=shared_ptr <int>(new int(30));//
auto w4=move(w3);//将w3的所有权转交给w4,此时w3为nullptr
cout<<((w3.get()!=nullptr)?(*w3.get()):-1)<<endl;//-1
cout<<((w4.get()!=nullptr)?(*w4.get()):-1)<<endl;//30
//w已经不再持有对象,引用值为0;而w4有引用对象,引用值为1
cout<<w3.use_count()<<endl;//0
cout<<w4.use_count()<<endl;//1
return 0;
class b;//声明b类
class a
{
public:
shared_ptr<b> pb;
~a() {cout<<"~a()"<<endl;}
};
class b
{
public:
shared_ptr<a> pa;
~b() {cout<<"~b()"<<endl;}
};
//pa和pb存在着循环引用,pa和pb都无法正常释放
void fun1()
{
cout<<"第一次:"<<endl;
shared_ptr<a>ta(new a());
shared_ptr<b>tb(new b());
cout<<ta.use_count()<<endl;//1
cout<<tb.use_count()<<endl;//1
ta->pb=tb;
tb->pa=ta;
cout<<ta.use_count()<<endl;//2
cout<<tb.use_count()<<endl;//2
}
问题:可能会出现循环引用计数会带来循环引用的问题,导致内存无法正常回收,内存泄露。
4.weak_ptr
为了解决shared_ptr存在的循环引用问题,引入weak_ptr
weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的 shared_ptr. weak_ptr只是提供了对管理对象的一个访问手段。weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。weak_ptr是用来解决shared_ptr相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。
weak_ptr协助shared_ptr工作(类似于观察者模式),weak_ptr只对shared_ptr进行引用,不会改变其引用个数。当share_ptr失效后,相应的weak_ptr也会失效。
class bb;//声明b类
class aa
{
public:
shared_ptr<bb> pb1;
~aa() {cout<<"~aa()"<<endl;}
};
class bb
{
public:
weak_ptr<aa> pa1;//注意此处为weak_ptr
~bb() {cout<<"~bb()"<<endl;}
};
void fun2()
{
cout<<"第二次:"<<endl;
shared_ptr<aa>ta1(new aa());
shared_ptr<bb>tb1(new bb());
cout<<ta1.use_count()<<endl; //1
cout<<tb1.use_count()<<endl; //1
ta1->pb1=tb1;
tb1->pa1=ta1;
cout<<ta1.use_count()<<endl;//1
cout<<tb1.use_count()<<endl;//2
}