Colderleo's blog Colderleo's blog
首页
Linux
C++
Python
前端
工具软件
mysql
索引
关于
GitHub (opens new window)

Colder Leo

热爱代码的打工人
首页
Linux
C++
Python
前端
工具软件
mysql
索引
关于
GitHub (opens new window)
  • bug定位的一些情形
  • c++性能调优,可能的情况
  • total-编程知识点集锦
  • hpc_common.hpp
  • memory order 内存模型
  • 类型推导之auto-template-decltype
  • 完美转发forward源码分析
  • 左值和右值,右值引用、重载 std-move,引用折叠
  • cmake用法
  • alignas、alignof、sizeof实现内存对齐分配
  • 通过宏定义控制debug打印
  • 程序耗时性能测试
  • 线程池开源项目阅读
  • C++类中包含没有默认构造函数的成员
  • C++可变参数模板
  • C++属性继承 public protected private
  • C++智能指针
    • 三种智能指针简介
    • shared_ptr 内部实现
    • 类中含有 shared_ptr 时的拷贝构造函数
    • 尽量用makeshared对sharedptr初始化
    • 注意避坑
    • 避免内存圈养(shared_ptr循环依赖导致内存不释放)
  • C++导出so的方法,以及extern C 注意事项
  • 四种spin lock
  • condition_variable和unique_lock
  • dpdk、kernel bypass
  • 智能网卡solarflare、Mellanox、X10等
  • 汇编寄存器和常见指令
  • c++ 类的静态成员变量未定义
  • C++获取类成员函数地址
  • preload示例
  • C++异常安全和RAII
  • C++11单例模式
  • C++歪门邪道
  • boost-program-option用法
  • c++17通过模板获取类成员的个数
  • 通过模板实现结构体成员拷贝-按成员名匹配
  • STL学习路径
  • boost库安装使用方法
  • C++文件读写
  • linux下socket通信demo,server和client
  • makefile写法
  • RxCpp
  • C++
gaoliu
2021-11-02
目录

C++智能指针

# 三种智能指针简介

  • 参考 https://www.cnblogs.com/wang1994/p/10765974.html (opens new window)

  • shared_ptr shared_ptr的多个对象指向同一个指针(大多是new出来的空间指针),该指针使用引用计数,每使用一次,内部计数器加1,每析构一次,内部的引用计数器减1,减为0的时候,自动删除指向的堆内存。

  • unique_ptr “唯一”拥有所指对象,同一时刻只能有一个unique_ptr指向给定象(禁止使用拷贝语义,只能用移动语义将其移动)。对比原始指针,也是利用了RAII的特性。用户可以定义delete操作。可以通过move、release等转移所有权。

  • weak_ptr 当两个shared_ptr互相引用时会引起死锁,导致都不释放。为了解决这种情况,引入weak_ptr配合shared_ptr,是弱引用,相对于shared_ptr强引用来说的。看似就像一个观察者,观测资源的使用情况。weak_ptr可以从一个shared_ptr获取另一个weak_ptr来构造,获取资源的观察权 。 并不会引起计数加的情况,成员有use_count(),查看资源的引用计数,expired(),判断是否指向的资源被释放, 当返回为true的时候,这个资源的引用计数为0,相当于被释放,反之就没有被释放掉。lock(),返回当前分享指 针,计数器并加1.

    image-20211101163240014

    {
        sp1 = make_shared<A_t> A;
        sp2 = make_shared<A_t> B;
    }
    
    1
    2
    3
    4

    如图,左边是A中有一个shared_ptr指向B, B中有一个shared_ptr指向A。A的引用计数为2(sp1、sp4),B的引用计数也为2(sp2、sp3)。sp1和sp3析构完,A和B的引用都还剩1,无法释放。

    右边A中有一个shared_ptr指向B,B中有一个weak_ptr指向A。A的引用计数为1(sp1),B的引用计数为2(sp2、sp3)。当sp1和sp3析构时,A的引用计数减1后释放,B的引用计数减2后释放。

    weak_ptr使用时,不能直接访问成员,需要通过lock函数得到一个shared_ptr,再通过该shared_ptr访问成员。因为直接使用weak_ptr可能会在过程中对象析构,而通过lock得到shared_ptr就不会出现这种情况。

