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 实例之间的所有权转换。

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());
cout << *pInt;
}

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

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

int main()
{
// 创建一个unique_ptr实例
unique_ptr<int> pInt(new int());
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());
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 = ;
unique_ptr<int> ret = clone(p);
cout << *ret << endl;
}
使用举例
{
//创建一个指向int的空指针
std::unique_ptr<int> fPtr1;
std::unique_ptr<int> fPtr2(new int());
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: and pF value:
  move before fPtr1 address: fPtr3 address:00EFEC60
  move after fPtr1 address:00EFEC60 fPtr3 address:
  move before fPtr1 address:00EFEC60
  move after fPtr1 address:

unique_ptr使用场景

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

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

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

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

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

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

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

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

3、在容器中保存指针

int main()
{
vector<unique_ptr<int>> vec;
unique_ptr<int> p(new int());
vec.push_back(std::move(p)); // 使用移动语义
}

4、管理动态数组

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

int main()
{
unique_ptr<int[]> p(new int[] {, , , , });
p[] = ; // 重载了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[]);
}
//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[]); // 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();
} int main()
{
TestAutoDestroy();
TestOwner();
TestArrayOwner();
} 输出:
TestDestroy....................
Foo::Foo
Foo::~Foo
Foo::Foo
Foo::Foo
Foo::Foo
Foo::Foo
Foo::~Foo
Foo::~Foo
Foo::~Foo
Foo::~Foo
Foo::Foo
D operator()
Foo::~Foo
TestOwner....................
Foo::Foo
Foo::bar
f(const Foo&)
Foo::bar
destroying p2...
Foo::bar
Foo::~Foo
TestArrayOwner....................
Foo::Foo
Foo::Foo
Foo::Foo
Foo::Foo
Foo::bar
f(const Foo&)
Foo::bar
destroying p2...
Foo::bar
Foo::~Foo
Foo::~Foo
Foo::~Foo
Foo::~Foo
 

C++ 11 创建和使用 unique_ptr的更多相关文章

  1. Windows server2008/2012 安装oracle 11 创建实例HANG住在百分之2

    Windows server2008/2012 安装oracle 11.2.0.1的时候,可能会在创建数据库实例的时候卡在百分之2的地方. 这个时候可以 1.点击开始菜单,在“搜索程序和文件”中输入“ ...

  2. C++ 11 创建和使用共享 weak_ptr

    1.为什么需要weak_ptr? 在正式介绍weak_ptr之前,我们先来回忆一下shared_ptr的一些知识.我们知道shared_ptr是采用引用计数的智能指针,多个shared_ptr实例可以 ...

  3. C++11智能指针 share_ptr,unique_ptr,weak_ptr用法

    0x01  智能指针简介  所谓智能指针(smart pointer)就是智能/自动化的管理指针所指向的动态资源的释放.它是存储指向动态分配(堆)对象指针的类,用于生存期控制,能够确保自动正确的销毁动 ...

  4. 快乐学习 Ionic Framework+PhoneGap 手册1-1{创建APP项目}

    快乐学习 Ionic Framework+PhoneGap 手册1-1 * 前提必须安装 Node.js,安装PhoneGap,搭建Android开发环境,建议使用真机调试 {1.1}= 创建APP项 ...

  5. Django1.11创建

    Djiango 1.11.1 虚拟环境配置 创建虚拟环境 mkvirtualenv 虚拟环境名字 -p python3 安装django pip install django==1.11.11 卸载包 ...

  6. Windows Azure Virtual Network (11) 创建VNet-to-VNet的连接

    <Windows Azure Platform 系列文章目录> 我们知道,Azure Virtual Network可以 1.将对台Azure VM加入到同一个网段里,同时绑定内网IP地址 ...

  7. Oracle SQL Lesson (11) - 创建其他数据库对象(试图/序列/索引/同义词)

    schema(模式)一个用户下一组对象的集合,一般与用户名一致. 视图 CREATE [OR REPLACE] [FORCE|NOFORCE] VIEW view [(alias[, alias].. ...

  8. 2.11. 创建托管对象(Core Data 应用程序实践指南)

    到现在为止,还没有创建过托管对象,回顾了一下,只是创建了托管对象模型,持久化存储区,持久化存储协调器,托管对象上下文. 那么,现在就来创建托管对象吧. 使用NSEntityDescription 的 ...

  9. Linux0.11 创建进程的过程分析--fork函数的使用

    /* * linux/kernel/fork.c * * (C) 1991 Linus Torvalds */ /* 注意:signal.c和fork.c文件的编译选项内不能有vc变量优化选项/Og, ...

随机推荐

  1. docker下运行labview2010

    前言 本人笔记本用kali,因课程需要,要在Linux下运行Labview,找到了2010的iso,但只支持rehat系列的发行版,用rpm转化deb的方案不可行,尝试了在virtualbox下运行w ...

  2. selenium-java,selenium版本和火狐浏览器版本对应关系

    selenium3.5.0,firefox57,geckodriver-v0.19.1

  3. LVS DR模式搭建、keepalived+lvs

    1.LVS DR模式搭建 条件: 即三台机器,在同一内网. 编辑脚本文件:/usr/local/sbin/lvs_dr.sh #! /bin/bashecho 1 > /proc/sys/net ...

  4. form表单以及内嵌框架标签

    今关于今天所学习的东西又复杂又简单,上午学习了form表单,也是挺简单的:搭配table表格使用也是非常熟练. 下午讲了讲给网页内嵌框架标签以及在内嵌框架标签中添加其他的网页:还有在内嵌框架标签中添加 ...

  5. 前端基础----CSS语法、CSS四种引入方式、CSS选择器、CSS属性操作

    一.CSS语法 CSS 规则由两个主要的部分构成:选择器,以及一条或多条声明. 例如: h1 {color:red; font-size:14px;} 二.CSS四种引入方式 1,行内式 行内式是在标 ...

  6. docker 常见错误总结

    docker common error Non-existing image of running container drm() { docker rm $(docker ps -q -a); } ...

  7. mongodb分片balance

    查看balance状态 mongos> sh.getBalancerState()true   通过balance锁查看balance活动 如果state是2,表示balance锁已经被获取 m ...

  8. PythonStudy——字典 Dictionary

    # 容器(集合):存放多个值的变量# 单列容器(系统中的单列容器很多):list | tuple# 双列容器(map):只有dict,存放数据 成对出现,dict存放数据采用 key-value键值对 ...

  9. oracle删除当前用户以及当前用户所有表、索引等操作

    ORACLE删除当前用户下所有的表的方法 如果有删除用户的权限,则可以: drop user user_name cascade; 加了cascade就可以把用户连带的数据全部删掉.删除后再创建该用户 ...

  10. [蓝桥杯]ALGO-186.算法训练_P0501

    输入两个无符号整数x, y, 用位操作实现无符号整数的乘法运算.不用考虑整数的溢出. 输入: 输出: 题目描述 代码如下: #include <stdio.h> #include < ...