读书笔记之智能指针广度搜索

make_shared和shared_ptr()的比较 make_shared只进行一次堆内存分配,shared_ptr()进行两次堆内存分配

  • make_shared :将对象Object 和 控制块Control Block分配到一起
  • shared_ptr() :先new Object,再分配Contrl Block
【读书笔记之智能指针广度搜索】造成的后果:
  1. Exception safety:函数 f 接收两个 shared_ptr A、B作为参数,那么经历四个步骤
    1. new A
    2. new B
    3. shared_ptr(A)
    4. shared_ptr(B)
    如果步骤2抛出异常(例如OOM),那么A对象没有被回收,因为此时A对象没有被智能指针管理起来,造成了资源泄露。
    这个问题在C++17中得到了解决,C++17对函数参数求值顺序做了规定,规定了函数参数求值需要 fully evaluate后 才能对下一个参数进行求值
  2. Disadvantage of make_shared:weak_ptr的存在使得对象可能永远得不到释放。
    1. 因为weak_ptr的实现原理,是根据Control Block中的引用计数来判断对象是否被释放,因此Control Block的生命周期至少要和weak_ptr一样长。
    2. 而make_shared在一次分配中分配了对象和Control Block的空间,因此只有对象和Control Block都不再使用的时候,才能进行释放。这就导致了即使引用计数为0,对象也没有被释放,直到最后一个weak_ptr析构,资源才能被一起释放。而shared_ptr由于是两次分配,可以分别释放。
noexcept 和 stack unwind 和 FPO noexcept 声明函数不抛出异常,方便编译器优化(使用move),对于big four(constructor 、 assignment 效果显著)。也可以在编译期对不同类型判断是否会抛出异常。
保证不抛出异常,编译器就不必进行Exception Handling
FPO1:
问题来源:如何索引栈上变量?
方案一:通过esp进行偏移索引。缺点是esp会发生变动,要求编译器生成的代码更复杂。
方案二:通过ebp进行偏移索引,ebp在一个栈帧中是固定的,更简单。
必须使用ebp的场景
  1. SEH 当处理异常时,不能通过esp正确索引栈上变量,因为esp可能已经发生变动
  2. 具有destructor的对象必须使用SEH来获得unwind support
  3. 使用alloca动态分配栈上数组,则必须 使用ebp,因为编译器无法获得变量对esp的偏移
=> 大部分场景 FPO disabled
FPO disabled的好处:可以在缺失符号的情况下,根据ebp,将栈当作链表串起来
FPO的优势:
1. x86寄存器太少,多一个寄存器可以少在栈上分配空间,访存更快 2. 没有保存、恢复ebp的开销,代码提交更小

custom deleter in smart_ptr
  • std::function => 32 bits on x64 payload of unnecessary flexibility
  • function ptr => 8 bits on x64
  • stateless lambda => 0 EBO
  • stateful lambda => sizeof (lambda)
关注引起的 smart_ptr size 变化2
Empty Base Optimization 对于空类(不含有 non static 成员变量的类),继承时不占用空间,而组合时占用空间3
  1. [FPO]:http://www.nynaeve.net/?p=91 ?
  2. [custom_deleter_in_smart_ptr]:https://www.bourez.be/?p=19#:...,default%20deleter%20(8%20bytes ?
  3. [EBO]: https://www.yhspy.com/2020/04... ?

    推荐阅读