C++智能指针:unique_ptr详解

文章目录

      • unique_ptr描述
        • 声明
        • 作用
      • 函数指针描述
      • 总结

unique_ptr描述

声明

头文件:
模版类:

  • 默认类型template > class unique_ptr
  • 数组类型template class unique_ptr;

作用

与shared_ptr最大的区别即是unique_ptr不能够共享同一个地址,它对地址是独占得。当unique_ptr对象的生命周期结束,则它所引用的地址空间也会被释放。

一个unique_ptr对象主要包含两个部分

  • 一个存储指针:主要用来管理对象的地址空间。它是在构造函数中分配的地址,并且能够通过赋值运算符以及reset成员函数进行地址空间的重新指向。并且可以通过get和release成员变量进行单独访问,获取unique_ptr对象的地址空间。
  • 一个存储删除器:删除器为一个可以被调用的对象。主要被用来删除unique_ptr对象的地址空间。同时能够使用赋值运算符进行当前对象的更改,并通过get_deleter成员函数进行单独访问。

函数指针描述

  • 构造函数
    //unique_ptr constructor example
    #include 
    #include 
    
    int main () {
      std::default_delete<int> d;
      //默认为空
      std::unique_ptr<int> u1;
      //使用null初始化指针,仍然为空
      std::unique_ptr<int> u2 (nullptr);
      //正常初始化
      std::unique_ptr<int> u3 (new int);
      //存储指针正常初始化,之后并初始化删除器的值,所以不为空
      std::unique_ptr<int> u4 (new int, d);
      //存储指针正常初始化,之后删除器使用默认的构造函数进行初始化,所以unique_ptr又为空了
      std::unique_ptr<int> u5 (new int, std::default_delete<int>());
      std::unique_ptr<int> u6 (std::move(u5));
      std::unique_ptr<int> u7 (std::move(u6));
      std::unique_ptr<int> u8 (std::auto_ptr<int>(new int));
    
      std::cout << "u1: " << (u1?"not null":"null") << '\n';
      std::cout << "u2: " << (u2?"not null":"null") << '\n';
      std::cout << "u3: " << (u3?"not null":"null") << '\n';
      std::cout << "u4: " << (u4?"not null":"null") << '\n';
      std::cout << "u5: " << (u5?"not null":"null") << '\n';
      std::cout << "u6: " << (u6?"not null":"null") << '\n';
      std::cout << "u7: " << (u7?"not null":"null") << '\n';
      std::cout << "u8: " << (u8?"not null":"null") << '\n';
    
      return 0;
    }
    
    输出如下:
    u1: null
    u2: null
    u3: not null
    u4: not null
    u5: null
    u6: null
    u7: not null
    u8: not null
    
  • 析构函数;如果对象为空的unique_ptr,即使用get()==nullptr,则析构函数无法产生作用
    否则会正常删除对象,就像get_deleter()函数一样
    // unique_ptr destructor example
    #include 
    #include 
    
    int main () {
      auto deleter = [](int*p){
        delete p;
        std::cout << "[deleter called]\n";
      };
    
      std::unique_ptr<int,decltype(deleter)> foo (new int,deleter);
    
      std::cout << "foo " << (foo?"is not":"is") << " empty\n";
    
      return 0;                        // [deleter called]
    }
    
    输出如下:
    foo is not empty
    [deleter called]
    
  • operator=
    // unique_ptr::operator= example
    #include 
    #include 
    
    int main () {
      std::unique_ptr<int> foo;
      std::unique_ptr<int> bar;
    
      foo = std::unique_ptr<int>(new int (101));  // rvalue
    	
      //std::move操作是将foo对象的地址以及空间内容转给bar,所以执行之后foo变为了empty
      //之所以使用std::move操作是因为unique_ptr对象地址空间只能被一个对象独享
      bar = std::move(foo);                       // using std::move
    
      std::cout << "foo: ";
      if (foo) std::cout << *foo << '\n'; else std::cout << "empty\n";
    
      std::cout << "bar: ";
      if (bar) std::cout << *bar << '\n'; else std::cout << "empty\n";
    
      return 0;
    }
    
    输出如下:
    foo: empty
    bar: 101
    
  • std :: unique_ptr :: get成员,改成员函数返回被管理的unique_ptr对象,此函数的调用不会使unique_ptr释放指针的所有权(即,它仍然负责在某个时刻删除管理数据)。因此,此函数返回的值不得用于构造新的管理指针。为了活动存储指针,并且能够正常释放,则使用release()成员函数
    // unique_ptr::get vs unique_ptr::release
    #include 
    #include 
    
    int main () {
                                               // foo   bar    p
                                               // ---   ---   ---
      std::unique_ptr<int> foo;                // null
      std::unique_ptr<int> bar;                // null  null
      int* p = nullptr;                        // null  null  null
    
      foo = std::unique_ptr<int>(new int(10)); // (10)  null  null
      //这里经过std::move之后foo的地址以及内容转移给了bar
      std::cout << "foo: " << foo.get() << std::endl;
      bar = std::move(foo);                    // null  (10)  null
      std::cout << "foo: " << foo.get() << std::endl;
      std::cout << "bar: " << bar.get() << std::endl;
      p = bar.get();                           // null  (10)  (10)
      *p = 20;                                 // null  (20)  (20)
      p = nullptr;                             // null  (20)  null
    
      foo = std::unique_ptr<int>(new int(30)); // (30)  (20)  null
      p = foo.release();                       // null  (20)  (30)
      *p = 40;                                 // null  (20)  (40)
    
      std::cout << "foo: ";
      if (foo) std::cout << *foo << '\n'; else std::cout << "(null)\n";
    
      std::cout << "bar: ";
      if (bar) std::cout << *bar << '\n'; else std::cout << "(null)\n";
    
      std::cout << "p: ";
      if (p) std::cout << *p << '\n'; else std::cout << "(null)\n";
      std::cout << '\n';
    
      delete p;   // the program is now responsible of deleting the object pointed to by p
                  // bar deletes its managed object automatically
    
      return 0;
    }
    
    输出如下:
    optionscompilationexecution
    foo: 0x817a10
    foo: 0
    bar: 0x817a10
    foo: (null)
    bar: 20
    p: 40
    
  • std :: unique_ptr :: release
    通过改成员函数的返回值以及空指针来释放当前unique_ptr指针的所有权
    当前调用并不会破坏管理对象,改成员函数不会删除对象,而需要其他实体在某个时候删除对象。如果想要强制删除对象,需要使用reset或者赋值运算符(std::move)
    // unique_ptr::release example
    #include 
    #include 
    
    int main () {
      std::unique_ptr<int> auto_pointer (new int);
      int * manual_pointer;
    
      *auto_pointer=10;
        std::cout << " auto_pointer " << auto_pointer.get() << std::endl;
      manual_pointer = auto_pointer.release();
      std::cout << " auto_pointer " << auto_pointer.get() << std::endl;
      std::cout << " manual_pointer " << manual_pointer<< std::endl;
      // (auto_pointer is now empty)
    
      std::cout << "manual_pointer points to " << *manual_pointer << '\n';
    
      delete manual_pointer;
    
      return 0;
    }
    
    输出如下:
    auto_pointer 0x3afffa0
     auto_pointer 0
     manual_pointer 0x3afffa0
    manual_pointer points to 10
    
  • std::unique_ptr::reset
    void reset (pointer p = pointer()) noexcept;
    破坏掉当前unique_ptr对象,并且获取它所有权p,如果p是空的,则当前unique_ptr对象也即为空
    如果想要释放当前对象,并且并不破坏对象所指地址空间以及内容,则使用release成员函数
    // unique_ptr::reset example
    #include 
    #include 
    
    int main () {
      std::unique_ptr<int> up;  // empty
    
      //reset之后 up之前对象所指空间已经被破坏,并重新接管up对象,分配新的地址空间
      up.reset (new int);       // takes ownership of pointer
      //可以看到地址空间已经由之前的null变为重新分配的空间
      std::cout << *up << " " << up.get() << '\n';
      *up=5;
      std::cout << *up << " " << up.get() << '\n';
    
      up.reset (new int);       // deletes managed object, acquires new pointer
      *up=10;
      std::cout << *up << " " << up.get() << '\n';
    
      up.reset();               // deletes managed object
    
      return 0;
    }
    
    输出如下:
    0 0xd7dc70
    5 0xd7dc70
    10 0xd7dc90
    
  • std::unique_ptr::swap交换对象空间以及内容,且并不破坏地址空间
    // unique_ptr::swap example
     #include 
     #include 
     
     int main () {
       std::unique_ptr<int> foo (new int(10));
       std::unique_ptr<int> bar (new int(20));
       
       std::cout << "foo: " << *foo << " " << foo.get() << '\n';
       std::cout << "bar: " << *bar << " " << bar.get() << '\n';
     
       foo.swap(bar);
     
       std::cout << "foo: " << *foo << " " << foo.get() << '\n';
       std::cout << "bar: " << *bar << " " << bar.get() << '\n';
     
       return 0;
     }
    
    输出如下:
    foo: 10 0x2cd9ae0
    bar: 20 0x2cd9b00
    foo: 20 0x2cd9b00
    bar: 10 0x2cd9ae0
    

总结

  • shared_ptr地址空间无法被多个智能指针共享,当实际当前对象地址作用域结束,则改对象所占有地址空间将被释放
  • 使用release成员函数可以转移unique_ptr队形的所有圈,即将unique_ptr对象转为非unique_ptr对象,并不破坏地址
  • 使用reset会重置对象地址空间,并重新分配。会破坏地址空间
  • 使用赋值元算符,和reset函数类似重新指定地址空间,同样会破坏地址空间
  • unique_ptr对象的返回智能使用成员函数get

你可能感兴趣的:(#,编程语言:C++,编程语言)