C++11智能指针:unique_ptr

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. * ->操作

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

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

时间: 2024-08-03 12:53:52

C++11智能指针:unique_ptr的相关文章

c++11 智能指针 unique_ptr、shared_ptr与weak_ptr

c++11 智能指针 unique_ptr.shared_ptr与weak_ptr C++11中有unique_ptr.shared_ptr与weak_ptr等智能指针(smart pointer),定义在<memory>中. 可以对动态资源进行管理,保证任何情况下,已构造的对象最终会销毁,即它的析构函数最终会被调用. unique_ptr unique_ptr持有对对象的独有权,同一时刻只能有一个unique_ptr指向给定对象(通过禁止拷贝语义.只有移动语义来实现). unique_ptr

C++11 智能指针unique_ptr使用 -- 以排序二叉树为例

用智能指针可以简化内存管理.以树为例,如果用普通指针,通常是在插入新节点时用new,在析构函数中调用delete:但有了unique_ptr类型的智能指针,就不需要在析构函数中delete了,因为当unique_ptr类型的指针P生命结束时(比如对于局部变量,程序执行到局部变量的作用域范围之外),P会自动delete它拥有的资源(指针指向的空间).对于shared_ptr,情况更加复杂一些,shared_ptr会维护一个use count,即有多少个指针共享这一资源,当use count为0时,

C++智能指针 unique_ptr

C++智能指针 unique_ptr unique_ptr 独占所指向的对象, 同一时刻只能有一个 unique_ptr 指向给定对象(通过禁止拷贝语义, 只有移动语义来实现), 定义于 memory (非memory.h)中, 命名空间为 std. 标准库早期版本中定义了 auto_ptr, 它具有 unique_ptr 的部分特征, 但不是全部, 例如, 不能在容器中保存 auto_ptr, 也不能从函数中返回 auto_ptr. 基于这些原因, 应该尽量使用 unique_ptr, 而不是

详解C++11智能指针

前言 C++里面的四个智能指针: auto_ptr, unique_ptr,shared_ptr, weak_ptr 其中后三个是C++11支持,并且第一个已经被C++11弃用. C++11智能指针介绍 智能指针主要用于管理在堆上分配的内存,它将普通的指针封装为一个栈对象.当栈对象的生存周期结束后,会在析构函数中释放掉申请的内存,从而防止内存泄漏.C++ 11中最常用的智能指针类型为shared_ptr,它采用引用计数的方法,记录当前内存资源被多少个智能指针引用.该引用计数的内存在堆上分配.当新

实战c++中的智能指针unique_ptr系列-- 使用std::unique_ptr代替new operator(错误:‘unique_ptr’ is not a member of ‘std’)

写了很多篇关于vector的博客,其实vector很便捷,也很简单.但是很多易错的问题都是vector中的元素为智能指针所引起的.所以决定开始写一写关于智能指针的故事,尤其是unique_ptr指针的故事. 这是个开始,就让我们使用std::unique_ptr代替new operator吧! 还是用程序说话: #include<iostream> int main() { while (true) int *x = new int; } 看下任务管理器中的内存: 此时使用智能指针unique

C++11智能指针

C成也指针,败也指针.确实,指针给程序员提供了很多便利和灵活性,但是不当的指针使用也会造成很多问题. Java和C#避免了指针(虽然C#中也能使用指针,但是估计很少有人这样做),其垃圾回收机制,给程序员减轻很多管理内存的负担. 为了带来指针更好的使用体验,C++中引入了智能指针的概念,其实质就是将指针的一些操作封装成类,程序员通过使用熟悉的指针运算符(-> 和 *)访问封装指针,该指针类通过运算符重载返回封装的原始指针. C++ 智能指针思路类似于C#等语言中创建对象的过程:创建对象后让系统负责

c++11 智能指针

如果在程序中使用new从堆(自由存储区)分配内存,等到不需要时, 应使用delete将其释放.c++引入了智能指针auto_ptr, 以帮助自动完成这个过程. c++11摒弃了auto_ptr,并新增了三种智能指针:unique_ptr, shared_ptr, weak_ptr. 一. auto_ptr, unique_ptr , shared_ptr 头文件:  #include <memory> 用法:  auto_ptr<double>  A(new double); un

C/C++ C++11智能指针

在使用基本指针类型时,因为要手动释放指针指向的内存,常常容易造成内存泄漏,特别是异常分支很多的情况下.而智能指针类型就是将基本指针类型封装成模板类,以便更好地管理内存. 智能指针都包含一个explicit构造函数,因此基本指针类型不能隐式转换成智能指针,需要显式调用. shared_ptr<double> sp_d; double *p_d = new double; sp_d = p_d; // 错误,隐式转换. sp_d = shared_ptr<double>(p_d); /

实战c++中的智能指针unique_ptr系列-- unique_ptr的operator=、operator bool、reset、swap、get等介绍

既然打算把unique_ptr写一个系列,就要详尽一点,有些内容也许在vector的时候有个涉及,但是现在还是再谈论一番. 我们要把unique_ptr看做一个类,废话了,它当然是一个类.所以这个类肯定也重载了赋值运算符,即operator=.现在就开始看看operator=在unique_ptr中的使用: 官方描述如下: move assignment (1) unique_ptr& operator= (unique_ptr&& x) noexcept; assign null

一次测试测试(C++11)智能指针引用的崩溃结论

项目中用到了智能指针,但是要在智能指针指向的类中获取此类的shared_ptr指针传给其引用的类,结果出现了问题, 测试代码如下: (包括错误解释) 1 //测试 shared_ptr weak_ptr map<string,shared_ptr> 2 #include <stdio.h> //pinrtf ... 3 #include <string> //string ... 4 #include <map> 5 #include <memory&