# shared_ptr 内部实现

参考: https://blog.csdn.net/hanzhen7541/article/details/105473228 (opens new window)

  • shared_ptr内部维护引用计数 *count,指向 count, 还有一个指向实际资源的地址 *resource。

  • 构造函数, 如果给了资源了,则*count=1, 没给则*count=0

  • 拷贝构造函数, atomic *count++

  • 赋值运算符 = , 原来的 atomic *count-- , 新的 *count++

  • 对引用计数的加减操作是原子操作(线程安全),对实际资源并无原子操作保护。

# 类中含有 shared_ptr 时的拷贝构造函数

当类中含有智能指针时,编译器为其自动生成的拷贝构造函数会增加引用计数,因为:

自动生成的拷贝构造函数将为每个成员变量使用拷贝构造函数,或对内置类型进行逐位复制。

如果用memcpy对 shared_ptr 进行拷贝,则会导致实际的引用多了1, 而引用计数没有增加。这种情况在析构时不会出错,但是在引用时可能会出错。

# 尽量用make_shared对shared_ptr初始化

参考: https://www.jianshu.com/p/03eea8262c11 (opens new window)

用new初始化,两次内存分配:

std::shared_ptr<Widget> spw(new Widget);
1

img

用make_shared初始化,一次内存分配, 智能指针和对象存在同一块区域:

auto spw = std::make_shared<Widget>();
1

img

make_shared缺点:

  • 构造函数是保护或私有时,无法使用 make_shared。

  • 对象的内存可能无法及时回收 make_shared 只分配一次内存, 这看起来很好. 减少了内存分配的开销. 问题来了, weak_ptr 会保持控制块(强引用, 以及弱引用的信息)的生命周期, 而因此连带着保持了对象分配的内存, 只有最后一个 weak_ptr 离开作用域时, 内存才会被释放. 原本强引用减为 0 时就可以释放的内存, 现在变为了强引用, 若引用都减为 0 时才能释放, 意外的延迟了内存释放的时间. 这对于内存要求高的场景来说, 是一个需要注意的问题.

# 注意避坑

参考: 使用智能指针的17个原则:https://www.cnblogs.com/lidabo/p/3911440.html (opens new window)

  • weak_ptr 在循环中会严重降低效率。

  • 不要把this指针给shared_ptr

# 避免内存圈养(shared_ptr循环依赖导致内存不释放)

参考: https://github.com/xhawk18/noshared_ptr

shared_ptr大量使用时,很可能导致循环依赖从而使得内存无法释放。为了避免这种情况,尽可能明确shared_ptr的归属,防止滥用。上面这个noshared_ptr就是一个很好的解决方案,它相当于是unique_ptr和unique_ref_ptr, 保证了拥有者只有一个,并且观测者ref_ptr在其作用域内临时lock()出的ptr只在作用域内有效,出了作用域后就无效了。

个人觉得上面的实现对于使用者来说内部原理不容易理解,可能会出错。可以考虑用三种类型来实现上述思想: unique_ptr、 unique_weak_ptr、 unique_lock_ptr 。其中:

  • unique_ptr可以创建unique_weak_ptr;
  • unique_weak_ptr可以lock出unique_lock_ptr; unique_lock_ptr不能被move、copy
编辑 (opens new window)
上次更新: 2023/05/07, 17:27:54
C++属性继承 public protected private
C++导出so的方法,以及extern C 注意事项

← C++属性继承 public protected private C++导出so的方法,以及extern C 注意事项→

最近更新
01
通过模板实现结构体成员拷贝-按成员名匹配
05-07
02
c++17通过模板获取类成员的个数
05-01
03
avx-sse切换惩罚
04-30
更多文章>
Theme by Vdoing | Copyright © 2019-2023 Colder Leo | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×