make_shared和shared_ptr<T>()的比拟

make_shared只进行一次堆内存调配,shared_ptr<T>()进行两次堆内存调配

  • make_shared :将对象Object 和 管制块Control Block调配到一起
  • shared_ptr<T>() :先new Object,再调配Contrl Block

造成的结果:

  1. Exception safety:函数 f 接管两个 shared_ptr A、B作为参数,那么经验四个步骤

    1. new A
    2. new B
    3. shared_ptr<T>(A)
    4. shared_ptr<T>(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... ↩