C++智能指针
智能指针是c++标准程序库中所提供的一种更为安全的使用堆内存的一种方式
前言
我们都知道,C++在使用中,new、delete操作堆内存。但是,对于堆内存,需要由程序员自行进行管理。这就意味着,new出来的堆内存,需要通过程序员手动使用delete进行释放。在实际项目中,程序员往往会犯两种错误,一种是忘记delete,释放内存,导致内存泄漏;还要一种,就是delete两次,会产生引用非法内存的指针。
智能指针的出现,是为了方便储层许愿进行堆内存的管理,防止以上两种错误。这对于编程人员来说,是一种非常方便的使用方式。智能指针是c++11引入的一种行为类似指针,但是可以自动释放指向对象的一种工具。
一、智能指针的原理
1 RALL
RAII 是 resource acquisition is initialization 的缩写(“资源获取即初始化”)。 这种机制的核心思想是,将资源的使用和对象的生命周期进行绑定。对象在初始化时,进行资源分配,对象在销毁时,进行资源释放。
2 原理
智能指针利用RALL技术(资源获取即初始化)堆普通指针进行封装。利用这种封装和操作符重载会令智能指针的使用和指针类似,但实际是一个对象。
二、分类
我们经常使用的智能指针,基本分成三个类型,shared_ptr、unique_ptr、weak_ptr。 下面将会分开介绍这三种不同的智能指针
1.shared_ptr
shared_ptr 实现多个智能指针指向相同对象,该对象和其相关资源会在“最后一个引用被销毁”时候释放。它使用引用计数法进行计数,当对象增加时,引用计数+1, 当对象减少时,引用计数-1。
代码如下(示例):
int main()
{
string *s1 = new string("s1");
shared_ptr<string> ps1(s1);
shared_ptr<string> ps2;
ps2 = ps1;
cout << ps1.use_count()<<endl;
cout<<ps2.use_count()<<endl;
cout << ps1.unique()<<endl;
string *s3 = new string("s3");
shared_ptr<string> ps3(s3);
cout << (ps1.get()) << endl;
cout << ps3.get() << endl;
swap(ps1, ps3);
cout << (ps1.get())<<endl;
cout << ps3.get() << endl;
cout << ps1.use_count()<<endl;
cout << ps2.use_count() << endl;
ps2 = ps1;
cout << ps1.use_count()<<endl;
cout << ps2.use_count() << endl;
ps1.reset();
cout << ps1.use_count()<<endl;
cout << ps2.use_count()<<endl;
}
2.weak_ptr
shared_ptr在使用中,有一种情况,无法进行解决,就是当两个对象相互使用一个shared_ptr成员变量指向对方,会造成循环引用,使引用计数失效,从而导致内存泄漏。
代码如下:
class B;
class A
{
public:
shared_ptr<B> pb_;
~A()
{
cout << "A delete" << endl;
}
};
class B
{
public:
shared_ptr<A> pa_;
~B()
{
cout << "B delete" << endl;
}
};
void fun()
{
shared_ptr<B> pb(new B());
shared_ptr<A> pa(new A());
cout << pb.use_count() << endl;
cout << pa.use_count() << endl;
pb->pa_ = pa;
pa->pb_ = pb;
cout << pb.use_count() << endl;
cout << pa.use_count() << endl;
}
int main()
{
fun();
return 0;
}
可以看到,class A和class B 互相在成员变量中使用shared_ptr引用了对方。在fun函数跳出时,pa和pb进行栈释放。引用计数-1;但是,pa->pb_以及pb->pa_却没有办法进行释放,这是因为A和B的析构函数没有进行调用,这就导致shared_ptr的引用计数仍旧为1,导致内存泄漏。这就需要引入weak_ptr。
weak_ptr 是一种不控制对象生命周期的智能指针,它是一种弱引入,不增加或减少引用计数。它的出现就是为了解决两个类相互引用时shared_ptr无法进行对象销毁的问题。它和shared_ptr可以进行相互转换。
关于weak_ptr,我们需要注意的是,它么有*和->的操作符重载,我们不能直接进行使用,而是要使用lock()方法转换成shared_ptr,才能进行使用。
shared_ptr<B> p = pa->pb_.lock();
3.unique_ptr
unique_ptr从名字上来看,它持对对象享有独占权利,所以,它不能进行赋值,智能进行移动操作。而当进行移动时,也意味着它的生命周期结束,则将进行销毁。
代码如下:
unique_ptr<int>p1(new int(5));
unique_ptr<int>p2 = p1;
unique_ptr<int>p3 = move(p1);
p3.reset();
p1.reset();
unique_ptr<int>p4(new int(4));
unique_ptr<int>p5(new int(5));
p4.swap(p5);
cout << *p4 << endl;
cout << *p5 << endl;
unique_ptr<int> auto_pointer (new int);
int * manual_pointer;
*auto_pointer=10;
manual_pointer = auto_pointer.release();
cout << "manual_pointer points to " << *manual_pointer << endl;
delete manual_pointer;
|