unique_ptr 的使用

std::unique_ptr是c++11起引入的智能指针,为什么必须要在c++11起才有该特性,主要还是c++11增加了move语义,否则无法对对象的所有权进行传递。

unique_ptr 介绍

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

示例程序

  • 示例程序1
#include <memory>
#include <iostream>
#include <utility> class Foo{ public:
Foo() = default;
Foo(int a):_a(a) {}
~Foo() {}
int get_a(){
return _a;
}
void set_a(int a) {
_a = a;
}
private:
int _a; }; std::unique_ptr<Foo> change_a(std::unique_ptr<Foo> f)
{
f->set_a(10);
return f;
} int main()
{
// std::make_unique是c++14才有
std::unique_ptr<Foo> pf = std::make_unique<Foo>(10);
// unique_ptr的复制构造函数和拷贝构造函数是删除了的,这样保证了对象独占,如果不是独占,那么跟shared_ptr
// 就是一样的功能了。
// std::unique_ptr<Foo> pf1 = pf; // compile error // 按值传参,会有拷贝问题,同上
// auto p = change_a(pf); //compile error auto p = change_a(std::move(pf));
std::cout << "get_a = " << p->get_a() << std::endl;
if(!pf)
{
std::cout << "pf is nullptr" << std::endl;
}
//owner transfer from function
std::unique_ptr<Foo> pf2 = std::make_unique<Foo>(11);
std::unique_ptr<Foo> p2 = change_a(std::move(pf2));
std::cout << "get_a = " << p2->get_a() << std::endl;
if(!pf2)
{
std::cout << "pf2 is nullptr" << std::endl;
} //使用reset
pf2.reset(new Foo(12));
std::cout << "pf2 is not null: " << pf2->get_a() << std::endl; //release获取原始指针
Foo* ff = pf2.release();
if(!pf2)
{
std::cout << "pf2 is nullptr" << std::endl;
}
std::cout << "ff is not null: " << ff->get_a() << std::endl; return 0;
}

输出结果

get_a = 10
pf is nullptr
get_a = 10
pf2 is nullptr
pf2 is not null: 12
pf2 is nullptr
ff is not null: 12
  • 示例程序2
#include <cassert>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <locale>
#include <memory>
#include <stdexcept> // helper class for runtime polymorphism demo below
struct B
{
virtual ~B() = default; virtual void bar() { std::cout << "B::bar\n"; }
}; 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;
} // helper function for the custom deleter demo below
void close_file(std::FILE* fp)
{
std::fclose(fp);
} // unique_ptr-based linked list demo
struct List
{
struct Node
{
int data;
std::unique_ptr<Node> next;
}; std::unique_ptr<Node> head; ~List()
{
// destroy list nodes sequentially in a loop, the default destructor
// would have invoked its `next`'s destructor recursively, which would
// cause stack overflow for sufficiently large lists.
while (head)
{
auto next = std::move(head->next);
head = std::move(next);
}
} void push(int data)
{
head = std::unique_ptr<Node>(new Node{data, std::move(head)});
}
}; int main()
{
std::cout << "1) Unique ownership semantics demo\n";
{
// Create a (uniquely owned) resource
std::unique_ptr<D> p = std::make_unique<D>(); // Transfer ownership to `pass_through`,
// which in turn transfers ownership back through the return value
std::unique_ptr<D> q = pass_through(std::move(p)); // p is now in a moved-from 'empty' state, equal to nullptr
assert(!p);
} std::cout << "\n" "2) Runtime polymorphism demo\n";
{
// Create a derived resource and point to it via base type
std::unique_ptr<B> p = std::make_unique<D>(); // Dynamic dispatch works as expected
p->bar();
} std::cout << "\n" "3) Custom deleter demo\n";
std::ofstream("demo.txt") << 'x'; // prepare the file to read
{
using unique_file_t = std::unique_ptr<std::FILE, decltype(&close_file)>;
unique_file_t fp(std::fopen("demo.txt", "r"), &close_file);
if (fp)
std::cout << char(std::fgetc(fp.get())) << '\n';
} // `close_file()` called here (if `fp` is not null) std::cout << "\n" "4) Custom lambda-expression deleter and exception safety demo\n";
try
{
std::unique_ptr<D, void(*)(D*)> p(new D, [](D* ptr)
{
std::cout << "destroying from a custom deleter...\n";
delete ptr;
}); throw std::runtime_error(""); // `p` would leak here if it were a plain pointer
}
catch (const std::exception&) { std::cout << "Caught exception\n"; } std::cout << "\n" "5) Array form of unique_ptr demo\n";
{
std::unique_ptr<D[]> p(new D[3]);
} // `D::~D()` is called 3 times std::cout << "\n" "6) Linked list demo\n";
{
List wall;
const int enough{1'000'000};
for (int beer = 0; beer != enough; ++beer)
wall.push(beer); std::cout.imbue(std::locale("en_US.UTF-8"));
std::cout << enough << " bottles of beer on the wall...\n";
} // destroys all the beers
}

输出

1) Unique ownership semantics demo
D::D
D::bar
D::~D 2) Runtime polymorphism demo
D::D
D::bar
D::~D 3) Custom deleter demo
x 4) Custom lambda-expression deleter and exception safety demo
D::D
destroying from a custom deleter...
D::~D
Caught exception 5) Array form of unique_ptr demo
D::D
D::D
D::D
D::~D
D::~D
D::~D 6) Linked list demo
1,000,000 bottles of beer on the wall...

