make_shared 和 shared_ptr<T>() 的比拟
make_shared 只进行一次堆内存调配,shared_ptr<T>() 进行两次堆内存调配
- make_shared:将对象 Object 和 管制块 Control Block 调配到一起
- shared_ptr<T>():先 new Object,再调配 Contrl Block
造成的结果:
-
Exception safety:函数 f 接管两个 shared_ptr A、B 作为参数,那么经验四个步骤
- new A
- new B
- shared_ptr<T>(A)
- shared_ptr<T>(B)
如果步骤 2 抛出异样(例如 OOM),那么 A 对象没有被回收,因为此时 A 对象没有被智能指针治理起来,造成了资源泄露。
这个问题在 C ++17 中失去了解决,C++17 对函数参数求值程序做了规定,规定了函数参数求值须要 fully evaluate 后 能力对下一个参数进行求值
-
Disadvantage of make_shared:weak_ptr 的存在使得对象可能永远得不到开释。
- 因为 weak_ptr 的实现原理,是依据 Control Block 中的援用计数来判断对象是否被开释,因而 Control Block 的生命周期至多要和 weak_ptr 一样长。
- 而 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 的场景
- SEH 当解决异样时,不能通过 esp 正确索引栈上变量,因为 esp 可能曾经产生变动
- 具备 destructor 的对象必须应用 SEH 来取得 unwind support
- 应用 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
- [FPO]:http://www.nynaeve.net/?p=91 ↩
- [custom_deleter_in_smart_ptr]:https://www.bourez.be/?p=19#:…,default%20deleter%20(8%20bytes ↩
- [EBO]: https://www.yhspy.com/2020/04… ↩