c++11 智能指针 (std::shared_ptr)(一)

2023-11-01

定义于头文件 <memory>
template< class T > class shared_ptr;     (C++11 起) 

std::shared_ptr 是通过指针保持对象共享所有权的智能指针。多个 shared_ptr 对象可占有同一对象。下列情况之一出现时销毁对象并解分配其内存:

  • 最后剩下的占有对象的 shared_ptr 被销毁;
  • 最后剩下的占有对象的 shared_ptr 被通过 operator= 或 reset() 赋值为另一指针。

delete 表达式或在构造期间提供给 shared_ptr 的定制删除器销毁对象。

shared_ptr 能在存储指向一个对象的指针时共享另一对象的所有权。此特性能用于在占有其所属对象时,指向成员对象。存储的指针为 get() 、解引用及比较运算符所访问。被管理指针是在 use_count 抵达零时传递给删除器者。

shared_ptr 亦可不占有对象,该情况下称它为空 (empty) (空 shared_ptr 可拥有非空存储指针,若以别名使用构造函数创建它)。

shared_ptr 的所有特化满足可复制构造 (CopyConstructible) 、可复制赋值 (CopyAssignable) 和可小于比较 (LessThanComparable) 的要求并可按语境转换为 bool

多个线程能在 shared_ptr 的不同实例上调用所有成员函数(包含复制构造函数与复制赋值)而不附加同步,即使这些实例是副本,且共享同一对象的所有权。若多个执行线程访问同一 shared_ptr 而不同步,且任一线程使用 shared_ptr 的非 const 成员函数,则将出现数据竞争;原子函数的 shared_ptr 特化能用于避免数据竞争。

构造函数

std::shared_ptr<T>::shared_ptr

constexpr shared_ptr() noexcept;

(1)

constexpr shared_ptr( std::nullptr_t ) noexcept;

(2)

template< class Y >
explicit shared_ptr( Y* ptr );

(3)

template< class Y, class Deleter >
shared_ptr( Y* ptr, Deleter d );

(4)

template< class Deleter >
shared_ptr( std::nullptr_t ptr, Deleter d );

(5)

template< class Y, class Deleter, class Alloc >
shared_ptr( Y* ptr, Deleter d, Alloc alloc );

(6)

template< class Deleter, class Alloc >
shared_ptr( std::nullptr_t ptr, Deleter d, Alloc alloc );

(7)

template< class Y >
shared_ptr( const shared_ptr<Y>& r, element_type* ptr ) noexcept;

(8)

template< class Y >
shared_ptr( shared_ptr<Y>&& r, element_type* ptr ) noexcept;

(8) (C++20 起)

shared_ptr( const shared_ptr& r ) noexcept;

(9)

template< class Y >
shared_ptr( const shared_ptr<Y>& r ) noexcept;

(9)

shared_ptr( shared_ptr&& r ) noexcept;

(10)

template< class Y >
shared_ptr( shared_ptr<Y>&& r ) noexcept;

(10)

template< class Y >
explicit shared_ptr( const std::weak_ptr<Y>& r );

(11)

template< class Y >
shared_ptr( std::auto_ptr<Y>&& r );

(12) (C++17 中移除)

template< class Y, class Deleter >
shared_ptr( std::unique_ptr<Y,Deleter>&& r );

(13)

从指代要管理的对象的各种指针类型构造新的 shared_ptr

为以下描述的目的,若指针类型 Y* 可转换为指针类型 T* ,或 U[N] 是类型数组而 TU cv [] (其中 cv 是某个 cv 限定符集合),则说 Y* 兼容 T*

(C++17 起)

1-2) 构造无被管理对象的 shared_ptr ,即空 shared_ptr

3-7) 构造 shared_ptr ,管理 ptr 所指向的对象。

Y* 必须可转换为 T*

(C++17 前)

