C++11中unique_ptr的使用

TuxedoLinux發表於2018-06-06

C++11中unique_ptr的使用

在C++中,動態記憶體的管理是通過一對運算子來完成的:new,在動態記憶體中為物件分配空間並返回一個指向該物件的指標,可以選擇對物件進行初始化;delete,接受一個動態物件的指標,銷燬該物件,並釋放與之關聯的記憶體。

動態記憶體的使用很容易出問題,因為確保在正確的時間釋放記憶體是極其困難的。有時會忘記釋放記憶體,在這種情況下會產生記憶體洩露;有時在尚有指標引用記憶體的情況下就釋放了它,在這種情況下就會產生引用非法記憶體的指標。

為了更容易(同時也更安全)地使用動態記憶體,C++11標準庫提供了兩種智慧指標(smart pointer)型別來管理動態物件。智慧指標的行為類似常規指標,重要的區別是它負責自動釋放所指的物件。C++11標準庫提供的這兩種智慧指標的區別在於管理底層指標的方式:shared_ptr允許多個指標指向同一個物件;unique_ptr則"獨佔"所指向的物件。C++11標準庫還定義了一個名為weak_ptr的輔助類,它是一種弱引用,指向shared_ptr所管理的物件。這三種型別都定義在memory標頭檔案中。智慧指標是模板類而不是指標。類似vector,智慧指標也是模板,當建立一個智慧指標時,必須提供額外的資訊即指標可以指向的型別。預設初始化的智慧指標中儲存著一個空指標。智慧指標的使用方式與普通指標類似。解引用一個智慧指標返回它指向的物件。如果在一個條件判斷中使用智慧指標,效果就是檢測它是否為空。

In C++, a smart pointer is implemented as a template class that mimics, by means of operator overloading, the behaviors of a traditional (raw) pointer, (e.g. dereferencing, assignment) while providing additional memory management features.

std::unique_ptr is a smart pointer that retains sole ownership of an object through a pointer and destroys that object when the unique_ptr goes out of scope. No two unique_ptr instances can manage the same object.

The object is destroyed and its memory deallocated when either of the following happens: (1)、the managing unique_ptr object is destroyed; (2)、the managing unique_ptr object is assigned another pointer via operator= or reset().

A unique_ptr may alternatively own no object, in which case it is called empty.

Only non-const unique_ptr can transfer the ownership of the managed object to another unique_ptr. The lifetime of an object managed by const std::unique_ptr is limited to the scope in which the pointer was created.

std::unique_ptr may be constructed for an incomplete type T. Conversely, std::shared_ptr can't be constructed from a raw pointer to incomplete type, but can be destroyed where T is incomplete.

A unique_ptr does not share its pointer. It cannot be copied to another unique_ptr, passed by value to a function, or used in any Standard Template Library (STL) algorithm that requires copies to be made. A unique_ptr can only be moved. This means that the ownership of the memory resource is transferred to another unique_ptr and the original unique_ptr no longer owns it.

When using unique_ptr, there can be at most one unique_ptr pointing at any one resource.When that unique_ptr is destroyed, the resource is automatically reclaimed.Because there can only be one unique_ptr to any resource, any attempt to make a copy of a unique_ptr will cause a compile-time error. However, unique_ptr can be moved using the new move semantics.

shared_ptr, allows for multiple pointers to point at a given resource. When the very last shared_ptr to a resource is destroyed, the resource will be deallocated. shared_ptr uses reference counting to track how many pointers refer to a resource, so you need to be careful not to introduce any reference cycles.

A unique_ptr is a container for a raw pointer, which the unique_ptr is said to own. A unique_ptr explicitly prevents copying of its contained pointer (as would happen with normal assignment), but the std::move function can be used to transfer ownership of the contained pointer to another unique_ptr. A unique_ptr cannot be copied because its copy constructor and assignment operators are explicitly deleted.

everything you can do with auto_ptr, unique_ptr will do as well.

There are two kinds of unique_ptr, one for scalars (i.e. non-arrays) and one for arrays:

(1)、unique_ptr<double> can hold a scalar of type double;

(2)、unique_ptr<double[]> can hold an array of double values with an unknown number of elements.

         std::unique_ptr是C++11標準中用來取代std::auto_ptr的指標容器(在C++11中,auto_ptr被廢棄)。它不能與其它unique_ptr型別的指標物件共享所指物件的記憶體。這種”所有權”僅能夠通過標準庫的move函式來轉移。unique_ptr是一個刪除了拷貝建構函式、保留了移動建構函式的指標封裝型別。

