#《C++11新特兴—C++标准库的更新》

特性1:智能指针

解决问题:C++中没有垃圾回收机制,必须自己释放内存,否则就会造成内存泄漏。因此采用智能指针(smart pointer)来解决该问题。动态指针是一个存储指向动态分配(堆)对象指针的类,用于生存期的控制,能够确保在离开指针所在作用域时,自动地销毁分配的对象,防止内存泄露。该技术的核心在于引用计数,每使用它一次,内部引用技术加1,每析构一次内部的引用计数减一,减到0时,删除所指向的堆内存。

类型

C++11中引入了三种智能指针,使用时需要包含#include <memory>头文件。

  • std::shared_ptr:==共享==的智能指针。可以由多个指针去管理一个内存,每增加引用一个计数器加1,减少一个引用计数器减1.当计数器减为0时需要删除该指针所指向的堆内存。

    未命名绘图

  • std:unique_ptr:==独占==的智能指针。只有一个指针指示,如果需要新的指针指示需要move操作。

  • std:weak_ptr:弱引用的智能指针,它不共享指针,不能操作资源,是用来监视shared_ptr的。

使用方法

1.std::shared_ptr

  • 初始化——可以采用四种初始化方法

    • 通过==构造函数==初始化std::shared_ptr<T> 智能指针名字(创建堆内存);

      1
      shared_ptr<int> ptr1(new int(10)); // 对ptr1采用构造函数初始化
    • 使用==拷贝==和==移动==构造函数初始化

      1
      2
      3
      4
      5
      6
      7
      8
      9
      shared_ptr<int> ptr1(new int(10)); // 对ptr1采用构造函数初始化

      // 拷贝构造函数
      shared_ptr<int> ptr2(ptr1); // 方式1
      shared_ptr<int> ptr3=ptr1; // 方式2

      // 移动构造函数
      shared_ptr<int> ptr2(std::move(ptr1)); // 方式1
      shared_ptr<int> ptr3=std::move(ptr1); // 方式2
    • C++中提供std::make_shared()可以完成内存对象的创建并将其初始化给智能指针

      1
      2
      template<class T, class... Args>
      shared_ptr<T> make_shared(Args&& ... args);
      • T:模板参数的数据类型
      • Args&& ... args:要初始化的数据,如果是通过make_shared创建对象,需按照构造函数的参数列表指定。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      class Test{
      public:
      Test(){}
      Test(int a){}
      Test(string str){}
      ~Test(){}
      };

      int main(){
      shared_ptr<Test> ptr=make_shared<Test>("dsfds"); // 有参构造函数
      shared_ptr<Test> ptr=make_shared<Test>(); // 默认构造函数
      return 0;
      }
    • 通过reset方法初始化。

      1
      2
      3
      4
      5
      6
      // reset 两个功能:重置指针 让该指针重新指向一块内存
      shared_ptr<Test> ptr=make_shread<Test>();
      // 重置
      ptr.reset(); // ptr指向的内存的引用计数为0
      // 重新指向一块内存
      ptr.reset(new Test()); // 注意这里ptr重新指向内存的内省必须和重置之前一样,都是Test
  • 使用方法

    • 获取到原始地址,再对内存进行操作

      1
      2
      Test* p=ptr.get();
      t->Get();
    • 直接使用智能指针进行操作

      1
      2
      3
      // 将指针指针想象成一个指针
      shared_ptr<Test> ptr=make_shared(new Test(10)); // ptr是一个对象
      ptr->setValue(5); // 将其想象成一个指针进行操作
    • 指定删除器,当内存的引用计数为$0$时,这块内存就会被智能指针析构掉。此外我们也可以在初始化时指定一个删除动作,该删除操作对应的函数称为删除器。删除器函数本质是一个回调函数,我们只需要进行实现,==调用由智能指针完成==。

      1
      2
      3
      4
      5
      6
      7
      void deleteIntPtr(int *p){

      delete p;
      cout<<<"point p was releaed"<<endl;
      }

      shared_ptr<int> ptr(new int(23),deleteIntPtr);

    ​ 注1:一般系统重写删除器主要是用于数据指针,因为==不指定数组类型指针时,只会删除一个对象==。再删除数组时除了自己写删除器,还可以使用系统中自带str::default_delete<T>()函数作为删除器。

    ​ 注2:在程序中多次需要对数组型指针释放时可以对申请智能时进行封装。

    1
    2
    3
    4
    template<Typename T>
    shared_ptr<T> make_share_array(size_t size){
    return shared_ptr<T>(new T[size],defalut_delete<T[]>()); //采用默认的删除器加上[]告诉系统是一个数组
    }

    ​ 注3:删除器函数也可以是lambda表达式。

    1
    shared_ptr<int> ptr(new int(23),[](int*p){delete p;});

    注意事项:

  • 不能使用一个原始地址初始化多个共享智能指针

    1
    2
    3
    4
    int * p=new int(10);
    shared_ptr<int> ptr1(p);
    shared_ptr<int> ptr2(p);
    // 此时会出现析构两次,因为每个智能指针中的计数都为1
  • 函数不能返回管理this的共享智能指针对象,可能会造成多次析构。

  • 共享智能指针不能循环引用,否则不能进行释放,因为计数器始终不会为0.

