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:
#include "unique_ptr.hpp"
#include <iostream>
#include <memory>
#include <string>
#include <cstdlib>
#include <utility>
#include <vector>
#include <algorithm>
#include <cassert>
#include <fstream>
#include <functional>namespace unique_ptr_ {///
// reference: http://en.cppreference.com/w/cpp/memory/unique_ptr
struct Foo
{Foo() { std::cout << "Foo::Foo\n"; }~Foo() { std::cout << "Foo::~Foo\n"; }void bar() { std::cout << "Foo::bar\n"; }
};void f(const Foo &)
{std::cout << "f(const Foo&)\n";
}int test_unique_ptr1()
{std::unique_ptr<Foo> p1(new Foo); // p1 owns Fooif (p1) p1->bar();{std::unique_ptr<Foo> p2(std::move(p1)); // now p2 owns Foof(*p2);p1 = std::move(p2); // ownership returns to p1std::cout << "destroying p2...\n";}if (p1) p1->bar();// Foo instance is destroyed when p1 goes out of scopereturn 0;
}//
// reference: http://www.cplusplus.com/reference/memory/unique_ptr/unique_ptr/
int test_unique_ptr2()
{std::default_delete<int> d;std::unique_ptr<int> u1;std::unique_ptr<int> u2(nullptr);std::unique_ptr<int> u3(new int);std::unique_ptr<int> u4(new int, d);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;
}//
// reference: http://eli.thegreenplace.net/2012/06/20/c11-using-unique_ptr-with-standard-library-containers
struct Foo_0 {Foo_0() { std::cerr << "Foo_0 [" << this << "] constructed\n"; }virtual ~Foo_0() { std::cerr << "Foo_0 [" << this << "] destructed\n"; }
};void sink(std::unique_ptr<Foo_0> p) {std::cerr << "Sink owns Foo_0 [" << p.get() << "]\n";
}std::unique_ptr<Foo_0> source() {std::cerr << "Creating Foo_0 in source\n";return std::unique_ptr<Foo_0>(new Foo_0);
}int test_unique_ptr3()
{std::cerr << "Calling source\n";std::unique_ptr<Foo_0> pmain = source(); // Can also be written as// auto pmain = source();std::cerr << "Now pmain owns Foo [" << pmain.get() << "]\n";std::cerr << "Passing it to sink\n";// sink(pmain); // ERROR! can't copy unique_ptrsink(move(pmain)); // OK: can move it!std::cerr << "Main done\n";return 0;
}// reference: http://www.codeguru.com/cpp/article.php/c17775/The-Smart-Pointer-That-Makes-Your-C-Applications-Safer--stduniqueptr.htm
void func(int*)
{}int test_unique_ptr4()
{// default constructionstd::unique_ptr<int> up; //creates an empty object// initialize with an argumentstd::unique_ptr<int> uptr(new int(3));double *pd = new double;std::unique_ptr<double> uptr2(pd);// overloaded * and ->*uptr2 = 23.5;std::unique_ptr<std::string> ups(new std::string("hello"));int len = ups->size();// Reset() releases the owned resource and optionally acquires a new resource:uptr2.reset(new double); //delete pd and acquire a new pointeruptr2.reset(); //delete the pointer acquired by the previous reset() call// If you need to access the owned pointer directly use get()func(uptr.get());// Unique_ptr has implicit conversion to bool.// This lets you use unique_ptr object in Boolean expressions such as this:if (ups) //implicit conversion to boolstd::cout << *ups << std::endl;elsestd::cout << "an empty smart pointer" << std::endl;// Array Support: Unique_ptr can store arrays as well.// A unique_ptr that owns an array defines an overloaded operator [].// Obviously, the * and -> operators are not available.// Additionally, the default deleter calls delete[] instead of delete:std::unique_ptr<int[]> arrup(new int[5]);arrup[0] = 5;// std::cout << *arrup << std::endl; //error, operator * not defined // Compatibility with Containers and Algorithms// You can safely store unique_ptr in Standard Library containers and let algorithms manipulate sequences of unique_ptr objects.std::vector<std::unique_ptr<int>> vi;vi.push_back(std::unique_ptr<int>(new int(0))); // populate vectorvi.push_back(std::unique_ptr<int>(new int(3)));vi.push_back(std::unique_ptr<int>(new int(2)));std::sort(vi.begin(), vi.end()); // result: {0, 2, 3}return 0;
}//
template <typename T>
class Add {
public:T add_sub(T a, T b){return (a + b) * (a - b);}
};int test_unique_ptr5()
{std::unique_ptr<Add<int>> tt(new Add<int>());int a{ 10 }, b{ 5 };std::cout << tt->add_sub(a, b) << std::endl;return 0;
}//
int test_unique_ptr6()
{std::unique_ptr<int[]> tmp(new int[100]);std::for_each(tmp.get(), tmp.get() + 100, [](int& n) {n = 66; });std::cout << tmp[99] << std::endl;return 0;
}///
// reference: https://en.cppreference.com/w/cpp/memory/unique_ptr
struct B {virtual void bar() { std::cout << "B::bar\n"; }virtual ~B() = default;
};
struct D : B
{D() { std::cout << "D::D\n"; }~D() { std::cout << "D::~D\n"; }void bar() override { std::cout << "D::bar\n"; }
};// a function consuming a unique_ptr can take it by value or by rvalue reference
std::unique_ptr<D> pass_through(std::unique_ptr<D> p)
{p->bar();return p;
}void close_file(std::FILE* fp) { std::fclose(fp); }int test_unique_ptr7()
{std::cout << "unique ownership semantics demo\n";{auto p = std::make_unique<D>(); // p is a unique_ptr that owns a Dauto q = pass_through(std::move(p));assert(!p); // now p owns nothing and holds a null pointerq->bar(); // and q owns the D object} // ~D called herestd::cout << "Runtime polymorphism demo\n";{std::unique_ptr<B> p = std::make_unique<D>(); // p is a unique_ptr that owns a D// as a pointer to basep->bar(); // virtual dispatchstd::vector<std::unique_ptr<B>> v; // unique_ptr can be stored in a containerv.push_back(std::make_unique<D>());v.push_back(std::move(p));v.emplace_back(new D);for (auto& p : v) p->bar(); // virtual dispatch} // ~D called 3 timesstd::cout << "Custom deleter demo\n";std::ofstream("demo.txt") << 'x'; // prepare the file to read{std::unique_ptr<std::FILE, decltype(&close_file)> fp(std::fopen("demo.txt", "r"), &close_file);if (fp) // fopen could have failed; in which case fp holds a null pointerstd::cout << (char)std::fgetc(fp.get()) << '\n';} // fclose() called here, but only if FILE* is not a null pointer// (that is, if fopen succeeded)std::cout << "Custom lambda-expression deleter demo\n";{std::unique_ptr<D, std::function<void(D*)>> p(new D, [](D* ptr){std::cout << "destroying from a custom deleter...\n";delete ptr;}); // p owns Dp->bar();} // the lambda above is called and D is destroyedstd::cout << "Array form of unique_ptr demo\n";{std::unique_ptr<D[]> p{ new D[3] };} // calls ~D 3 timesreturn 0;
}/
// reference: http://www.cplusplus.com/reference/memory/unique_ptr/~unique_ptr/
int test_unique_ptr8()
{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]
}/
// reference: http://www.cplusplus.com/reference/memory/unique_ptr/get_deleter/
class state_deleter { // a deleter class with stateint count_;
public:state_deleter() : count_(0) {}template <class T>void operator()(T* p) {std::cout << "[deleted #" << ++count_ << "]\n";delete p;}
};int test_unique_ptr9()
{state_deleter del;std::unique_ptr<int> p; // uses default deleter// alpha and beta use independent copies of the deleter:std::unique_ptr<int, state_deleter> alpha(new int);std::unique_ptr<int, state_deleter> beta(new int, alpha.get_deleter());// gamma and delta share the deleter "del" (deleter type is a reference!):std::unique_ptr<int, state_deleter&> gamma(new int, del);std::unique_ptr<int, state_deleter&> delta(new int, gamma.get_deleter());std::cout << "resetting alpha..."; alpha.reset(new int);std::cout << "resetting beta..."; beta.reset(new int);std::cout << "resetting gamma..."; gamma.reset(new int);std::cout << "resetting delta..."; delta.reset(new int);std::cout << "calling gamma/delta deleter...";gamma.get_deleter()(new int);alpha.get_deleter() = state_deleter(); // a brand new deleter for alpha// additional deletions when unique_ptr objects reach out of scope// (in inverse order of declaration)return 0;
}//
// reference: http://www.cplusplus.com/reference/memory/unique_ptr/operator=/
int test_unique_ptr10()
{std::unique_ptr<int> foo;std::unique_ptr<int> bar;foo = std::unique_ptr<int>(new int(101)); // rvaluebar = std::move(foo); // using std::movestd::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;
}/
// reference: http://www.cplusplus.com/reference/memory/unique_ptr/release/
int test_unique_ptr11()
{std::unique_ptr<int> auto_pointer(new int);int * manual_pointer;*auto_pointer = 10;manual_pointer = auto_pointer.release();// (auto_pointer is now empty)std::cout << "manual_pointer points to " << *manual_pointer << '\n';delete manual_pointer;return 0;}///
// reference: http://www.cplusplus.com/reference/memory/unique_ptr/swap/
int test_unique_ptr12()
{std::unique_ptr<int> foo(new int(10)), foo2(new int(10));std::unique_ptr<int> bar(new int(20)), bar2(new int(20));foo.swap(bar);std::cout << "foo: " << *foo << '\n';std::cout << "bar: " << *bar << '\n';std::swap(foo2, bar2);std::cout << "foo2: " << *foo2 << '\n';std::cout << "bar2: " << *bar2 << '\n';return 0;
}void math_add(int* a)
{int b = ++(*a);delete a;fprintf(stdout, "add operation: %d\n", b);
}void math_subtract(int* a)
{int b = --(*a);delete a;fprintf(stdout, "subtraction operation: %d\n", b);
}int test_unique_ptr13()
{{std::unique_ptr<int, decltype(&math_add)> A(new int, &math_add);if (!A) {fprintf(stderr, "A is nullptr\n");return -1;}*A = 10;}{typedef std::unique_ptr<int, std::function<void(int*)>> Oper;Oper A(new int, math_add);*A = 10;Oper B(new int, math_subtract);*B = 10;}return 0;
}} // namespace unique_ptr_
GitHub:https://github.com/fengbingchun/Messy_Test
總結
以上是生活随笔為你收集整理的C++11中unique_ptr的使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++11中shared_ptr的使用
- 下一篇: C++11中weak_ptr的使用