Skip to content

Commit 1b6b93d

Browse files
committed
Update notes for Smart pointers
1 parent 448c8a5 commit 1b6b93d

File tree

1 file changed

+68
-0
lines changed

1 file changed

+68
-0
lines changed

2016/1.markdown

+68
Original file line numberDiff line numberDiff line change
@@ -187,3 +187,71 @@
187187
+ 为你的C++11类,明确声明constructor/copy/move/destructor的`=default`的行为
188188

189189
### Smart Pointers
190+
+ Item 18: Use std::unique_ptr for exclusive-ownership resource management
191+
+ 裸指针的缺点
192+
+ 没有说明指向的单元素还是数组
193+
+ 没有说明你是否应该负责销毁指向的元素
194+
+ 没有说明应该怎样销毁,delete还是destroy
195+
+ 如果是delete,没有说明应该用delete还是delete[]
196+
+ 很难保证销毁、并且仅销毁一次
197+
+ 没法判断一个指针是否是悬空指针
198+
+ 析构函数在很多情况下不保证被调用(智能指针也失效)
199+
+ 异常没有被处理的时候(抛出main函数,此时不保证stack unwinding;相对的,此时c#的finally块儿保证被调用)
200+
+ noexcept异常规范被破坏的时候
201+
+ std::abort 或者 exit (std::_Exit, std::exit, std::quick_exit)函数被调用的时候
202+
+ unique_ptr 几乎和裸指针性能一样,没有shared_ptr的那些性能惩罚(atomic ref count, control block allocation, virtual call of deleter, at least 2 word size)
203+
+ 关于std::unique_ptr
204+
+ deleter是type的一部分,所以声明unique_ptr变量的地方,要求deleter访问的对象是complete type
205+
+ 因为deleter是type的一部分,所以实际的销毁是inline的,没有性能惩罚
206+
+ 默认的deleter是delete,此时unique_ptr对象的大小是一个字
207+
+ customer deleter如果是stateless的话,unique_ptr对象的大小仍然是一个字;否则会加上deleter的状态大小
208+
+ unique_ptr 被广泛的用作工厂函数,尤其是他可以隐式转换成 shared_ptr
209+
+ unique_ptr 被广泛的用于 pimpl idiom
210+
+ unique_ptr 支持数组形式 (`unique_ptr<T[]>`),此时不能解引用(`operator*``operator->`),但支持`operator[]`;数组形式没有隐式的向下转型(因为数组的元素size不同)
211+
+ 尽管此时用std::vector和std::array更好
212+
+ Item 19: Use std::shared_ptr for shared-ownership resource management
213+
+ 相对于GC的优势:generality and predictability
214+
+ shared_ptr 的缺点
215+
+ 对象是2个字大 (对象指针+控制块指针)
216+
+ 控制块(control block)中,包括引用数、弱引用数、deleter数据
217+
+ 控制块的实现中往往需要虚函数
218+
+ 除非使用make_shared,控制块的内存需要一次额外分配
219+
+ 增加和减小引用计数是原子的
220+
+ 所以move运算比copy运算快,前者不需要修改引用数
221+
+ deleter 不是类型的一部分,因此更灵活,但有运行时惩罚
222+
+ 因此,声明shared_ptr变量的时候,不要求completed type,只在初始化的时候要求可见
223+
+ deleter 的大小不影响 shared_ptr 对象大小
224+
+ 有 shared_from_this() 需求的类型应该继承 enable_shared_from_this ,然后声明 private 构造,然后工厂函数返回 shared_ptr
225+
+ 构造 shared_ptr 的时候,enable_shared_from_this 内部会记录 control block 的地址
226+
+ 不应该用 shared_ptr 保存数组(T[])
227+
+ 默认的delete不能用于数组
228+
+ shared_ptr 允许从drived sp转换到base sp,这对数组是错的
229+
+ 不应该通过裸指针构造shared_ptr,因为这个裸指针可能被多次用于构造sp
230+
+ Item 20: Use std::weak_ptr for std::shared_ptr-like pointers that can dangle
231+
+ 需要通过 lock 来返回一个 shared_ptr 是因为分离的 expired() 判断和解引用有race condition
232+
+ 在很多实现当中,control block 中的weak ref count是不等于weak_ptr对象数的。比如,可能用额外的1表示有shared_ptr存在
233+
+ 典型应用
234+
+ cache
235+
+ observer list
236+
+ prevention of shared_ptr cycles
237+
+ Item 21: Prefer std::make_unique and std::make_shared to direct use of new
238+
+ make_unique 和 make_shared 不支持custom allocator,后者有提供对应的 allocate_shared,它的第一个参数是allocator
239+
+ make_xxx 的优点
240+
+ make + auto 只需要写对象类型一次,而 new 需要两次(声明sp+new),避免了重复
241+
+ exception safety。一旦new和ptr的构造之间有其他函数调用,并抛出了异常,会是资源泄露
242+
+ 在c++17以前,函数各个实参求值之间彼此是unsequenced关系,即可用相互overlap,此时写符合表达式来初始化sp对象很容易导致泄露;c++17以后,求值顺序变成了indeterminately sequenced,稍好,但一样不可避免异常安全问题
243+
+ 对策,应该以单独的语句来以空指针初始化sp (当然此句之前,上至new,都不应该有异常)
244+
+ make_shared 有性能优势,因为 control block 可以和对象放在一个内存块上
245+
+ 两次分配在性能和尺寸上都有惩罚
246+
+ 以裸指针构造sp,可能导致同一个裸指针被用于构造多个sp
247+
+ make_xxx 的缺点
248+
+ 不支持 custom deleter。尤其是 customer deleter 往往也要求特殊的 allocator
249+
+ make 内部使用的是 () 初始化而不是 {} 初始化
250+
+ 无法初始化c-style struct
251+
+ 无法完美转发 braced-initialization,所以`make_shared<vector<int>>({1, 2, 3})` 是无效的,必须引入一个额外的initializer_list 变量
252+
+ make_shared 不支持用户自定义operator new和operator delete,因为make_shared实际分配的内存是object size + control block size
253+
+ make_shared 使得control block 和对象布局在同一个内存块上,虽然对象已经因为ref count归0而析构,但整块内存还要等到weak ref count归零才回收。如果weak_ptr生命期明显比较长,而对象尺寸又很大,那么这导致这块内存长时间不得释放。而直接构造shared_ptr会有两个内存块,对象内存块在ref count归0就回收了,weak ref count只会延迟control block的回收。
254+
+ Item 22: When using the Pimpl Idiom, define special member functions in the implementation file
255+
+ 使用 unique_ptr 来实现 Pimpl idiom 时,由于 deleter 是 unique_ptr 的一部分,所以需要在实现代码中明确的定义析构和move op甚至copy op
256+
+ 多用 `= default`
257+
+ 使用 shared_ptr 来实现 Pimpl idiom 时(往往是实现flyweight pattern的对象),只需要写构造即可,因为deleter不再是类型的一部分,只在构造shared_ptr的时候要求completed type

0 commit comments

Comments
 (0)