item14:updating

This commit is contained in:
racaljk 2018-06-21 16:11:13 +08:00
parent 527864312a
commit bc4cda0bf8

View File

@ -33,3 +33,13 @@ Widget w;
vw.push_back(w); // add w to vw
```
假设这个代码能正常工作你也无意修改为C++11风格。但是你确实想要C++11移动语义带来的性能优势毕竟这里的类型是可以移动的(move-enabled types)。因此你需要确保**Widget**有移动操作可以手写代码也可以让编译器自动生成当然前提是自动生成的条件能满足参见Item 17
当新元素添加到`std::vector``std::vector`可能没地方放它,换句话说,`std::vector`的大小(size)等于它的容量(capacity)。这时候,`std::vector`会分配一片的新的大块内存用于存放然后将元素从已经存在的内存移动到新内存。在C++98中移动是通过复制老内存区的每一个元素到新内存区完成的然后老内存区的每个元素发生析构。
这种方法使得`push_back`可以提供很强的异常安全保证:如果在复制元素期间抛出异常,`std::vector`状态保持不变,因为老内存元素析构必须建立在它们已经成功复制到新内存的前提下。
在C++11中一个很自然的优化就是将上述复制操作替换为移动操作。但是很不幸运这回破坏`push_back`的异常安全。如果**n**个元素已经从老内存移动到了新内存区,但异常在移动第**n+1**个元素时抛出,那么`push_back`操作就不能完成。但是原始的`std::vector`已经被修改:有**n**个元素已经移动走了。恢复`std::vector`至原始状态也不太可能,因为从新内存移动到老内存本身又可能引发异常。
这是个很严重的问题,因为老代码可能依赖于`push_back`提供的强烈的异常安全保证。因此C++11版本的实现不能简单的将`push_back`里面的复制操作替换为移动操作,除非知晓移动操作绝不抛异常,这时复制替换为移动就是安全的,唯一的副作用就是性能得到提升。
`std::vector::push_back`受益于"如果可以就移动,如果必要则复制"策略,并且它不是标准库中唯一采取该策略的函数。