c++中unique_ptr 的使用和理解的更多相关文章

  1. C#中对IDisposable接口的理解

    http://blog.sina.com.cn/s/blog_8abeac5b01019u19.html C#中对IDisposable接口的理解 本人最近接触一个项目,在这个项目里面看到很多类实现了 ...

  2. js中的回调函数的理解和使用方法

    js中的回调函数的理解和使用方法 一. 回调函数的作用 js代码会至上而下一条线执行下去,但是有时候我们需要等到一个操作结束之后再进行下一个操作,这时候就需要用到回调函数. 二. 回调函数的解释 因为 ...

  3. [BS-18] 对OC中不可变类的理解

    对OC中不可变类的理解 OC中存在很多不可变的类(如NSString,NSAttributedString,NSArray,NSDictionary,NSSet等),用它们创建的对象存在于堆内存中,但 ...

  4. oracle中 connect by prior 递归算法 -- 理解

    oracle中 connect by prior 递归算法 -- 理解 http://blog.163.com/xxciof/blog/static/7978132720095193113752/  ...

  5. MVC架构中的Repository模式 个人理解

    关于MVC架构中的Repository模式   个人理解:Repository是一个独立的层,介于领域层与数据映射层(数据访问层)之间.它的存在让领域层感觉不到数据访问层的存在,它提供一个类似集合的接 ...

  6. 关于MySQL中的自联结的通俗理解

    关于MySQL中的自联结的通俗理解 前言:最近在通过SQL必知必会这本书学习MySQL的基本使用,在学习中也或多或少遇到了点问题,我也正好分享给大家,我的这篇博客用到的所有表格的代码都是来自SQL必知 ...

  7. 关于Autosar中的NM模块的理解

    本篇文章主要介绍AutoSar中关于NM模块的理解. 阅读本篇文章希望达到的目的: 1. NM(网络管理)是用来做什么的: 2. AutoSar中网络管理的原理: 3.项目实例介绍 1. NM(网络管 ...

  8. Spring中Bean及@Bean的理解

    Spring中Bean及@Bean的理解 Bean在Spring和SpringMVC中无所不在,将这个概念内化很重要,下面分享一下我的想法: 一.Bean是啥 1.Java面向对象,对象有方法和属性, ...

  9. Oracle中rownum和rowid的理解

    rownum,rowid都叫伪列. 但是,rownum是逻辑上的编号,且其值总是从1开始,每行的rounum不是固定的.而rowid是“物理”编号.若数据库文件没有移动,则每行的 rowid一般是固定 ...

  10. 【转】C#中对IDisposable接口的理解

    IDisposable接口定义:定义一种释放分配的资源的方法. .NET 平台在内存管理方面提供了GC(Garbage Collection),负责自动释放托管资源和内存回收的工作,但它无法对非托管资 ...

随机推荐

  1. drf重写authenticate方法实现多条件登录(源码分析)

    drf重写authenticate方法实现多条件登录(源码分析) 1. 思路 JWT拓展的登录视图中, 在接受到用户名和密码时, 调用的也是Django的认证系统中提供的authenticate()来 ...

  2. #Python pandas库,读取模块,代码笔记

    日常数据清洗中,利用python清洗的第一步就是读取对应文件,今天一起复盘一下数据读取环节的常规操作. csv和xlsx格式读取类似,所以用csv做案例 X-MIND图

  3. 通过空间占用和执行计划了解SQL Server的行存储索引

    1 索引介绍 索引是一种帮助查询语句能够快速定位到数据的一种技术.索引的存储方式有行存储索引.列存储索引和内存优化三种存储方式: 行存储索引,使用B+树结构,行存储指的是数据存储格式为堆.聚集索引和内 ...

  4. 2020-10-15:mysql的双1设置是什么?

    福哥答案2020-10-15:#福大大架构师每日一题# [答案来自知乎:](https://www.zhihu.com/question/425704691) 其实就是innodb_flush_log ...

  5. 2022-07-13:给你一个整数数组 arr ,你一开始在数组的第一个元素处(下标为 0)。 每一步,你可以从下标 i 跳到下标 i + 1 、i - 1 或者 j : i + 1 需满足:i +

    2022-07-13:给你一个整数数组 arr ,你一开始在数组的第一个元素处(下标为 0). 每一步,你可以从下标 i 跳到下标 i + 1 .i - 1 或者 j : i + 1 需满足:i + ...

  6. 2021-01-05:mysql的自增id的实现逻辑是什么样子的?

    福哥答案2021-01-05:答案来自这个链接:[ 每日一面 - mysql 的自增 id 的实现逻辑是什么样子的?](https://zhanghaoxin.blog.csdn.net/articl ...

  7. 2022-04-21:给定一个包含 [0,n) 中不重复整数的黑名单 blacklist, 写一个函数从 [0, n) 中返回一个不在 blacklist 中的随机整数, 对它进行优化使其尽量少调用系

    2022-04-21:给定一个包含 [0,n) 中不重复整数的黑名单 blacklist, 写一个函数从 [0, n) 中返回一个不在 blacklist 中的随机整数, 对它进行优化使其尽量少调用系 ...

  8. 「GPT虚拟直播」实战篇|GPT接入虚拟人实现直播间弹幕回复

    摘要 ChatGPT和元宇宙都是当前数字化领域中非常热门的技术和应用.结合两者的优势和特点,可以探索出更多的应用场景和商业模式.例如,在元宇宙中使用ChatGPT进行自然语言交互,可以为用户提供更加智 ...

  9. Springboot——参数校验

    springboot参数校验注解 在controller层需要对前端传来的参数进行校验 校验简单数据类型 使用springboot自带的validation工具可以从后端对前端传来的数据进行校验 使用 ...

  10. 有关ODOO的ORM操作

    1.查询操作 sale_id = self.env['sale.order'].search([]) env将对象实例化,search进行搜索,可以根据需求添加搜索条件 search_count([] ...