C++智能指针

01 shared_ptr 共享的智能指针

shared_ptr 使用引用计数每个 shared_ptr 的拷贝都指向相同的内存,在最后一个 shared_ptr 析构时它指向的内存才会被释放。

shared_ptr 在多线程中线程中的 shared_ptr 共享同一个对象的所有权,若在多个线程中访问同一个 shared_ptr 而不同步,且任意线程使用 shared_ptr 的非 const 成员函数,将会产生数据竞争,使用原子函数的 shared_ptr 特化能用于避免数据竞争(Delete in C++20)。

shared_ptr 在实现上有两个核心的成员:一个是指向资源对象的指针变量,另一个是指向引用计数的指针变量
shared_ptr 就是通过指向引用计数的指针变量实现实现多个 shared_ptr 对象共享一个 count 计数的目的,即引用计数。
原生的 shared_ptr 在多线程中的读写是不安全的,因为读写操作不具有原子性,所以多线程中使用 shared_ptr 一定要加锁。

  • 成员函数

    (constructor)           // 构造新的 shared_ptr
    (destructor)            // 如果没有更多的 shared_ptr 指向持有的对象则析构对象
    operator=               // 对 shared_ptr 进行赋值
    swap                    // 交换管理的对象,不会改变引用计数
    reset                   // 替换所管理的对象,原指针指向的对象引用计数减一
    get                     // 返回存储的指针
    operator*               // 解引用存储的指针
    operator->              // 同上
    use_count               // 返回 shared_ptr 所指对象的引用计数
    unique                  // 检查所管理的对象是否仅由当前的 shared_ptr 管理
    get_deleter             // 返回指定类型中的删除其(如果有的话)
    operator bool           // 检查是否有被管理的对象
    owner_before            // 提供基于拥有者的共享指针排序
    
  • 非成员函数

    std::make_shared             // 创建管理一个对象的共享指针
    std::allocate_shared         // 创建一个用于分配器分配的对象共享指针
    std::static_pointer_cast     // 应用对象的显式类型转换到被存储指针
    std::dynamic_pointer_cast
    std::const_pointer_cast
    std::reinterpret_pointer_cast
    std::swap(std::shared_ptr)  
                            // 特化的 std::swap 算法
    relational operators    // 在 shared_ptr 对象 lhs 和 rhs 之间或 shared_ptr 和 nullptr 之间执行适当的关系比较操作
    ostream operator<<      // 输入输出接口
    
  • 注意事项

    • 应该优先使用 std::make_shared 构造智能指针,因为它更高效。
    • 不能将一个原始指针直接赋值给一个智能指针。
    • 不要使用同一个原始指针初始化多个 shared_ptr:
      int* p1 = new int(10);
      std::shared_ptr<int> sp1(p1);       // ok
      std::shared_ptr<int> sp2(p1);       // error
      
    • 要避免循环引用,否则会造成内存泄漏(产生了类似死锁互相等待的情况)。
    • 线程不安全。
  • 应用实例

    /***************************************************************
     * (constructor)
     * ************************************************************/
    std::shared_ptr<int> sp1;               // 默认构造,没有获得的任何指针的所有权,引用计数为 0
    std::shared_ptr<int> sp2(nullptr);      // 作用同上
    std::shared_ptr<int> sp3(new int);      // 拥有指向 int 对象指针的所有权,引用计数为 1
    std::shared_ptr<int> sp4(new int, std::default_delete<int>());  
                                            // 作用同上,但是拥有自己的删除器(析构函数)。主要用于管理对象中包含指针的情况
    std::shared_ptr<int> sp5(new int, [](int* p) { delete p; }, std::allocator<int>());
                                            // 作用同上,但是拥有自己的删除器(析构函数)和分配器(构造函数),主要用于管理对象中包含指针的情况
    std::shared_ptr<int> sp6(sp5);          // 拷贝构造,sp5 的引用计数 + 1
    std::shared_ptr<int> sp7(std::move(sp6));
                                            // 移动构造,sp6 的所有权被移交到 sp7
    std::shared_ptr<int> sp8(std::unique_ptr<int>(new int));
                                            // sp8 引用计数为 1
    
    /***************************************************************
     * operator=
     * ************************************************************/
    std::shared_ptr<int> sp1;
    std::shared_ptr<int> sp2(new int(10));
    sp1 = sp2;                              // 拷贝赋值
    sp2 = std::make_shared<int>(20);        // 移动赋值
    std::unique_ptr<int> up1(new int(30));
    sp1 = std::move(up1);                   // 将 unique_ptr 移动到 shared_ptr 并设置引用计数为 1
    
    /***************************************************************
     * swap
     * ************************************************************/
    std::shared_ptr<int> sp1(new int(10));
    std::shared_ptr<int> sp2(new int(20));
    
    sp1.swap(sp2);                          // 注意此处的 sp1 是对象而不是指针,引用要使用 . 而非 ->
    
    /***************************************************************
     * reset
     * ************************************************************/
    std::shared_ptr<int> sp1;
    sp1.reset(new int(20));                 // 重置 sp1 管理的对象
    
    /***************************************************************
     * get
     * ************************************************************/
    int* p1 = new int(10);
    std::shared_ptr<int> sp1(p1);
    
    std::cout << (p1 == sp1.get()) << std::endl;    // 1
    
    /***************************************************************
     * use_count
     * ************************************************************/
    std::shared_ptr<int> sp1;
    std::shared_ptr<int> sp2(new int(10));
    std::shared_ptr<int> sp3(new int(20));
    std::shared_ptr<int> sp4(sp2);
    
    std::cout << sp1.use_count() << std::endl;      // 0
    std::cout << sp2.use_count() << std::endl;      // 2
    std::cout << sp3.use_count() << std::endl;      // 1
    std::cout << sp4.use_count() << std::endl;      // 2
    
    /***************************************************************
     * unique
     * ************************************************************/
    std::shared_ptr<int> sp1;
    std::shared_ptr<int> sp2(new int(10));
    std::shared_ptr<int> sp3(new int(20));
    std::shared_ptr<int> sp4(sp2);
    
    std::cout << sp1.unique() << std::endl;         // 0
    std::cout << sp2.unique() << std::endl;         // 0
    std::cout << sp3.unique() << std::endl;         // 1
    std::cout << sp4.unique() << std::endl;         // 0
    
    /***************************************************************
     * operator bool
     * ************************************************************/
    std::shared_ptr<int> sp1;
    std::shared_ptr<int> sp2(new int(10));
    std::shared_ptr<int> sp3(new int(20));
    std::shared_ptr<int> sp4(sp2);
    
    std::cout << (sp1 ? "true" : "false") << std::endl;     // false
    std::cout << (sp2 ? "true" : "false") << std::endl;     // true
    std::cout << (sp3 ? "true" : "false") << std::endl;     // true
    std::cout << (sp4 ? "true" : "false") << std::endl;     // true
    
    /***************************************************************
     * relational operators
     * ************************************************************/
    std::shared_ptr<int> sp1, sp2, sp3, sp4;
    sp1 = std::make_shared<int>(10);
    sp2 = std::make_shared<int>(10);
    sp3 = sp2;
    
    std::cout << (sp1 == sp2 ? "true" : "false") << std::endl;  // false
    std::cout << (sp2 == sp3 ? "true" : "false") << std::endl;  // true
    std::cout << (sp3 == sp4? "true" : "false") << std::endl;   // fasle
    std::cout << (sp1 == nullptr? "true" : "false") << std::endl;   
                                                                // false
    std::cout << (sp2 == nullptr ? "true" : "false") << std::endl;
                                                                // false
    std::cout << (sp3 == nullptr ? "true" : "false") << std::endl;
                                                                // false
    std::cout << (sp4 == nullptr ? "true" : "false") << std::endl;
                                                                // true
    
    /***************************************************************
     * ostream operator<<
     * ************************************************************/
    std::shared_ptr<int> sp1(new int(10));
    
    std::cout << sp1 << std::endl;          // 0070A590
    std::cout << *sp1 << std::endl;         // 10
    
    /***************************************************************
     * std::make_shared
     * ************************************************************/
    std::shared_ptr<int> sp1 = std::make_shared<int>(10);
    
    /***************************************************************
     * std::swap
     * ************************************************************/
    std::shared_ptr<int> sp1(new int(10));
    std::shared_ptr<int> sp2(new int(20));
    std::swap(sp1, sp2);
    
    /***************************************************************
     * Add in C++17
     * std::static_pointer_cast  
     * std::dynamic_pointer_cast
     * std::const_pointer_cast
     * std::reinterpret_pointer_cast
     * ************************************************************/
    std::shared_ptr<int> sp1(new int(10));
    std::shared_ptr<const int> sp2 = std::const_pointer_cast<const int>(sp1);
    
  • 线程不安全原因

    • 一个 shared_ptr 对象实体可被多个线程同时读取(文档例1);
    • 两个 shared_ptr 对象实体可以被两个线程同时写入(例2),“析构”算写操作;
    • 如果要从多个线程读写同一个 shared_ptr 对象,那么需要加锁(例3~5)。

02 unique_ptr 独占的智能指针

unique_ptr 独占一个指针对象指向的内存的所有权:一旦它获得指针指向的内存的所有权,它就会在一定的时刻负责释放指针指向的内存
unique_ptr 独占内存不需要引用计数所以与 shared_ptr 相比开销较小。
unique_ptr 出于安全的原因不支持拷贝构造、复制运算符重载,只支持移动构造、移动赋值运算符重载。

  • 成员函数

    (constructor)           // 构造新的 shared_ptr
    (destructor)            // 如果没有更多的 shared_ptr 指向持有的对象则析构对象
    operator=               // 对 unqiue_ptr 进行赋值
    swap                    // 交换管理的对象,不会改变引用计数
    reset                   // 替换所管理的对象,并销毁直至指向的对象
    release                 // 放弃了原本持有的指针指向内存的控制权,返回持有的指针并将持有的指针设置为 nullptr
    get                     // 返回存储的指针
    get_deleter             // 返回指定类型中的删除其(如果有的话)
    operator bool           // 检查是否有被管理的对象
    
  • 非成员函数

    std::make_unique        // [C++14]创建管理一个对象的 unique_ptr
    std::swap(std::shared_ptr)  
                            // 特化的 std::swap 算法
    relational operators    // 在 unique_ptr 对象 lhs 和 rhs 之间或 unique_ptr 和 nullptr 之间执行适当的关系比较操作
    ostream operator<<      // 输入输出接口
    
  • 注意事项

    • 不支持拷贝构造、复制运算符重载,只支持移动构造、移动赋值运算符重载。
    • 线程安全。
  • 应用实例

    /***************************************************************
     * constructor
     * ************************************************************/
    std::default_delete<int> dd;
    std::unique_ptr<int> up1;
    std::unique_ptr<int> up2(nullptr);
    std::unique_ptr<int> up3(new int);
    std::unique_ptr<int> up4(new int, dd);
    std::unique_ptr<int> up5(new int, std::default_delete<int>());
    std::unique_ptr<int> up6(std::move(up5));
    
    /***************************************************************
     * operator=
     * ************************************************************/
    std::unique_ptr<int> up1;
    std::unique_ptr<int> up2;
    std::unique_ptr<int> up3;
    
    up1 = std::unique_ptr<int>(new int(10));
    up2 = std::move(up1);
    up3 = std::make_unique<int>(10);    // Add in C++14              
    
    /***************************************************************
     * swap  
     * ************************************************************/
    std::unique_ptr<int> up1;
    std::unique_ptr<int> up2;
    
    up1.swap(up2);
    
    /***************************************************************
     * reset
     * ************************************************************/
    std::unique_ptr<int> up1;
    up1.reset(new int);     
    
    std::cout << *up1 << std::endl;     // 8975 随机值
    
    up1.reset(new int(10));
    std::cout << *up1 << std::endl;     // 10
        
    *up1 = 20;
    std::cout << *up1 << std::endl;     // 20
    
    /***************************************************************
     * release
     * ************************************************************/
    std::unique_ptr<int> up1(new int(10));
    // up1.release();                       // error: unique_ptr 放弃了原本持有的指针指向内存的控制权,返回持有的指针并将持有的指针设置为 nullptr
    auto p1 =  up1.release();
    std::cout << *p1 << std::endl;          // 10
    std::cout << (up1.get() == nullptr ? "up1.get == nullptr" : "up1.get == not nullptr") << std::endl;
                                            // up1.get == nullptr
    
    /***************************************************************
     * get
     * ************************************************************/
    int* p1 = new int(10);
    std::unique_ptr<int> sp1(p1);
    
    std::cout << (p1 == sp1.get()) << std::endl;    // 1
    
    /***************************************************************
     * 以下方法用法同 std::shared_ptr
     * operator bool
     * std::make_unique [C++14]
     * std::swap(std::shared_ptr)  
     * relational operators 
     * ostream operator<<   
     * ************************************************************/
    

03 weak_ptr 独占的智能指针

weak_ptr 主要是为了解决 shared_ptr 可能引起的循环引用问题。
weak_ptr 对被 shared_ptr 管理的对象具有弱引用,即不会增加 shared_ptr 持有指针的引用计数
weak_ptr 不能直接使用,在访问所引用的对象前必须先转换成 shared_ptr 类型,如果原来的。

  • 成员函数

    (constructor)           // 构造新的 weak_ptr
    (destructor)            // 如果没有更多的 weak_ptr 指向持有的对象则析构对象
    operator=               // 对 weak_ptr 进行赋值
    swap                    // 交换管理的对象,不会改变引用计数
    reset                   // 替换所管理的对象,并销毁直至指向的对象
    use_count               // 返回 weak_ptr 所指对象的引用计数
    expired                 // 返回与 use_count == 0 相同的值
    lock                    // 获取 weak_ptr 监视的 shared_ptr
                            // 如果 weak_ptr 没有过期则返回 shared_ptr 
                            // 如果 weak_ptr 过期则返回空的 shared_ptr 
    owner_before            // 
    
  • 非成员函数

    std::swap(std::shared_ptr)      // 特化的 std::swap 算法
    
  • 注意事项

  • 应用实例

    /***************************************************************
     * constructor
     * ************************************************************/
    std::shared_ptr<int> sp1(new int(10));
    
    std::weak_ptr<int> wp1;
    std::weak_ptr<int> wp2(wp1);
    std::weak_ptr<int> wp3(sp1);
    
    std::cout << wp1.use_count() << std::endl;          // 0
    std::cout << wp2.use_count() << std::endl;          // 0
    std::cout << wp3.use_count() << std::endl;          // 1
    
    /***************************************************************
     * operator=
     * ************************************************************/
    std::shared_ptr<int> sp1(new int(10));
    std::weak_ptr<int> wp1, wp2;
    
    wp1 = sp1;
    wp2 = wp1;
    
    /***************************************************************
     * swap
     * ************************************************************/
    std::shared_ptr<int> sp1 (new int(10));
    std::shared_ptr<int> sp2 (new int(20));
    
    std::weak_ptr<int> wp1(sp1);
    std::weak_ptr<int> wp2(sp2);
    
    wp1.swap(wp2)
    
    /***************************************************************
     * reset
     * ************************************************************/
    std::shared_ptr<int> sp1 (new int(10));
    
    std::cout << sp1.get() << std::endl;                // 1
    
    std::weak_ptr<int> wp1(sp1);
    
    wp1.reset();
    
    std::cout << sp1.get() << std::endl;                // 1
    /***************************************************************
     * use_count
     * ************************************************************/
    std::shared_ptr<int> sp1(new int(10));
    std::weak_ptr<int> wp1, wp2;
    
    wp1 = sp1;
    
    std::cout << wp1.use_count() << std::endl;      // 1
    std::cout << wp2.use_count() << std::endl;      // 0
    
    /***************************************************************
     * expired
     * ************************************************************/
    std::shared_ptr<int> sp1(new int(10));
    std::weak_ptr<int> wp1 = sp1;
    
    std::cout << wp1.expired() << std::endl;        // 0
    
    sp1.reset();
    
    std::cout << wp1.expired() << std::endl;        // 1
    
    /***************************************************************
     * lock
     * ************************************************************/
    std::shared_ptr<int> sp1(new int(10));
    std::weak_ptr<int> wp1 = sp1;
    
    std::shared_ptr<int> sp2 = wp1.lock();
    std::weak_ptr<int> wp2 = sp2;
    
    /***************************************************************
     * std::swap(std::shared_ptr) 
     * ************************************************************/
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值