C++11智能指針之std::unique_ptr


C++11智能指針之std::unique_ptr

 

uniqut_ptr是一種對資源具有排他性擁有權的智能指針,即一個對象資源只能同時被一個unique_ptr指向。

一、初始化方式

  1. 通過new雲算法或者普通指針

       unique_ptr<Investment> up(new Investment());

       或者

       Investment *pInv = new Investment();

unique_ptr<Investment> up1(pInv);

  1. 通過make_unique

auto pInv = make_unique<Investment>();

  1. 通過move()函數

    unique_ptr<Investment> up1 = std::move(up);

注意:unique_ptr不能被復制或者拷貝,下面的代碼將出錯:

       unique_ptr<Investment> up(new Investment()); //ok

       unique_ptr<Investment> up1(up);              //error, can not be copy

       unique_ptr<Investment> up2 = up;            //error, can not be assigned

       但是,unique_ptr可以作為函數的返回值:

       unique_ptr<Investment> GetPtr();        //function getthe unique pointer

       unique_ptr<Investment> pInv = GetPtr(); // ok

        

二、自定義釋放器

 

用如下方式使用帶自定義資源釋放的unique_ptr

auto delete_Investment = [](Investment* pInv)

{

       pInv->getObjectType();

       delete pInv;

    };

    unique_ptr<Investment,decltype(delete_Investment)> pInvest(nullptr,delete_Investment);

        或者也可以使用函數指針

    void deleteInv(Investment* pInv) {}

    std::unique_ptr<Investment,void(*)(Investment*)>ptr(nullptr,deleteInv) ;

 

三、 unique_ptr 基本操作

 

unique_ptr<Investment> pInvestment;     // 創建一個空的智能指針

pInvestment.reset(new Investment());    //"綁定”動態對象 

Investment *pI = pInvestment.release(); //釋放所有權 

pI= nullptr;                         //顯式銷毀所指對象,同時智能指針變為空指針。

 

四、管理動態數組

由於unique_ptr有std::unique_ptr<T[]>的重載函數,所以它可以用來管理數組資源

unique_ptr<int[]> pArray(new int[3]{1,3,3});

 

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 Foo
    if (p1) p1->bar();
 
    {
        std::unique_ptr<Foo> p2(std::move(p1));  // now p2 owns Foo
        f(*p2);
 
        p1 = std::move(p2);  // ownership returns to p1
        std::cout << "destroying p2...\n";
    }
 
    if (p1) p1->bar();
 
    // Foo instance is destroyed when p1 goes out of scope
 
    return 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_ptr
    sink(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 construction
    std::unique_ptr<int> up; //creates an empty object
 
    // initialize with an argument
    std::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 pointer
    uptr2.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 bool
        std::cout << *ups << std::endl;
    else
        std::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 vector
    vi.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 D
        auto q = pass_through(std::move(p));
        assert(!p); // now p owns nothing and holds a null pointer
        q->bar();   // and q owns the D object
    } // ~D called here
 
    std::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 base
        p->bar(); // virtual dispatch
 
        std::vector<std::unique_ptr<B>> v;  // unique_ptr can be stored in a container
        v.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 times
 
    std::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 pointer
            std::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 D
        p->bar();
    } // the lambda above is called and D is destroyed
 
    std::cout << "Array form of unique_ptr demo\n";
    {
        std::unique_ptr<D[]> p{ new D[3] };
    } // calls ~D 3 times
 
    return 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 state
    int 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));  // rvalue
 
    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;
}
 
/////////////////////////////////////////////////////////////////
// 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_
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM