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) 
     * ************************************************************/
    
C++智能指针是一种工具,用于提供更安全、简单和方便的内存管理方式。它实际上是一种对象,内部管理着一个指向动态分配内存块的指针。智能指针的实现依赖于C++的RAII(资源获取即初始化)技术,在对象构造时分配资源,析构时释放资源,以此确保资源正确使用 [^2]。 智能指针将动态分配的内存块与一个或多个智能指针对象相关联,确保内存块在不再需要时自动释放。通常会把内存所有权转移到自身对象,并在析构函数中释放内存,避免常见的内存泄漏和释放非法内存等问题。实现时常用引用计数技术,每个智能指针对象有计数器记录引用同一内存块的对象数量,引用计数为0时释放内存。不过引用计数可能存在循环引用问题,为此C++11引入了`weak_ptr`来解决 [^2]。 C++中定义了4种智能指针:`auto_ptr`、`unique_ptr`、`shared_ptr`和`weak_ptr`,其中`auto_ptr`在C++11被摒弃,C++17中移除不可用 [^4]。以下是几种常用智能指针的使用方法: ### 自定义智能指针示例 ```cpp #include <iostream> template <typename T> class MyPointer{ private: T* pointer_; public: MyPointer(T *p): pointer_(p){ } ~MyPointer(){ delete pointer_; std::cout << "pointer_ destroy."; } }; int main(){ MyPointer<int> p(new int(10)); } ``` 此示例展示了简单自定义智能指针的实现,利用类对象出作用域自动调用析构函数的特点,在析构函数中进行内存回收 [^1]。 ### unique_ptr智能指针 `unique_ptr`是一种独占式智能指针,同一时间只能有一个`unique_ptr`指向某个对象,当它被销毁时,所指向的对象也会被销毁。 ```cpp #include <iostream> #include <memory> int main() { std::unique_ptr<int> p1(new int(10)); // 判断是否为空 if (p1) { std::cout << *p1 << std::endl; } // 修改智能指针所有权 std::unique_ptr<int> p2 = std::move(p1); if (!p1 && p2) { std::cout << *p2 << std::endl; } return 0; } ``` ### shared_ptr智能指针 `shared_ptr`采用引用计数技术,多个`shared_ptr`可以指向同一个对象,当引用计数为0时,对象被销毁。 ```cpp #include <iostream> #include <memory> int main() { std::shared_ptr<int> p1(new int(10)); std::shared_ptr<int> p2 = p1; std::cout << p1.use_count() << std::endl; // 输出引用计数 return 0; } ``` ### weak_ptr智能指针 `weak_ptr`是一种弱引用智能指针,它不控制对象的生命周期,主要用于解决`shared_ptr`的循环引用问题。 ```cpp #include <iostream> #include <memory> class B; class A { public: std::shared_ptr<B> b_ptr; ~A() { std::cout << "A destroyed" << std::endl; } }; class B { public: std::weak_ptr<A> a_ptr; ~B() { std::cout << "B destroyed" << std::endl; } }; int main() { std::shared_ptr<A> a = std::make_shared<A>(); std::shared_ptr<B> b = std::make_shared<B>(); a->b_ptr = b; b->a_ptr = a; return 0; } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值