zoukankan      html  css  js  c++  java
  • c++智能指针

    智能指针的行为类似常规指针,重要的区别是它负责自动释放所指的对象。C++11标准库提供的这两种智能指针的区别在于管理底层指针的方式:shared_ptr允许多个指针指向同一个对象;unique_ptr则"独占"所指向的对象。

    C++11提供了三种智能指针:std::shared_ptr, std::unique_ptr, std::weak_ptr,使用时需添加头文件<memory>

    一、unique_ptr

    unique_ptr <>是c ++ 11提供的智能指针实现之一,用于防止内存泄漏。unique_ptr对象包含一个原始指针,并负责其生命周期。当这个对象被销毁的时候,它的析构函数会删除关联的原始指针。

    unique_ptr有重载的- >和*运算符。

    1、初始化

    直接使用new

    unique_ptr<int> up1(new int());      //okay,直接初始化

    unique_ptr<int> up2 = new int();    //error! 构造函数是explicit  (C++中的explicit关键字只能用于修饰只有一个参数的类构造函数, 它的作用是表明该构造函数是显式的, 而非隐式的)

    unique_ptr<int> up3(up1);             //error! 不允许拷贝

    与shared_ptr不同,unique_ptr拥有它所指向的对象,在某一时刻,只能有一个unique_ptr指向特定的对象。当unique_ptr被销毁时,它所指向的对象也会被销毁。因此不允许多个unique_ptr指向同一个对象,所以不允许拷贝与赋值。

    2、成员函数

    unique_ptr<T> up 
    空的unique_ptr,可以指向类型为T的对象,默认使用delete来释放内存

    up.release() up 放弃对它所指对象的控制权,并返回保存的指针,将up置为空,不会释放内存

    up.reset(…) 参数可以为 空、内置指针,先将up所指对象释放,然后重置up的值.

    用法请见:

    https://blog.csdn.net/lijinqi1987/article/details/79005794

    https://www.cnblogs.com/DswCnblog/p/5628195.html

     使用实例如下:

    #include <iostream>
    #include <memory>
     
    struct Task {
      int mId;
      Task(int id) : mId(id) {
        std::cout << "Task::Constructor" << std::endl;
      }
      ~Task() {
        std::cout << "Task::Destructor" << std::endl;
      }
    };
     
    int main() {
      //空unique_ptr对象
      std::unique_ptr<int> ptr1;
     
      //检查unique_ptr对象是否为空
      if (!ptr1) {
        std::cout << "ptr1 is empty" << std::endl;
      }
     
      //检查unique_ptr对象是否为空
      if (ptr1 == nullptr) {
        std::cout << "ptr1 is empty" << std::endl;
      }
     
      //不能通过赋值初始化创建unique_ptr对象
      //std::unique_ptr<Task> taskPtr2 = new Task(); //编译错误
     
      //通过原始指针创建unique_ptr对象
      std::unique_ptr<Task> taskPtr(new Task(23));
     
      //检查taskPtr是否为空,或者是否有关联的原始指针
      if (taskPtr != nullptr) {
        std::cout << "taskPtr is  not empty" << std::endl;
      }
     
      //通过unique_ptr访问内部元素
      std::cout << taskPtr->mId << std::endl;
     
      std::cout << "Reset the taskPtr" << std::endl;
      //重置unique_ptr将删除关联的原始指针,并使unique_ptr对象为空
      taskPtr.reset();
     
      //检查taskPtr是否为空,或者是否有关联的原始指针
      if (taskPtr == nullptr) {
        std::cout << "taskPtr is  empty" << std::endl;
      }
     
      //通过原始指针创建unique_ptr对象
      std::unique_ptr<Task> taskPtr2(new Task(55));
     
      if (taskPtr2 != nullptr) {
        std::cout << "taskPtr2 is  not empty" << std::endl;
      }
     
      //unique_ptr 对象不可复制
      //taskPtr = taskPtr2;  //编译错误
     
      //unique_ptr 对象不可复制
      //std::unique_ptr<Task> taskPtr3 = taskPtr2; //编译错误
     
      {
        //转移所有权
        std::unique_ptr<Task> taskPtr4 = std::move(taskPtr2);  //将原始指针的所有权转移给taskPtr4后,taskPtr2将为空
     
        if (taskPtr2 == nullptr) {
          std::cout << "taskPtr2 is  empty" << std::endl;
        }
     
        //taskPtr2的所有权转移给了task4
        if (taskPtr4 != nullptr) {
          std::cout << "taskPtr4 is not empty" << std::endl;
        }
     
        std::cout << taskPtr4->mId << std::endl;
        //taskPtr4超出范围并删除关联的原始指针
      }
     
      //通过原始指针创建unique_ptr对象
      std::unique_ptr<Task>taskPtr5(new Task(55));
     
      if (taskPtr5 != nullptr) {
        std::cout << "taskPtr5 is not empty" << std::endl;
      }
     
      //从原始指针释放对象的所有权
      Task* ptr = taskPtr5.release();
     
      if (taskPtr5 == nullptr) {
        std::cout << "taskPtr5 is empty" << std::endl;
      }
     
      std::cout << ptr->mId << std::endl;
     
      delete ptr;
     
      return 0;
    }

    运行结果如下:

    3、 unique_ptr特性用法——release()和reset()用法区别

    //release()用法
     //release()返回原来智能指针指向的指针,只负责转移控制权,不负责释放内存,常见的用法
     unique_ptr<int> q(p.release()) // 此时p失去了原来的的控制权交由q,同时p指向nullptr  
     //所以如果单独用:
     p.release()
     //则会导致p丢了控制权的同时,原来的内存得不到释放
    //reset()用法
     p.reset()     // 释放p原来的对象,并将其置为nullptr,
     p = nullptr   // 等同于上面一步
     p.reset(q)    // 注意此处q为一个内置指针,令p释放原来的内存,p新指向这个对象

    主意release()只转移控制权,并不释放内存,而reset和=nullptr操作会释放原来的内存

    二、shared_ptr

    shared_ptr会记录有多少个shared_ptr指向同一个对象,当我们拷贝或者赋值一个shared_ptr时,计数器加一,被销毁则减一,为0则释放内存(析构函数)。

    每个 shared_ptr 对象在内部指向两个内存位置:
    1)、指向对象的指针。
    2)、用于控制引用计数数据的指针。
    共享所有权如何在参考计数的帮助下工作:
    1)、当新的 shared_ptr 对象与指针关联时,则在其构造函数中,将与此指针关联的引用计数增加1。
    2)、当任何 shared_ptr 对象超出作用域时,则在其析构函数中,它将关联指针的引用计数减1。如果引用计数变为0,则表示没有其他 shared_ptr 对象与此内存关联,在这种情况下,它使用delete函数删除该内存。

     1、初始化

    1) 构造函数初始化

      std::shared_ptr<int> pointer(new int(1));

      std::shared_ptr<int> pointer1 = pointer;

      std::shared_ptr<std::string> ss(new std::string("AAA"));

      std::shared_ptr<std::string> = std::shared_ptr<std::string>(new std::string("AAA"));

    2)std::make_shared 初始化(推荐方式)

      std::shared_ptr<string> p3 = std::make_shared<string>();

      std::shared_ptr<string> p2 = std::make_shared<string>("hello");

      //auto关键字代替std::shared_ptr,p5指向一个动态分配的空vector<string>
      auto p5 = make_shared<vector<string>>();

    3)reset初始化

         std::shared_ptr<int> pointer = nullptr;

      pointer.reset(new int(1));

         

    2、成员函数 

    1) reset()  使 shared_ptr 对象取消与相关指针的关联

    不带参:

    pointer .reset();    //它将引用计数减少1,如果引用计数变为0,则删除指针。

    带参数:

    pointer.reset(new int(1));  //在这种情况下,它将在内部指向新指针,因此其引用计数将再次变为1。

    使用nullptr重置:

    p1 = nullptr;

     2)use_count 引用计数

    #include <iostream>
    #include <memory>
    using namespace std;
    
    int main()
    {
        std::shared_ptr<int> ptrA1 = std::make_shared<int>(10);
         std::cout << ptrA1.use_count() << std::endl;
        std:shared_ptr<int> ptrA2(ptrA1);
         std::cout << ptrA1.use_count() << std::endl;
        std::shared_ptr<int> ptrB1 = std::make_shared<int>(20);
         std::cout << ptrB1.use_count() << std::endl;
        ptrA2 = ptrB1;
         std::cout << ptrA1.use_count() << std::endl;
         std::cout << ptrB1.use_count() << std::endl;
        return 0;
    }

    输出:1 2 1 1 2

    智能指针陷阱:

    https://blog.csdn.net/y1196645376/article/details/53023848

    附:explicit用法

    #include <iostream>
    using namespace  std;
    
    class A {
      public:
         explicit A(int a)
        {
            cout<<"创建类成功了!"<<endl;
        
        }
    };
    int main()
    {
        A a=10;
        return 0;
    }

    上面的代码编译不成功,原因是当显式地定义了一个带一个参数的构造函数( 带explicit),必须要显示地调用构造函数,

    A a(10);

    如果不加 explicit的话,可以这样用

    A a=10;

    实际的转换过程如下:
    相当于直接调用A(10)

  • 相关阅读:
    mysql之全局查询日志
    使用MySQL Workbench进行数据库设计——MySQL Workbench使用方法总结
    HttpClient工具类
    JSON字符串转换为Map
    Java判断一个日期是否在下周日期区间
    Linux 查看日志文件
    判断一个日期是否为当前日期的前后几天的方法
    Spring-Boot 整合Dubbo 解决@Reference 注解为null情况
    spring注解之@Scope
    Swagger Annotation 详解(建议收藏)
  • 原文地址:https://www.cnblogs.com/sunshine1218/p/14485177.html
Copyright © 2011-2022 走看看