码迷,mamicode.com
首页 > 编程语言 > 详细

C++11智能指针:unique_ptr

时间:2019-04-19 12:12:26      阅读:188      评论:0      收藏:0      [点我收藏+]

标签:namespace   不为   默认   原因   eve   没有   托管   art   table   

unique_ptr

1.概念

unique_ptr形如其名,与所指对象的内存紧密地绑定,不能与其他的unique_ptr类型的指针对象共享所指向对象的内存。

在cplusplus.com中,unique_ptr声明如下:

// non-specialized
template <class T, class D = default_delete<T>> class unique_ptr;
// array specialization 
template <class T, class D> class unique_ptr<T[],D>;

是一个模版类,T指得是指向内存的类型,D指得是deleter类型,默认为default_deleter。

请看如下例子:

int main(int argc, char* argv[]) {
    std::unique_ptr<int> u1(new int(1));
    std::cout << "u1 value : " << *u1 << '\n' << " addredd : " << u1.get() << std::endl;
    std::unique_ptr<int> u2 = u1;        //编译出错
    return 0;
}

编译结果如下:
技术图片

从编译log来看,use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]’,具体原因是unique_ptr不允许与其他对象共享所指向对象的内存,已经删除了拷贝构造函数,无法进行拷贝操作。

将上述代码改为如下形式编译成功:

int main(int argc, char* argv[]) {
    std::unique_ptr<int> u1(new int(1));
    std::cout << "u1 value : " << *u1 << '\n' << " addredd : " << u1.get() << std::endl;
    std::unique_ptr<int> u2 = move(u1);
    std::cout << "u2 value : " << *u2 << '\n' << " addredd : " << u2.get() << std::endl;
    return 0;
}

执行结果如下:
技术图片

从结果可以看出u2所指向的值为1,u2所指向的地址与u1所指向的地址相同(通过get成员函数可以获取所指向的地址)。这是由于使用了move操作,u1把内存的所有权释放,u2获取到内存的所有权,所以u2所指向的地址和u1所指向的地址相同。此时如果再次打印u1的值和所指向的内存地址,代码如下:

int main(int argc, char* argv[]) {
    std::unique_ptr<int> u1(new int(1));
    std::cout << "u1 value : " << *u1 << '\n' << " addredd : " << u1.get() << std::endl;
    std::unique_ptr<int> u2 = move(u1);
    std::cout << "u2 value : " << *u2 << '\n' << " addredd : " << u2.get() << std::endl;
    std::cout << "u1 value : " << *u1 << '\n' << " addredd : " << u1.get() << std::endl;
    return 0;
}

执行结果如下:
技术图片

出现Segmentation fault的原因是u1此时以及把所指内存的所有权转给u2,但还是打印了u1所指的值和地址。在此我就不进行core file分析了。

2.用法

1.构造函数

unique_ptr一共有8个构造函数,分别是:

