菜鸟笔记
提升您的技术认知

神兵利器shared-ag真人游戏

本系列的上一篇文章讲了手动方式的内存管理 new delete
这种方式往往存在很多问题  这一节将讲通过智能指针来自动管理动态内存

智能指针的引入有以下几大原因

1.在动态内存管理中,如果new上一块空间,但是没有delete,就会产生内存泄露的问题。
2.但是有时候,我们new了,也delete了,但是还会出现问题。例如在new和delete之间调用了某个抛异常的函数,就有可能导致没有执行delete。
例如:fun2里面使用了new[],最后也使用了delete[],看着没有问题,但是在new和delete之间调用了fun1,而fun1里面抛了异常,但是在fun2的delete之前并没有捕获,就会导致delete没有执行,仍然会有内存泄露的问题。
void fun1()
{
      throw int(11);
}
void fun2()
{
      int* p = new int[10000];
      fun1();
      delete[] p;
}
int main()
{
      try
      {
           fun2();
      }
      catch (int& e)
      {
           cout << "捕获" << endl;
      }
      system("pause");
      return 0;
}
如果想要解决上面的问题,有一种方法:就是如果发现delete之前发现某个函数抛出了异常,就在delete之前捕获这个异常,并且在catch语句里面进行资源的释放,并且可以再将这个异常重新抛出。
void fun2()
{
      int* p = new int[10000];
      try
      {
           fun1();
      }
      catch(int& e)
      {
           delete[] p;
           cout << "重新抛出" << endl;
           throw;
      }
      delete[] p;
}
但是这种方法写着比较繁琐,而且如果代码很多,就有可能忘记哪个函数抛出了异常,会防不胜防。
3.还有一种场景就是内存空间是delete释放了以后  但是指针没有置null  (delete是不会把指针自动置为null的) 这样是野指针 
3.因此,只要有一种方法,能够在出了作用域之后能够自动释放掉申请的空间,就会让空间得到正确的释放。而一个对象出了作用域会自动调用自己的析构函数,只要在析构函数里能够释放自己开辟的空间,就能达到目的。
4.智能指针就有上面的作用,能够自动的管理指针所指向的动态资源的释放。它不仅有着raii的思想还能够像指针一样。(raii:分配资源即初始化,即构造函数分配资源和初始化资源,在析构函数清理资源。像指针一样:能够解引用)。
5.智能指针实质上就是一个模板类,成员变量包含一个任意类型的指针,构造函数获得资源并初始化,析构函数清理资源。
注意:智能指针只能管理动态开辟的空间。

 

c 98时代的智能指针

 

boost库的智能指针

 

c 11时代的智能指针
 

unique_ptr
	
介绍
		unique_ptr是auto_ptr的继承者,对于同一块内存只能有一个持有者,而unique_ptr和auto_ptr唯一区别就是unique_ptr不允许赋值操作,也就是不能放在等号的右边(函数的参数和返回值例外),这一定程度避免了一些误操作导致指针所有权转移,然而,unique_str依然有提供所有权转移的方法move,调用move后,原unique_ptr就会失效,再用其访问裸指针也会发生和auto_ptr相似的crash,如下面示例代码,所以,即使使用了unique_ptr,也要慎重使用move方法,防止指针所有权被转移。
unique_ptr up(new int(5));
//auto up2 = up; // 编译错误
auto up2 = move(up);
cout << *up << endl; //crash,up已经失效,无法访问其裸指针
除了上述用法,unique_ptr还支持创建动态数组。在c  中,创建数组有很多方法,如下所示:
// 静态数组,在编译时决定了数组大小
int arr[10];
// 通过指针创建在堆上的数组,可在运行时动态指定数组大小,但需要手动释放内存
int *arr = new int[10];
// 通过std::vector容器创建动态数组,无需手动释放数组内存
vector arr(10);
// 通过unique_ptr创建动态数组,也无需手动释放数组内存,比vector更轻量化
unique_ptr arr(new int[10]);
这里需要注意的是,不管vector还是unique_ptr,虽然可以帮我们自动释放数组内存,但如果数组的元素是复杂数据类型时,我们还需要在其析构函数中正确释放内存。
使用
 /***unique_ptr********/
    unique_ptr uni_p1;//unique_ptr默认初始化
    unique_ptr uni_p2(new int(42));//unique_ptr 普通初始化
        unique_ptr uni_p4(new int(51));//unique_ptr 普通初始化
        unique_ptr uni_p3(uni_p4.release());//unique_ptr 使用release初始化 
    //unique_ptr  uni_p2(uni_p1) 是错误的  不支持拷贝
    //unique_ptr uni_p2; uni_p2=unoi_p1是错误的 不支持赋值
       *uni_p3=4;//unique_ptr解引用赋值  
       cout<<*uni_p3<spa = make_shared(); //析构spa是会先打印dealloc b,再打印dealloc a
    return 0;
}
循环引用,笔者最先接触引用计数的语言就是objective-c,而oc中最常出现的内存问题就是循环引用,如下面代码所示,a中引用b,b中引用a,spa和spb的强引用计数永远大于等于1,所以直到程序退出前都不会被退出,这种情况有时候在正常的业务逻辑中是不可避免的,而解决循环引用的方法最有效就是改用weak_ptr,具体可见下一章。
class a {
public:
    shared_ptr b;
};
class b {
public:
    shared_ptr
网站地图