C++ 11 的 unique_ptr

来源:互联网 发布:ug nx软件 编辑:程序博客网 时间:2024/06/05 04:44

unique_ptr 不共享它的指针。它无法复制到其他 unique_ptr,无法通过值传递到函数,也无法用于需要副本的任何标准模板库 (STL) 算法。只能移动unique_ptr这意味着,内存资源所有权将转移到另一 unique_ptr,并且原始 unique_ptr 不再拥有此资源。我们建议你将对象限制为由一个所有者所有,因为多个所有权会使程序逻辑变得复杂。因此,当需要智能指针用于纯 C++ 对象时,可使用 unique_ptr,而当构造 unique_ptr 时,可使用make_unique Helper 函数。

std::unique_ptr实现了独享所有权的语义。一个非空的std::unique_ptr总是拥有它所指向的资源。转移一个std::unique_ptr将会把所有权也从源指针转移给目标指针(源指针被置空)。拷贝一个std::unique_ptr将不被允许,因为如果你拷贝一个std::unique_ptr,那么拷贝结束后,这两个std::unique_ptr都会指向相同的资源,它们都认为自己拥有这块资源(所以都会企图释放)。因此std::unique_ptr是一个仅能移动(move_only)的类型。当指针析构时,它所拥有的资源也被销毁。默认情况下,资源的析构是伴随着调用std::unique_ptr内部的原始指针的delete操作的。

下图演示了两个 unique_ptr 实例之间的所有权转换。

转移 unique_ptr 的所有权

1、如何创建unique_ptr

unique_ptr不像shared_ptr一样拥有标准库函数make_shared来创建一个shared_ptr实例。要想创建一个unique_ptr,我们需要将一个new 操作符返回的指针传递给unique_ptr的构造函数。

示例:

int main(){    // 创建一个unique_ptr实例    unique_ptr<int> pInt(new int(5));    cout << *pInt;}

2、无法进行复制构造和赋值操作

unique_ptr没有copy构造函数,不支持普通的拷贝和赋值操作。


int main() {    // 创建一个unique_ptr实例    unique_ptr<int> pInt(new int(5));    unique_ptr<int> pInt2(pInt);    // 报错    unique_ptr<int> pInt3 = pInt;   // 报错}

3、可以进行移动构造和移动赋值操作

unique_ptr虽然没有支持普通的拷贝和赋值操作,但却提供了一种移动机制来将指针的所有权从一个unique_ptr转移给另一个unique_ptr。如果需要转移所有权,可以使用std::move()函数。

示例:


int main() {    unique_ptr<int> pInt(new int(5));    unique_ptr<int> pInt2 = std::move(pInt);    // 转移所有权    //cout << *pInt << endl; // 出错,pInt为空    cout << *pInt2 << endl;    unique_ptr<int> pInt3(std::move(pInt2));}

4、可以返回unique_ptr

unique_ptr不支持拷贝操作,但却有一个例外:可以从函数中返回一个unique_ptr。

示例:


unique_ptr<int> clone(int p){    unique_ptr<int> pInt(new int(p));    return pInt;    // 返回unique_ptr}int main() {    int p = 5;    unique_ptr<int> ret = clone(p);    cout << *ret << endl;}


使用举例:    {        //创建一个指向int的空指针        std::unique_ptr<int> fPtr1;        std::unique_ptr<int> fPtr2(new int(4));        auto fPtr3 = std::make_unique<int>();                //fPtr2释放指向对象的所有权,并且被置为nullptr        std::cout << "fPtr2 release before:" << fPtr2.get() << std::endl;        int *pF = fPtr2.release();        std::cout << "fPtr2 release before:" << fPtr2.get() << " and pF value:" << *pF << std::endl;                //所有权转移,转移后fPtr3变为空指针        std::cout << "move before fPtr1 address:" << fPtr1.get() << " fPtr3 address:" << fPtr3.get() << std::endl;        fPtr1 = std::move(fPtr3);        std::cout << "move after  fPtr1 address:" << fPtr1.get() << " fPtr3 address:" << fPtr3.get() << std::endl;        std::cout << "move before fPtr1 address:" << fPtr1.get() << std::endl;        fPtr1.reset();        std::cout << "move after  fPtr1 address:" << fPtr1.get() << std::endl;    }输出:  fPtr2 release before:00EFB120  fPtr2 release before:00000000 and pF value:4  move before fPtr1 address:00000000 fPtr3 address:00EFEC60  move after fPtr1 address:00EFEC60 fPtr3 address:00000000  move before fPtr1 address:00EFEC60  move after fPtr1 address:00000000

 

unique_ptr使用场景

1、为动态申请的资源提供异常安全保证

我们先来看看下面这一段代码:

void Func(){    int *p = new int(5);    // ...(可能会抛出异常)    delete p;}

这是我们传统的写法:当我们动态申请内存后,有可能我们接下来的代码由于抛出异常或者提前退出(if语句)而没有执行delete操作。

解决的方法是使用unique_ptr来管理动态内存,只要unique_ptr指针创建成功,其析构函数都会被调用。确保动态资源被释放。

void Func(){    unique_ptr<int> p(new int(5));    // ...(可能会抛出异常)}

2、返回函数内动态申请资源的所有权


unique_ptr<int> Func(int p){    unique_ptr<int> pInt(new int(p));    return pInt;    // 返回unique_ptr}int main() {    int p = 5;    unique_ptr<int> ret = Func(p);    cout << *ret << endl;    // 函数结束后,自动释放资源}

3、在容器中保存指针

int main() {    vector<unique_ptr<int>> vec;    unique_ptr<int> p(new int(5));    vec.push_back(std::move(p));    // 使用移动语义    //vec.push_back(p.release()); //release, p 放弃控制权 }

3.1   vector<unique_ptr<T> > 的拷贝

但是对于vector内的unique point来说,就不能简单的使用普通迭代器了:需要使用对迭代器就行std::make_move_iterator操作:

看下英文描述最可靠:
A move_iterator is an iterator adaptor that adapts an iterator (it) so that dereferencing it produces rvalue references (as if std::move was applied), while all other operations behave the same。

简单的说就是将vector的iterator变成make_move_iterator, 这样源vector中的所有元素就深拷贝到目标vector中。不会导致源vector的指针问题。


#include<iostream>
#include<vector>
#include <memory>
using namespace std;
void display_vector(vector<unique_ptr<int>> &vec);
int main()
{
    vector<unique_ptr<int>> vec;
    unique_ptr<int> s1(new int(1));
    unique_ptr<int> s2(new int(2));
    unique_ptr<int> s3(new int(3));
    unique_ptr<int> s4(new int(4));
    vec.push_back(std::move(s1));
    vec.push_back(std::move(s2));
    vec.push_back(std::move(s3));
    vec.push_back(std::move(s4));


    unique_ptr<int> s5(new int(5));
    vector<unique_ptr<int>> des_vec;
    des_vec.push_back(std::move(s5));
    des_vec.insert(des_vec.end(), std::make_move_iterator(vec.begin()), std::make_move_iterator(vec.end()));
    display_vector(des_vec);    
    cout << "now, des_vec size: " << des_vec.size() << endl;
    cout << "now, vec size: " << vec.size() << endl;

    return 0;
}

void display_vector(vector<unique_ptr<int>> &vec)
{
    for (auto it = vec.begin(); it != vec.end(); it++)
    {
        cout << **it << endl;
    }

//输出结果:
5
1
2
3
4
now, des_vec size: 5
now, vec size: 4

4、管理动态数组

标准库提供了一个可以管理动态数组的unique_ptr版本。

int main() {    unique_ptr<int[]> p(new int[5] {1, 2, 3, 4, 5});    p[0] = 0;   // 重载了operator[]}

5、作为auto_ptr的替代品

创建与释放举例

#include <iostream>#include <memory>#include <stdlib.h>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";}struct D{    void operator()(Foo* foo)    {        std::cout << "D operator()" << std::endl;        delete foo;    }};void TestAutoDestroy(){    //1. 普通的new对象.    std::cout << "TestDestroy...................." << std::endl;    {        std::unique_ptr<Foo> p1(new Foo);    }    //2. 普通的new[]对象.    {        std::unique_ptr<Foo[]> p2(new Foo[4]);    }    //3. 自定义的deleter.    {        std::unique_ptr<Foo, D> p3(new Foo);    }}void TestOwner(){    std::cout << "TestOwner...................." << std::endl;    //1. new object.    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        p2->bar();        std::cout << "destroying p2...\n";    }    p1->bar();}void TestArrayOwner(){    std::cout << "TestArrayOwner...................." << std::endl;    //1. new[] object.    std::unique_ptr<Foo[]> p1(new Foo[4]);  // p1 owns Foo    if (p1) p1[0].bar();    {        std::unique_ptr<Foo[]> p2(std::move(p1));  // now p2 owns Foo        f(p2[0]);        p1 = std::move(p2);  // ownership returns to p1        p2[0].bar();        std::cout << "destroying p2...\n";    }    p1[0].bar();}int main(){    TestAutoDestroy();    TestOwner();    TestArrayOwner();}输出:
TestDestroy....................Foo::FooFoo::~FooFoo::FooFoo::FooFoo::FooFoo::FooFoo::~FooFoo::~FooFoo::~FooFoo::~FooFoo::FooD operator()Foo::~FooTestOwner....................Foo::FooFoo::barf(const Foo&)Foo::bardestroying p2...Foo::barFoo::~FooTestArrayOwner....................Foo::FooFoo::FooFoo::FooFoo::FooFoo::barf(const Foo&)Foo::bardestroying p2...Foo::barFoo::~FooFoo::~FooFoo::~FooFoo::~Foo
 
转载地址:http://www.cnblogs.com/DswCnblog/p/5628195.html
0 0
原创粉丝点击