**2.std::unique_ptr**:和shared_ptr的主要区别在于==不允许其他的智能指针共享其内部的指针==,可以通过它的构造函数初始化一个独占智能指针对象,但是不允许通过赋值将一个unique_ptr复制给另一个unique_pte

  • 初始化——可以采用四种初始化方法

    • 通过构造函数初始化。

      1
      2
      unique<int> ptr1(new int(10)); // 构造函数初始化
      unique<int> ptr2=ptr1; // 直接赋值错误
    • 通过std::move来m_STRAPs给其他std::unique_ptr,但是原始指针的所有权就被转移了,这个原始指针还是被独占的。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      unique_ptr<int> func()
      {
      return unique_ptr<int> (new int(520));
      }

      int main(){
      unique<int> ptr1(new int(10));
      unique<int> ptr2=move(ptr1); // 采用move
      unique<int> ptr3=func(); // 将亡值
      return 0;
      }
    • 通过**reset方法**,使用该方法可以让unique_pte解除对原始内存的管理,也可以用来初始化一个独占的智能指针。

      1
      2
      3
      4
      5
      6
      7
      int main(){
      unique<int> ptr1(new int(10));
      unique<int> ptr2=move(ptr1); // 采用move
      ptr1.reset(); // 解除对原始内存的管理
      ptr2.reset(new int(732)); // 重新指定智能指针管理的原始内存
      return 0;
      }
    • 使用make_unique()函数进行初始化。

  • 使用方法——获取智能指针管理的原始地址

    • 使用get()方法,函数原型如下:

      1
      2
      3
      4
      5
      6
      7
      poniter get() const noexcept;
      // 具体使用
      int main(){
      unique<int> ptr1(new int(10));
      cout<<*ptr1.get(); //10
      return 0;
      }
    • 采用指针直接使用。

  • 指定删除器

    • 在初始时指定删除器有所区别
1
2
3
4
5
using ptrFunc =void(*)(Test*);
unique_ptr<Test,function<void(Test*)>> ptr4(new Test("hello")),[=](Test*)(){
cout<<"------------"<<endl;
delete t;
}
  • ==独占==的智能指针可以管理数组类型的地址,==能够自动释放==。但是==C++11中共享型不可以自动释放数组,在C++11之后才可以支持这种写法==。

    1
    2
    unique_ptr<Test[]> ptr(new Test[3]);  // right 数组型释放
    shared_ptr<Test[]> ptr(new Test[3]); // c++11之后才支持该写法

Tips: lambda表达式?仿函数?可调用对象包装器?

3.std::weak_ptr

主要功能:用于辅助shared_ptr的工作。采用shared_ptr时会存在以下问题:

1、在一些特殊的对象下会将析构函数调用两次

1
2
3
4
5
6
7
struct Test
{
shared_ptr<Test> getSharedPtr()
{
return shared_ptr<Test>(this); // 采用对象进行初始化,此时会产生析构两次的问题
}
}

2、可能会产生循环调用,无法进行释放内存。因此weak_ptr用于将下面的结构变为链式结构

![Untitled Diagram.drawio (1)](https://skdtypora1.oss-cn-beijing.aliyuncs.com/img/Untitled Diagram.drawio (1).png)

解决方法:让其中一个shared_ptr变为weak_ptr