描述 函数原型
1.默认构造函数 constexpr unique_ptr() noexcept;
2.初始化为空指针 constexpr unique_ptr (nullptr_t) noexcept : unique_ptr() {}
3.初始化为非空指针 explicit unique_ptr (pointer p) noexcept;
4.初始化为非空指针+左值deleter unique_ptr (pointer p,typename
5.初始化为非空指针+右值deleter unique_ptr (pointer p,typename remove_reference::type&& del) noexcept;
6.移动构造函数 unique_ptr (unique_ptr&& x) noexcept;
7.非默认deleter的移动构造 template <class U, class E> unique_ptr (unique_ptr<U,E>&& x) noexcept;
8.从auto_ptr类型移动(C++17中移除掉这种用法了) template unique_ptr (auto_ptr&& x) noexcept;
9.拷贝构造函数(已删除,不可使用 unique_ptr (const unique_ptr&) = delete;

下面我们分别测试一下每种构造函数的用法:

#include <memory>
#include <iostream>

using namespace std;

class smart_point_class {
public:
    smart_point_class(int i) : _i(i) {
        cout << "Default construct..." << _i << endl;
    }
    ~smart_point_class() {
        cout << "Destroct..." << _i << endl;
    }
    inline int get_value() {
        return _i;
    }

private:
    smart_point_class(const smart_point_class&) = delete;
    smart_point_class(smart_point_class&&) = delete;

private:
    int _i;
};

class deleter {
public:
    deleter() = default;
    deleter(const deleter& other) {
        cout << "Copy deleter." << endl;
    }
    deleter(deleter&& other) {
        cout << "Move deleter." << endl;
    }
    deleter& operator=(const deleter&) {
        cout << "Copy assign deleter." << endl;
        return *this;
    }
    deleter& operator=(deleter&&) {
        cout << "Move assign deleter." << endl;
        return *this;
    }

    void operator() (smart_point_class* spc) {
        cout << "Start of deleter function : " << spc->get_value() << endl;
        delete spc;
        spc = nullptr;
        cout << "End of deleter function." << endl;
    }
};

void test_construct_fun() {
    cout << "C++ unique_ptr test function :" << endl;
    cout << "----------------------------------------------------" << endl;
    cout << "u1" << endl;
    unique_ptr<smart_point_class> u1;
    cout << "u1 : " << (u1? "not null" : "null") << '\n';
    cout << "----------------------------------------------------" << endl;
    cout << "u2" << endl;
    unique_ptr<smart_point_class> u2(nullptr);
    cout << "u2 : " << (u2? "not null" : "null") << '\n';
    cout << "----------------------------------------------------" << endl;
    cout << "u3" << endl;
    unique_ptr<smart_point_class> u3(new smart_point_class(3));
    cout << "u3 : " << (u3? "not null" : "null") << '\n';
    cout << "----------------------------------------------------" << endl;
    cout << "u4" << endl;
    deleter d;
    unique_ptr<smart_point_class, decltype(d)> u4(new smart_point_class(4), d);
    cout << "u4 : " << (u4? "not null" : "null") << '\n';
    cout << "----------------------------------------------------" << endl;
    cout << "u5" << endl;
    unique_ptr<smart_point_class, deleter> u5(new smart_point_class(5), deleter());
    cout << "u5 : " << (u5? "not null" : "null") << '\n';
    cout << "----------------------------------------------------" << endl;
    cout << "u6" << endl;
    unique_ptr<smart_point_class> u6 = move(u3);
    cout << "u6 : " << (u6? "not null" : "null") << '\n';
    cout << "----------------------------------------------------" << endl;
    cout << "u7" << endl;
    unique_ptr<smart_point_class, deleter> u7 = move(u5);
    cout << "u7 : " << (u7? "not null" : "null") << '\n';
    cout << "----------------------------------------------------" << endl;
    cout << "u8" << endl;
    unique_ptr<smart_point_class> u8 (auto_ptr<smart_point_class>(new smart_point_class(8)));
    cout << "u8 : " << (u8? "not null" : "null") << '\n';
    cout << "----------------------------------------------------" << endl;

    cout << "Test finish..." << endl;
    cout << "u1 : " << (u1? "not null" : "null") << '\n';
    cout << "u2 : " << (u2? "not null" : "null") << '\n';
    cout << "u3 : " << (u3? "not null" : "null") << '\n';
    cout << "u4 : " << (u4? "not null" : "null") << '\n';
    cout << "u5 : " << (u5? "not null" : "null") << '\n';
    cout << "u6 : " << (u6? "not null" : "null") << '\n';
    cout << "u7 : " << (u7? "not null" : "null") << '\n';
    cout << "u8 : " << (u8? "not null" : "null") << '\n';
    cout << "----------------------------------------------------" << endl;
}

int main(int argc, char* argv[]) {
    test_construct_fun();
    return 0;
}

编译结果如下:
技术图片

会发现有个警告,是因为在C++11中不再建议使用auto_ptr了,但既然构造函数中有这个用法,我们就在这里测试一下(在C++17中已经移除掉这种用法了)。

这段代码执行结果如下:
技术图片

现在我们来分析一下执行结果:

  1. u1使用了默认构造函数,所指向内容为空;
  2. u2使用了初始化为空指针的构造函数,所指向内容为空;
  3. u3使用了初始化为非空指针的构造函数,所指向内容不为空;
  4. u4使用了初始化为非空指针+左值deleter的构造函数,会线构造一个deleter,然后拷贝这个deleter,所指向内容不为空;
  5. u5使用了初始化为非空指针+右值deleter的构造函数,会线构造一个deleter,然后移动这个deleter,所指向内容不为空;
  6. u6使用了移动构造函数,u3将所有权转到了u6,u6不为空,但u3变为空;
  7. u7使用了自定义deleter类型的移动构造函数,u5将所有权转到了u7,u7不为空,但u5变为空;
  8. u8使用了从auto_ptr类型移动的构造函数,不为空,但C++17已经移除了这种用法,不再建议使用;
  9. 在函数体内所有语句执行完毕之后,释放资源。其中使用默认deleter的会调用smart_pointer_class的析构函数,不使用默认deleter的会调用deleter函数/仿函数。

2.析构函数

没有需要特殊说明的。

3.赋值重载操作

unique_ptr一共有3个赋值重载操作,分别是:

描述 函数原型
1.移动赋值 unique_ptr& operator= (unique_ptr&& x) noexcept;
2.赋值为空 unique_ptr& operator= (nullptr_t) noexcept;
3.赋值非默认deleter的unique_ptr template <class U, class E> unique_ptr& operator= (unique_ptr<U,E>&& x) noexcept;
4.拷贝赋值函数(已删除,不可使用) unique_ptr& operator= (const unique_ptr&) = delete;

下面我们分别测试一下每种赋值函数的用法:

#include <memory>
#include <iostream>

using namespace std;

class smart_point_class {
public:
    smart_point_class(int i) : _i(i) {
        cout << "Default construct..." << _i << endl;
    }
    ~smart_point_class() {
        cout << "Destroct..." << _i << endl;
    }
    inline int get_value() {
        return _i;
    }

private:
    smart_point_class(const smart_point_class&) = delete;
    smart_point_class(smart_point_class&&) = delete;

private:
    int _i;
};

class deleter {
public:
    deleter() = default;
    deleter(const deleter& other) {
        cout << "Copy deleter." << endl;
    }
    deleter(deleter&& other) {
        cout << "Move deleter." << endl;
    }
    deleter& operator=(const deleter&) {
        cout << "Copy assign deleter." << endl;
        return *this;
    }
    deleter& operator=(deleter&&) {
        cout << "Move assign deleter." << endl;
        return *this;
    }

    void operator() (smart_point_class* spc) {
        cout << "Start of deleter function : " << spc->get_value() << endl;
        delete spc;
        spc = nullptr;
        cout << "End of deleter function." << endl;
    }
};

void test_assign_fun() {
    cout << "C++ unique_ptr assign test :" << endl;
    cout << "u_base" << endl;
    unique_ptr<smart_point_class> u_base(new smart_point_class(1));
    cout << "u_base value : " << u_base->get_value() << '\n' << " addredd : " << u_base.get() << endl;
    cout << "----------------------------------------------------" << endl;
    cout << "u1" << endl;
    unique_ptr<smart_point_class> u1;
    u1 = move(u_base);
    cout << "u1 value : " << u1->get_value() << '\n' << " addredd : " << u1.get() << endl;
    cout << "----------------------------------------------------" << endl;
    unique_ptr<smart_point_class> u2(new smart_point_class(2));
    u2 = nullptr;
    cout << "----------------------------------------------------" << endl;
    unique_ptr<smart_point_class, deleter> u4;
    u4 = unique_ptr<smart_point_class, deleter>(new smart_point_class(3));
    cout << "----------------------------------------------------" << endl;
    cout << "Test finish..." << endl;
}

int main(int argc, char* argv[]) {
    test_assign_fun();
    return 0;
}

上述代码执行结果如下:
技术图片

现在我们来分析一下执行结果:

  1. u1从u_base处获取所有权;
  2. u2使用了初始化为非空指针的构造函数,所指向内容不为空,然后赋值为空,u2 = nullptr,等同于u2.reset(),(在后面会具体说到reset的使用方法),在此进行析构操作;
  3. u3首先指向空,然后用右值赋值,调用了unique_ptr的移动赋值函数;
  4. 所有语句执行完成之后,释放资源。u3会调用deleter函数/仿函数,u2在之前已经释放,最后释放u1,用默认的deleter。

4.get()

get函数会返回存储的指针。如果由unique_ptr不为空,则存储的指针指向由unique_ptr管理的对象,否则指向nullptr。需要注意的是调用这个函数并不会释放指针的所有权(它仍然负责删除管理的数据)。因此,此函数的返回值不用于构建新的指针。如果要获取存储的指针并释放所有权,调用unique_ptr::release()。

get函数使用方法如下:

int main(int argc, char* argv[]) {
    int *i = new int(1);
    cout << "i : " << i << endl;
    unique_ptr<int> u(i);
    cout << "u value : " << *u << '\n' << "u address : " << u.get() << endl;
    return 0;
}

该段代码执行结果如下:
技术图片

从图中看出可以正确打印出地址。

5.get_deleter()

get_deleter函数会返回deleter。deleter是一个可调用的对象。使用成员类型指针的单个参数对该对象的函数调用将删除托管对象,并在unique_ptr本身被销毁、分配了一个新值或在非空时重置时自动调用。unique_ptr模板使用的默认deleter类型是default_delete,一个无状态类。

get_deleter使用例子:

int main(int agrc, char* argv[]) {
    // smart_point_class和deleter在之前的代码中有定义
    unique_ptr<smart_point_class, deleter> u(new smart_point_class(1), deleter());
    smart_point_class* p(new smart_point_class(2));

    cout << "----------------------------------------------------" << endl;
    u.get_deleter()(p);
    cout << "----------------------------------------------------" << endl;
}

上述代码执行结果如下:
技术图片

在两条分割线之间的语句u.get_deleter()(p),就等同于调用deleter函数/仿函数。

6.bool操作重载

没有需要特殊说明的。

7.release()

该操作会释放指针的所有权,并通过返回值返回。此调用不会销毁托管对象,但unique_ptr对象将从删除对象的责任中释放。某些其他实体必须负责删除对象。

请看如下代码:

int main(int agrc, char* argv[]) {
    unique_ptr<smart_point_class, deleter> u(new smart_point_class(1), deleter());
    u.release();
}

上述代码执行结果如下:
技术图片

会发现并没有对u存储的指针进行销毁的操作,这样非常容易造成内存泄漏。代码应该改为如下形式:

int main(int agrc, char* argv[]) {
    unique_ptr<smart_point_class, deleter> u(new smart_point_class(1), deleter());
    smart_point_class *p = u.release();
    delete p;
}

上述代码执行结果如下:
技术图片

8.reset()

销毁当前由unique_ptr管理的对象(如果有的话),并获取p的所有权。如果p是一个空指针(例如默认初始化的指针),unique_ptr将变为空,在调用之后不管理任何对象。若要释放存储指针的所有权而不破坏它,请使用成员函数release。

release使用例子:

int main(int agrc, char* argv[]) {
    unique_ptr<smart_point_class> u1(new smart_point_class(1));
    cout << "u1 value : " << u1->get_value() << '\n' << " addredd : " << u1.get() << endl;
    smart_point_class *p (new smart_point_class(2));
    cout << "----------------------------------------------------" << endl;
    u1.reset(p);
    cout << "u1 value : " << u1->get_value() << '\n' << " addredd : " << u1.get() << endl;
    cout << "----------------------------------------------------" << endl;
    u1.reset();
    cout << "----------------------------------------------------" << endl;
}

上述代码执行结果如下:
技术图片

  1. 在执行完u1.reset(p)语句之后,会首先销毁资源,然后u1所管理内存的value由1变为2,对应的地址也有了改变;
  2. 在执行完u1.reset()语句之后,会销毁u1现在所管理内存的资源。

9.swap()

交换两个unique_ptr所管理的对象。

swap使用例子:

int main(int agrc, char* argv[]) {
    unique_ptr<smart_point_class> u1(new smart_point_class(1));
    unique_ptr<smart_point_class> u2(new smart_point_class(2));
    cout << "----------------------------------------------------" << endl;
    cout << "Before swap :" << endl;
    cout << "u1 value : " << u1->get_value() << '\n' << " addredd : " << u1.get() << endl;
    cout << "u2 value : " << u2->get_value() << '\n' << " addredd : " << u2.get() << endl;
    cout << "----------------------------------------------------" << endl;
    u1.swap(u2);
    cout << "After swap :" << endl;
    cout << "u1 value : " << u1->get_value() << '\n' << " addredd : " << u1.get() << endl;
    cout << "u2 value : " << u2->get_value() << '\n' << " addredd : " << u2.get() << endl;
    cout << "----------------------------------------------------" << endl;
}

上述代码执行结果如下:
技术图片

可以看到u1和u2所管理的对象做了交换操作。

10. * ->操作

与普通指针操作相同,在此不再赘述。

C++11智能指针:unique_ptr

标签:namespace   不为   默认   原因   eve   没有   托管   art   table   

原文地址:https://www.cnblogs.com/gofran/p/10733145.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!