T 是数组类型 U[N] ,则若 Y(*)[N] 不可转换为 T* 则这些构造函数不参与重载决议。若 T 是数组类型 U[] ,则若 Y(*)[] 不可转换为 T* 则这些构造函数不参与重载决议。否则,若 Y* 不可转换为 T* 则这些构造函数不参与重载决议。

(C++17 起)

另外:

3) 以 delete 表达式 delete ptr 为删除器,若 T 不是数组类型,以 delete[] ptr 为删除器,若 T 是数组类型 (C++17 起)。 Y 必须是完整类型。 delete 表达式必须为良式,拥有良好定义行为且不抛异常。若 delete 表达式不为良式,则此构造函数不参与重载决议。 (C++17 起)

4-5) 以指定的删除器 d 为删除器。表达式 d(ptr) 表达式必须为良式,拥有良好定义行为且不抛异常。 d 的构造和从 d 构造存储的删除器必须不抛异常。

Deleter 必须可复制构造 (CopyConstructible) 。

(C++17 前)

若表达式 d(ptr) 非良式,或若 std::is_move_constructible<D>::value 为 false ,则这些构造函数不额外参与重载决议。

(C++17 起)

6-7) 同 (4-5) ,但额外地用 alloc 的副本分配内部使用的数据。 Alloc 必须是分配器 (Allocator) 。

8) 别名使用构造函数:构造 shared_ptr ,与 r 的初始值共享所有权信息,但保有无关且不管理的指针 ptr 。若此 shared_ptr 是离开作用域的组中的最后者,则它将调用最初 r 所管理对象的析构函数。然而,在此 shared_ptr 上调用 get() 将始终返回 ptr 的副本。程序员负责确保只要此 shared_ptr 存在,此 ptr 就保持合法,例如在典型使用情况中,其中 ptrr 所管理对象的成员,或是 r.get() 的别名(例如向下转型)。对于接收右值的第二重载,调用后 r 为空且 r.get() == nullptr 。 (C++20 起)

9) 构造 shared_ptr ,共享 r 所管理对象的所有权。若 r 不管理对象,则 *this 亦不管理对象。若 Y* 不可隐式转换为 (C++17 前)兼容 (C++17 起) T* ,则模板重载不参与重载决议。

10) 从 r 移动构造 shared_ptr 。构造后, *this 含 r 先前状态的副本,而 r 为空且其存储的指针为空。若 Y* 不可隐式转换为 (C++17 前)兼容 (C++17 起) T* ,则模板重载不参与重载决议。

11) 构造 shared_ptr ,共享 r 所管理对象的所有权。 Y* 必须可隐式转换为 T* 。 (C++17 前)此重载仅若 Y* 兼容 T* 才参与重载决议。 (C++17 起)注意可为相同目的用 r.lock() :区别是若参数为空则此构造函数抛异常,而 std::weak_ptr<T>::lock() 在该情况下构造空的 std::shared_ptr

12) 构造 shared_ptr ,存储并占有 r 先前占有的对象。 Y* 必须可转换为 T* 。构造后, r 为空。

13) 构造 shared_ptr ,管理当前为 r 所管理的对象。存储与 r 关联的删除器以在未来删除被管理对象。调用后 r 不管理对象。

std::unique_ptr<Y, Deleter>::pointer兼容 T* 则此重载不参与重载决议。若 r.get() 是空指针,则此重载等价于默认构造函数 (1) 。 (C++17 起)

Deleter 是引用类型,则等价于 shared_ptr(r.release(), std::ref(r.get_deleter()) 。否则,等价于 shared_ptr(r.release(), r.get_deleter()) 。

T 不是数组类型时,重载 (3) 、 (4) 及 (6) 以 ptr 启用 shared_from_this ,而重载 (13) 以 r.release() 所返回的指针启用 shared_from_this

注意

T 不是数组类型是,重载 (3) 、 (4) 及 (6) 以 ptr 启用 shared_from_this ,而重载 (13) 以 r.release() 所返回的指针启用 shared_from_this 。

构造函数以 U* 类型指针 ptr 启用 shared_from_this ,表示它确定 U 是否拥有作为 std::enable_shared_from_this 特化的无歧义且可访问 (C++17 起)基类,而若如此,则构造函数求值该语句:

if (ptr != nullptr && ptr->weak_this.expired())
  ptr->weak_this = std::shared_ptr<std::remove_cv_t<U>>(*this,
                                  const_cast<std::remove_cv_t<U>*>(ptr));

其中 weak_this 是 std::shared_from_this 的隐藏 mutable std::weak_ptr 成员。对 weak_this 成员的赋值不是原子的,且与任何到同一对象的潜在并发访问冲突。这确保将来对 shared_from_this() 调用,将与此裸指针构造函数所创建的 shared_ptr 共享所有权。

上述解释代码中,测试 ptr->weak_this.expired() 是为确保若 weak_this 指示已有占有者则重赋值它。从 C++17 起要求此测试。

裸指针重载认定被指向对象的所有权。从而,用裸指针重载为已经为 shared_ptr 所管理对象构造 shared_ptr ,例如以 shared_ptr(ptr.get()) 很可能导致未定义行为,即使对象有导出自 std::enable_shared_from_this 的类型。

因为默认构造函数是 constexpr ,静态 shared_ptrs 作为静态非局部初始化的一部分初始化,在任何动态初始化开始前。这使得在任何静态对象的构造函数中使用 shared_ptr 是安全的。

C++11 和 C++14 中,从 std::unique_ptr<T[]> 构造 std::shared_ptr<T> 是合法的:

std::unique_ptr<int[]> arr(new int[1]);
std::shared_ptr<int> ptr(std::move(arr));

因为 shared_ptrunique_ptr 获得其删除器( std::default_delete<T[]> 对象),故能正确解分配数组。

C++ 中这不再受允许。应当使用替代的数组形式 std::shared_ptr<T[]> 。

参数

ptr - 指向要管理的对象的指针
d - 用于销毁对象的删除器
alloc - 用于分配内部使用的数据的分配器
r - 要共享所有权或从它获得所有权的另一智能指针

异常

3) 若无法获得要求的附加内存则为 std::bad_alloc 。可能因其他错误抛出实现定义的异常。若异常出现,则调用 delete ptr,若 T 非数组类型,否则调用 delete[] ptr (C++17 起) 。

4-7) 若无法获得要求的附加内存则为 std::bad_alloc 。可能因其他错误抛出实现定义的异常。若异常出现,则调用 d(ptr) 。

11) 若 r.expired() == true 则为 std::bad_weak_ptr 。若异常出现,则此构造函数无效果。

12) 若无法获得要求的附加内存则为 std::bad_alloc 。可能因其他错误抛出实现定义的异常。若异常出现,则此构造函数无效果。

13) 若抛异常,则构造函数无效果。

调用示例

#include <memory>
#include <iostream>

struct Foo
{
    Foo()
    {
        std::cout << "Foo...\n";
    }
    ~Foo()
    {
        std::cout << "~Foo...\n";
    }
};

struct D
{
    void operator()(Foo* p) const
    {
        std::cout << "Call delete from function object...\n";
        delete p;
    }
};

int main()
{
    {
        std::cout << "constructor with no managed object\n";
        std::shared_ptr<Foo> sh1;
    }

    {
        std::cout << "constructor with object\n";
        std::shared_ptr<Foo> sh2(new Foo);
        std::shared_ptr<Foo> sh3(sh2);
        std::cout << sh2.use_count() << '\n';
        std::cout << sh3.use_count() << '\n';
    }

    {
        std::cout << "constructor with object and deleter\n";
        std::shared_ptr<Foo> sh4(new Foo, D());
        std::shared_ptr<Foo> sh5(new Foo, [](Foo * p)
        {
            std::cout << "Call delete from lambda...\n";
            delete p;
        });
    }
}

 输出

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

c++11 智能指针 (std::shared_ptr)(一) 的相关文章

  • 查找哪些页面不再与写入时复制共享

    假设我在 Linux 中有一个进程 我从中fork 另一个相同的过程 后forking 因为原始进程将开始写入内存 Linux写时复制机制将为进程提供与分叉进程使用的不同的唯一物理内存页 在执行的某个时刻 我如何知道原始进程的哪些页面已被写
  • asp.net 文本框文本模式数字,仅允许数字

    我只是想知道 ASP NET 中是否有一种方法只允许文本框中的数字textmode number 当我使用这个时
  • 迭代变量并查找特定类型实例的技术

    我想迭代进程中内存中的变量 通过插件动态加载 并查找特定类型的实例 以前我可以找到特定类型 或内存中的所有类型 我可以创建类型的实例 我可以获取作为不同类型的字段包含的实例 但我无论如何都不知道只是 搜索 特定类型的实例 一种方法是使用 W
  • 为什么我不能用 `= delete;` 声明纯虚函数?

    Intro 纯虚函数使用通用语法声明 virtual f 0 然而 自 c 11 以来 有一种方法可以显式地传达non existence 特殊 成员函数的 Mystruct delete eg default constructor Q
  • 以编程方式检查页面是否需要基于 web.config 设置进行身份验证

    我想知道是否有一种方法可以检查页面是否需要基于 web config 设置进行身份验证 基本上如果有这样的节点
  • 为什么要序列化对象需要 Serialized 属性

    根据我的理解 SerializedAttribute 不提供编译时检查 因为它都是在运行时完成的 如果是这样 那么为什么需要将类标记为可序列化呢 难道序列化器不能尝试序列化一个对象然后失败吗 这不就是它现在所做的吗 当某些东西被标记时 它会
  • 从 MVC 迁移到 ASP.NET Core 3.1 中的端点路由时,具有角色的 AuthorizeAttribute 不起作用

    我正在尝试将我的项目从 UseMVC asp net core 2 2 兼容样式 升级到 UseEndpoint Routing 并且我的所有请求都被重定向到我的验证失败页面 它与声明有关 如果我删除 Authorize Roles Adm
  • 对齐 GridView 中的行值

    我需要在 asp net 3 5 中右对齐 gridview 列中的值 我怎样才能做到这一点
  • 暂停下载线程

    我正在用 C 编写一个非常简单的批量下载程序 该程序读取要下载的 URL 的 txt 文件 我已经设置了一个全局线程和委托来更新 GUI 按下 开始 按钮即可创建并启动该线程 我想要做的是有一个 暂停 按钮 使我能够暂停下载 直到点击 恢复
  • 访问者和模板化虚拟方法

    在一个典型的实现中Visitor模式 该类必须考虑基类的所有变体 后代 在许多情况下 访问者中的相同方法内容应用于不同的方法 在这种情况下 模板化的虚拟方法是理想的选择 但目前这是不允许的 那么 模板化方法可以用来解析父类的虚方法吗 鉴于
  • 基于xsd模式生成xml(使用.NET)

    我想根据我的 xsd 架构 cap xsd 生成 xml 文件 我找到了这篇文章并按照说明进行操作 使用 XSD 文件生成 XML 文件 https stackoverflow com questions 6530424 generatin
  • 无法将类型“System.IO.Stream”隐式转换为“Java.IO.InputStream”

    我提到了一些类似的问题 但没有一个涉及IO 当我使用时 我在java中使用了相同的代码Eclipse 那次就成功了 但现在我尝试在中使用这段代码Mono for Android C 它不起作用 我正在尝试运行此代码来创建一个InputStr
  • 通过 NHibernate 进行查询,无需 N+1 - 包含示例

    我有一个 N 1 问题 我不知道如何解决它 可以在这个问题的底部找到完全可重复的样本 因此 如果您愿意 请创建数据库 设置 NUnit 测试和所有附带的类 并尝试在本地消除 N 1 这是我遇到的真实问题的匿名版本 众所周知 这段代码对于帮助
  • 将构建日期放入“关于”框中

    我有一个带有 关于 框的 C WinForms 应用程序 我使用以下方法将版本号放入 关于 框中 FileVersionInfo GetVersionInfo Assembly GetExecutingAssembly Location F
  • 在 C 中使用 GNU automake 中的解析器

    我是 GNU autotools 的新手 在我的项目中使用了 lex 和 yacc 解析器 将它们作为 makefile am 中的源代码会产生以下错误 配置 in AC CHECK PROGS YACC bison yacc none i
  • 如何挤出平面 2D 网格并赋予其深度

    我有一组共面 连接的三角形 即二维网格 现在我需要将其在 z 轴上挤出几个单位 网格由一组顶点定义 渲染器通过与三角形数组匹配来理解这些顶点 网格示例 顶点 0 0 0 10 0 0 10 10 0 0 10 0 所以这里我们有一个二维正方
  • 获取 2 个数据集 c# 中的差异

    我正在编写一个简短的算法 它必须比较两个数据集 以便可以进一步处理两者之间的差异 我尝试通过合并这两个数据集并将结果更改放入新的数据集来实现此目标 我的方法如下所示 private DataSet ComputateDiff DataSet
  • 当前的 x86 架构是否支持非临时加载(来自“正常”内存)?

    我知道有关此主题的多个问题 但是 我没有看到任何明确的答案或任何基准测量 因此 我创建了一个处理两个整数数组的简单程序 第一个数组a非常大 64 MB 第二个数组b很小 无法放入 L1 缓存 程序迭代a并将其元素添加到相应的元素中b在模块化
  • 双精度类型二维多维数组的 pinvoke 编组作为 c# 和 c++ 之间的输入和输出

    我有以下我正在尝试解决的双物质类型的 2d 多维数组的 c 和 c pinvoke 编组 我已经查看了以下热门内容以获得我目前拥有的内容使用双精度数组进行 P Invoke 在 C 和 C 之间编组数据 https stackoverflo
  • Googletest:如何异步运行测试?

    考虑到一个包含数千个测试的大型项目 其中一些测试需要几分钟才能完成 如果按顺序执行 整套测试需要一个多小时才能完成 通过并行执行测试可以减少测试时间 据我所知 没有办法直接从 googletest mock 做到这一点 就像 async选项

随机推荐

  • 【vue】keep-alive清除缓存最简单暴力的方法

    项目场景 场景一 使用vue开发移动端 有ABC三个页面 点击A跳转到B 点B跳转到C 点C返回B 点B返回A 场景二 场景一实现之后 会出现这样一个问题 先从A跳转到B B页面会被缓存下来 当再从D跳转到B时 B页面并不会更新 解决方案
  • 我的硬盘出现I/O错误,

    用pctools去修复吧 在天空软件园里能下到 用Windows 9x启动盘启动 插入含有Pctools9 0的光盘 运行PCT90目录下的de exe 先进入 Options 菜单 选 Configuration 配置 命令 按下 空格
  • 【j2ee系列】springmvc中使用quartz,项目启动就执行某些任务

    quartz有几种执行任务的方式 至于几种我也不知道 至少有两种吧 一种是org springframework scheduling quartz CronTriggerBean方式 配置指定的时间执行一次任务 如
  • 项目经理如何分配任务

    http www 51testing com html 62 n 245962 html 记得自己第一次当PM 那是接手的项目 原来的PM 在项目需求分析做完之后 去接手另一个重要的项目去了 当时我和另外两个小组长 自然就成了接手PM的人选
  • DC-3靶机渗透实战

    0x00 信息收集 使用arp scan查看局域网内所有主机IP 定位到目标主机IP 使用nmap扫描端口服务 发现只有80端口开放 服务应该是Joomla 使用dirsearch进行路径爆破 发现administrator后台路径 访问h
  • myBatis大于1000的in查询解决办法

    之前公司一位同事写的方法
  • 笔记本安装centos之后,合上盖还正常运行设置

    修改如下配置 让其生效即可 具体操作 vim etc systemd logind conf 将上图所示 HandleLidSwitch suspend 修改为lock 并将起前面 号去掉 重启配置让其生效systemctl restart
  • IMX6学习记录(12)-通过系统接口点亮LED

    上面是我的微信和QQ群 欢迎新朋友的加入 1 硬件 硬件上 led连接的IO是GPIO5 PIN8 高电平熄灭 低电平点亮 2 export引脚 GPIO5 PIN8的在gpio上的位置是5 32 8 136 cd sys class gp
  • 大搜索时代!SEO如何挖掘关键词?方法都在这里!-搜嗖工具箱

    做SEO关键词挖掘是关键 好的关键词可以帮助您的网站在搜索引擎中获得更好的排名 要问都有哪些挖掘关键词的方法 那就太多了 下边就列举几个我常用的方式吧 方法一实用工具挖掘关键词 我们知道有很多关键词在线挖掘工具可以帮助我们快速实现关键词挖掘
  • 这款开源神器,让你能在 iPad 上随心所欲写代码!

    注意 这篇文章就是在劝你买iPad Pro 手动狗头 最近 苹果推出了新的iPad Pro 号称生产力工具 然而对程序员来说 不能写代码 就难以称得上生产力 虽然也有一些优秀的写代码App可供程序员使用 但本着能不花钱就不花钱的原则 还是可
  • epoll实现原理

    epoll的使用 epoll只有以下的三个系统函数调用 epoll create epoll ctl和epoll wait int epoll create int size 其中参数 1 size指明了生成描述符的最大范围 该函数返回一个
  • Java_.jar .war .ear区别

    jar 全称 java archive 包含 class properties文件 是文件封装的最小单元 部署文件 application client xml 级别 小 war 全称 web archive 包含 Servlet JSP页
  • es搜索引擎

    ES的优势及使用场景 ES的功能及使用简介 简介 Elaticsearch简称为ES 是一个开源的可扩展的分布式的全文检索引擎 它可以近乎实时的存储 检索数 据 本身扩展性很好 可扩展到上百台服务器 处理PB级别的数据 ES使用Java开发
  • switch删除用户显示无法连接服务器,switch无法连接互联网怎么办 NS无法联机联网详细解决办法...

    switch最经常碰到的问题是就是联网的问题 很多玩家会遇到无法联网以及联机对战的情况 那么遇到这样的问题该怎么办呢 下面就来为大家分享一下解决办法 可能的原因 网络NAT类型不是创建与其他用户的对等连接 Peer to Peer P2P
  • 软件测试相关试题知识点

    软件测试相关试题 1 下面不属于软件测试步骤的是 A 集成测试 B 回归测试 C 确认测试 D 单元测试 解析 B 回归测试是指修改旧代码后重新进行测试以确认修改没有引入新的错误或导致其他代码产生错误 因此不是软件测试的步骤 2 Junit
  • MAC下jupyter的安装及使用

    一 安装 在终端输入以下命令 conda install jupyter notebook 运行结果如下 Collecting package metadata done Solving environment done Package P
  • date时间加减(linux,aix)

    需求是这样的 有一个在日志中的时间 格式化为 Y m d H M S格式的 那现在想比较这个时间与当前时间差值是否大于一天 这个应该怎么做呢 设计到日期的减法运算 首先先man date来看一下用法吧 DATE 1 User Command
  • java排序之快排

    这篇文章来谈谈快排 最近有一种感觉 只要有规律可循的代码 分解成为两部分以后效率就会提高很多 代码思想如下 这个代码写的是快排 快排最主要的思维就是寻找一个分界值 大的放在一边 小的放在一边 然后递归分别处理大的和小的 这里需要注意的是我们
  • 拓扑 排序

    拓扑排序的适用范围 有向无环图 DAG 实际上拓扑排序不止可以用来求拓扑序 在 D A G DAG DAG 中 我们即可以用 t o
  • c++11 智能指针 (std::shared_ptr)(一)

    定义于头文件