一個unique_ptr"擁有"它所指向的物件。與shared_ptr不同,某個時刻只能有一個unique_ptr指向一個給定物件。當unique_ptr被銷燬時,它所指向的物件也被銷燬。與shared_ptr不同,在C++11中,沒有類似make_shared的標準庫函式返回一個unique_ptr。當定義一個unique_ptr時,需要將其繫結到一個new返回的指標上。類似shared_ptr,初始化unique_ptr必須採用直接初始化形式。由於一個unique_ptr擁有它指向的物件,因此unique_ptr不支援普通的拷貝或賦值操作。雖然不能拷貝或賦值unique_ptr,但可以通過呼叫release或reset將指標的所有權從一個(非const)unique_ptr轉移給另一個unique。

呼叫release會切斷unique_ptr和它原來管理的物件間的聯絡。release返回的指標通過被用來初始化另一個智慧指標或給另一個智慧指標賦值。如果不用另一個智慧指標來儲存release返回的指標,程式就要負責資源的釋放。

不能拷貝unique_ptr的規則有一個例外:我們可以拷貝或賦值一個將要被銷燬的unique_ptr,最常見的例子是從函式返回一個unique_ptr。

類似shared_ptr,unique_ptr預設情況下用delete釋放它指向的物件。與shared_ptr一樣,可以過載一個unique_ptr中預設的刪除器。但是,unique_ptr管理刪除器的方式與shared_ptr不同。

下圖列出了unique_ptr支援的操作(來源於C++ Primer Fifth Edition 中文版):


