RAII策略
RAII(资源获取即初始化)是一种资源管理策略,它将内存分配,文件开关,执行线程等操作于对象的生命周期绑定起来,确保在控制对象的生命周期结束时,按照资源获取的相反顺序释放所有资源。
什么是智能指针
智能指针就是使用了RAII策略来对堆区分配出来的空间进行管理的一些类。
这些类中分装了原生指针。
为什么要有智能指针
在创建堆区空间时,如果申请内存失败,而报错,则就无法到达释放test_ptr的位置,就会导致堆区内存无法被释放,导致内存泄露。
两种智能指针解析
1).unique_ptr
只能管理一份资源,及不支持拷贝构造,赋值,同时不支持隐式类型转换来构造。
但是unique_ptr支持移动拷贝和移动赋值,但是被move的对象会被制空
在智能指针作为函数参数是,也要遵守不能拷贝构造和拷贝赋值。
2).shared_ptr
多个shared_ptr可以指向同时堆区空间(及支持拷贝构造,赋值),其通过引用计数来实现的,同时不支持隐式类型转换来构造。
shared_ptr中常用的接口如下。
use_count()接口,返回同一个堆区资源,有多少个指针指向该资源。
unique()接口,判断某个shared_ptr指针的引用计数是否是1。
C++14后智能指针新接口
make_unique
该接口存在符合现代 c++ 尽量避免使用 new 来构造的原则。
make_shared
该接口主要有以下优点
1).比起构造,赋值等初始化shared_ptr指针,make_shared接口的性能要好。
直接使用构造,赋值初始化shared_ptr会将分配的内存块和控制块分开申请。
使用make_shared接口初始化shared_ptr,内存块和控制块一起申请。
2).异常安全
方法1:调用init函数,则分为三布,1.new,2.调用shared_ptr构造, 3.调用getVal函数。
如果在上述三布中,2和3调换次序,且getVal函数报错,则会导致shared_ptr没有被构造,及智能指针没有管理new出来的空间。
方法2:分为两步,1.调用make_shared函数,2.调用getVal函数。
即使次序对调,也不会产生智能指针没有管理new出来的空间的情况,要么没有被申请空间,要么申请出了空间并且被智能指针管理起来了。
weak_ptr的作用
weak_ptr的作用主要是解决shared_ptr可能导致的循环引用问题
class Parent;
class Child
{
public:
Child()
{
cout << "Child" << endl;
}
~Child()
{
cout << "~Child" << endl;
}
public:
shared_ptr<Parent> Parent;
};
class Parent
{
public:
Parent()
{
cout << "Parent" << endl;
}
~Parent()
{
cout << "~Parent" << endl;
}
public:
shared_ptr<Child> Child;
};
int main()
{
shared_ptr<Child> pa(new Child());
shared_ptr<Parent> pb(new Parent());
pa->Parent = pb;
pb->Child = pa;
return 0;
}
其图形的内存布局如下。
当main函数结束后,pa,pb会被释放,则shared_ptr pa,shared_ptr pb会被释放,同时引用计数减减,会被减到1。
但是两个类内的指针没有被释放,但是pa,pb已经被释放了,那么这两个类就内存泄漏了。
为了解决该问题,C++提出了weak_ptr。
class Parent;
class Child
{
public:
Child()
{
cout << "Child" << endl;
}
~Child()
{
cout << "~Child" << endl;
}
public:
weak_ptr<Parent> Parent;
};
class Parent
{
public:
Parent()
{
cout << "Parent" << endl;
}
~Parent()
{
cout << "~Parent" << endl;
}
public:
weak_ptr<Child> Child;
};
int main()
{
shared_ptr<Child> pa(new Child());
shared_ptr<Parent> pb(new Parent());
pa->Parent = pb;
pb->Child = pa;
return 0;
}
weak_ptr的特点如下。
1).weak_ptr只对资源进行监视,而不对资源进行管理,不能指向某个固定的资源,及weak_ptr不会让强引用++。
强引用没有++
2).weak_ptr只会对shared_ptr指向的资源进行管理。
3).make_shared接口会因为存在weak_ptr而导致资源释放不及时,在内存吃紧的情况下,对内存的利用率较低。
在make_shared中,内存块和资源管理块是在一起的,要释放资源管理块,需要让强引用和弱引用都减到0,而如果没有使用make_shared接口,而使用new申请内存,则当强引用减到0后,就会释放内存块。