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


    定义于头文件 
    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& r, element_type* ptr ) noexcept;

    (8)

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

    (8)(C++20 起)

    shared_ptr( const shared_ptr& r ) noexcept;

    (9)

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

    (9)

    shared_ptr( shared_ptr&& r ) noexcept;

    (10)

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

    (10)

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

    (11)

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

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

    template< class Y, class Deleter >
    shared_ptr( std::unique_ptr&& 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::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::lock() 在该情况下构造空的 std::shared_ptr

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

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

    std::unique_ptr::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>(*this,
                                      const_cast*>(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 构造 std::shared_ptr 是合法的:

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

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

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

    参数

    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) 若抛异常,则构造函数无效果。

    调用示例

    1. #include <memory>
    2. #include <iostream>
    3. struct Foo
    4. {
    5. Foo()
    6. {
    7. std::cout << "Foo...\n";
    8. }
    9. ~Foo()
    10. {
    11. std::cout << "~Foo...\n";
    12. }
    13. };
    14. struct D
    15. {
    16. void operator()(Foo* p) const
    17. {
    18. std::cout << "Call delete from function object...\n";
    19. delete p;
    20. }
    21. };
    22. int main()
    23. {
    24. {
    25. std::cout << "constructor with no managed object\n";
    26. std::shared_ptr<Foo> sh1;
    27. }
    28. {
    29. std::cout << "constructor with object\n";
    30. std::shared_ptr<Foo> sh2(new Foo);
    31. std::shared_ptr<Foo> sh3(sh2);
    32. std::cout << sh2.use_count() << '\n';
    33. std::cout << sh3.use_count() << '\n';
    34. }
    35. {
    36. std::cout << "constructor with object and deleter\n";
    37. std::shared_ptr<Foo> sh4(new Foo, D());
    38. std::shared_ptr<Foo> sh5(new Foo, [](Foo * p)
    39. {
    40. std::cout << "Call delete from lambda...\n";
    41. delete p;
    42. });
    43. }
    44. }

     输出

     

  • 相关阅读:
    hadoop报错:HADOOP_HOME and hadoop.home.dir are unset. 解决方法
    Python入门-变量定义与切片&Python引入包和引入模块
    基于JAVA+SpringBoot的学生成长管理评价系统
    罗克韦尔AB PLC 通过KEPServer实现与西门子1200PLC的以太网通信
    Ubuntu18.04 安装完成后的开发配置
    Python的高级用法:偏函数
    HarmonyOS应用开发-ArkTS基础知识
    TiDB整体架构详解TiDB核心特性
    解决Node服务启动失败的问题
    详解MySQL information_schema数据库常用的表信息以及各表对应的字段信息;以及如何登录mysql和创建视图
  • 原文地址:https://blog.csdn.net/qq_40788199/article/details/126695619