下面是從其他文章中copy的測試程式碼,詳細內容介紹可以參考對應的reference:

  1. #include "unique_ptr.hpp"  
  2. #include <iostream>  
  3. #include <memory>  
  4. #include <string>  
  5. #include <cstdlib>  
  6. #include <utility>  
  7. #include <vector>  
  8. #include <algorithm>  
  9.   
  10. ///////////////////////////////////////////////////////  
  11. // reference: http://en.cppreference.com/w/cpp/memory/unique_ptr  
  12. struct Foo  
  13. {  
  14.     Foo()      { std::cout << "Foo::Foo\n"; }  
  15.     ~Foo()     { std::cout << "Foo::~Foo\n"; }  
  16.     void bar() { std::cout << "Foo::bar\n"; }  
  17. };  
  18.   
  19. void f(const Foo &)  
  20. {  
  21.     std::cout << "f(const Foo&)\n";  
  22. }  
  23.   
  24. int test_unique_ptr1()  
  25. {  
  26.     std::unique_ptr<Foo> p1(new Foo);  // p1 owns Foo  
  27.     if (p1) p1->bar();  
  28.   
  29.     {  
  30.         std::unique_ptr<Foo> p2(std::move(p1));  // now p2 owns Foo  
  31.         f(*p2);  
  32.   
  33.         p1 = std::move(p2);  // ownership returns to p1  
  34.         std::cout << "destroying p2...\n";  
  35.     }  
  36.   
  37.     if (p1) p1->bar();  
  38.   
  39.     // Foo instance is destroyed when p1 goes out of scope  
  40.   
  41.     return 0;  
  42. }  
  43.   
  44. //////////////////////////////////////////////////////  
  45. // reference: http://www.cplusplus.com/reference/memory/unique_ptr/unique_ptr/  
  46. int test_unique_ptr2()  
  47. {  
  48.     std::default_delete<int> d;  
  49.     std::unique_ptr<int> u1;  
  50.     std::unique_ptr<int> u2(nullptr);  
  51.     std::unique_ptr<int> u3(new int);  
  52.     std::unique_ptr<int> u4(new int, d);  
  53.     std::unique_ptr<int> u5(new int, std::default_delete<int>());  
  54.     std::unique_ptr<int> u6(std::move(u5));  
  55.     std::unique_ptr<int> u7(std::move(u6));  
  56.     std::unique_ptr<int> u8(std::auto_ptr<int>(new int));  
  57.   
  58.     std::cout << "u1: " << (u1 ? "not null" : "null") << '\n';  
  59.     std::cout << "u2: " << (u2 ? "not null" : "null") << '\n';  
  60.     std::cout << "u3: " << (u3 ? "not null" : "null") << '\n';  
  61.     std::cout << "u4: " << (u4 ? "not null" : "null") << '\n';  
  62.     std::cout << "u5: " << (u5 ? "not null" : "null") << '\n';  
  63.     std::cout << "u6: " << (u6 ? "not null" : "null") << '\n';  
  64.     std::cout << "u7: " << (u7 ? "not null" : "null") << '\n';  
  65.     std::cout << "u8: " << (u8 ? "not null" : "null") << '\n';  
  66.   
  67.     return 0;  
  68. }  
  69.   
  70. //////////////////////////////////////////////////////  
  71. // reference: http://eli.thegreenplace.net/2012/06/20/c11-using-unique_ptr-with-standard-library-containers  
  72. struct Foo_0 {  
  73.     Foo_0() { std::cerr << "Foo_0 [" << this << "] constructed\n"; }  
  74.     virtual ~Foo_0() { std::cerr << "Foo_0 [" << this << "] destructed\n"; }  
  75. };  
  76.   
  77. void sink(std::unique_ptr<Foo_0> p) {  
  78.     std::cerr << "Sink owns Foo_0 [" << p.get() << "]\n";  
  79. }  
  80.   
  81. std::unique_ptr<Foo_0> source() {  
  82.     std::cerr << "Creating Foo_0 in source\n";  
  83.     return std::unique_ptr<Foo_0>(new Foo_0);  
  84. }  
  85.   
  86. int test_unique_ptr3()  
  87. {  
  88.     std::cerr << "Calling source\n";  
  89.     std::unique_ptr<Foo_0> pmain = source();  // Can also be written as  
  90.     // auto pmain = source();  
  91.   
  92.     std::cerr << "Now pmain owns Foo [" << pmain.get() << "]\n";  
  93.     std::cerr << "Passing it to sink\n";  
  94.     // sink(pmain);                    // ERROR! can't copy unique_ptr  
  95.     sink(move(pmain));              // OK: can move it!  
  96.   
  97.     std::cerr << "Main done\n";  
  98.     return 0;  
  99. }  
  100.   
  101. ////////////////////////////////////////////////////  
  102. // reference: http://www.codeguru.com/cpp/article.php/c17775/The-Smart-Pointer-That-Makes-Your-C-Applications-Safer--stduniqueptr.htm  
  103. void func(int*)  
  104. {  
  105.   
  106. }  
  107.   
  108. int test_unique_ptr4()  
  109. {  
  110.     // default construction  
  111.     std::unique_ptr<int> up; //creates an empty object  
  112.   
  113.     // initialize with an argument  
  114.     std::unique_ptr<int> uptr(new int(3));  
  115.     double *pd = new double;  
  116.     std::unique_ptr<double> uptr2(pd);  
  117.     // overloaded * and ->  
  118.     *uptr2 = 23.5;  
  119.     std::unique_ptr<std::string> ups(new std::string("hello"));  
  120.     int len = ups->size();  
  121.   
  122.     // Reset() releases the owned resource and optionally acquires a new resource:  
  123.     uptr2.reset(new double); //delete pd and acquire a new pointer  
  124.     uptr2.reset(); //delete the pointer acquired by the previous reset() call  
  125.   
  126.     // If you need to access the owned pointer directly use get()  
  127.     func(uptr.get());  
  128.   
  129.     // Unique_ptr has implicit conversion to bool.  
  130.     // This lets you use unique_ptr object in Boolean expressions such as this:  
  131.     if (ups) //implicit conversion to bool  
  132.         std::cout << *ups << std::endl;  
  133.     else  
  134.         std::cout << "an empty smart pointer" << std::endl;  
  135.   
  136.     // Array Support: Unique_ptr can store arrays as well.  
  137.     // A unique_ptr that owns an array defines an overloaded operator [].  
  138.     // Obviously, the * and -> operators are not available.  
  139.     // Additionally, the default deleter calls delete[] instead of delete:  
  140.     std::unique_ptr<int[]> arrup(new int[5]);  
  141.     arrup[0] = 5;  
  142.     // std::cout << *arrup << std::endl; //error, operator * not defined   
  143.   
  144.     // Compatibility with Containers and Algorithms  
  145.     // You can safely store unique_ptr in Standard Library containers and let algorithms manipulate sequences of unique_ptr objects.  
  146.     std::vector<std::unique_ptr<int>> vi;  
  147.     vi.push_back(std::unique_ptr<int>(new int(0)));  // populate vector  
  148.     vi.push_back(std::unique_ptr<int>(new int(3)));  
  149.     vi.push_back(std::unique_ptr<int>(new int(2)));  
  150.     std::sort(vi.begin(), vi.end());  // result: {0, 2, 3}  
  151.   
  152.     return 0;  
  153. }  
  154.   
  155. //////////////////////////////////////////////////////////////////  
  156. template <typename T>  
  157. class Add {  
  158. public:  
  159.     T add_sub(T a, T b)  
  160.     {  
  161.         return (a + b) * (a - b);  
  162.     }  
  163. };  
  164.   
  165.   
  166. int test_unique_ptr5()  
  167. {  
  168.     std::unique_ptr<Add<int>> tt(new Add<int>());  
  169.     int a{ 10 }, b{ 5 };  
  170.   
  171.     std::cout << tt->add_sub(a, b) << std::endl;  
  172.   
  173.     return 0;  
  174. }  
  175.   
  176. //////////////////////////////////////////////////////////////////  
  177. int test_unique_ptr6()  
  178. {  
  179.     std::unique_ptr<int[]> tmp(new int[100]);  
  180.     std::for_each(tmp.get(), tmp.get() + 100, [](int& n) {n = 66; });  
  181.     std::cout << tmp[99] << std::endl;  
  182.   
  183.     return 0;  
  184. }  

GitHubhttps://github.com/fengbingchun/Messy_Test

相關文章