public:it:cplusplus:effective_modern_cplusplus

差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
public:it:cplusplus:effective_modern_cplusplus [2022/07/06 11:32] – [章节4. 智能指针] oakfirepublic:it:cplusplus:effective_modern_cplusplus [2024/11/06 15:51] (当前版本) – [章节5. 右值引用、移动语义和完美转发] oakfire
行 82: 行 82:
   * 要避免同时重载整形与指针类型   * 要避免同时重载整形与指针类型
 ===  » 条目9. 相比 typedefs 优先选择使用别名 === ===  » 条目9. 相比 typedefs 优先选择使用别名 ===
-  * 别名用于声明函数指针时更直观:<code c++>+  * 别名用于声明函数指针时更直观:<code cpp>
 // FP is a synonym for a pointer to a function taking an int and // FP is a synonym for a pointer to a function taking an int and
 // a const std::string& and returning nothing // a const std::string& and returning nothing
行 96: 行 96:
   * 限定域枚举可以前置声明 ''enum class Color;'', 非现定域则需要指定基础类型才可前置声明 ''enum Color: std::uint8_t;''   * 限定域枚举可以前置声明 ''enum class Color;'', 非现定域则需要指定基础类型才可前置声明 ''enum Color: std::uint8_t;''
 ===  » 条目11. 阻止调用时,相比私有域声明不定义方式,优先使用函数删除的方式 === ===  » 条目11. 阻止调用时,相比私有域声明不定义方式,优先使用函数删除的方式 ===
-  * 删除默认构造函数与默认赋值重载函数:<code c++>Widget(const Widget& ) = delete;  +  * 删除默认构造函数与默认赋值重载函数:<code cpp>Widget(const Widget& ) = delete;  
 Widget& operator=(const Widget&) = delete;</code> Widget& operator=(const Widget&) = delete;</code>
   * 任何函数都可以被删除,包括非成员函数与模板。   * 任何函数都可以被删除,包括非成员函数与模板。
 ===  » 条目12. 声明覆盖函数时,使用修饰词 override === ===  » 条目12. 声明覆盖函数时,使用修饰词 override ===
   * 区分覆盖和重载的区别。   * 区分覆盖和重载的区别。
-  * c++11 比 c++98 对虚函数覆盖多了一条限定:覆盖函数的引用限定词必须与基类相同。+  * c++11 比 c++98 对虚函数覆盖多了一条限定:覆盖函数的引用限定词必须与基类相同。<code cpp> 
 +class Widget { 
 +public: 
 + … 
 + void doWork() &;      // this version of doWork applies only when *this is an lvalue 
 + void doWork() &&;     // this version of doWork applies only when *this is an rvalue 
 +};  
 +… 
 +Widget makeWidget();   // factory function (returns rvalue) 
 +Widget w;              // normal object (an lvalue) 
 +w.doWork();            // calls Widget::doWork for lvalues 
 +makeWidget().doWork(); // calls Widget::doWork for rval 
 +</code>
   * ''override'' 修饰词可规避因细微声明差异导致未覆盖。   * ''override'' 修饰词可规避因细微声明差异导致未覆盖。
 ===  » 条目13. 优先使用 const 迭代器 const_iterator === ===  » 条目13. 优先使用 const 迭代器 const_iterator ===
行 149: 行 161:
   * 引用计数的读写是原子性的,存在额外开销;   * 引用计数的读写是原子性的,存在额外开销;
   * ''std::shared_ptr'' 对象析构、构造时修改引用计数,但是移动构造与移动赋值直接省略了减一加一过程,比拷贝构造拷贝赋值要更有效率;   * ''std::shared_ptr'' 对象析构、构造时修改引用计数,但是移动构造与移动赋值直接省略了减一加一过程,比拷贝构造拷贝赋值要更有效率;
-  * ''std::shared_ptr'' 可自定义删除器,但删除器不作为其类型的一部分,而 ''std::unique_ptr'' 把删除器作为其类型的一部分:<code c++>+  * ''std::shared_ptr'' 可自定义删除器,但删除器不作为其类型的一部分,而 ''std::unique_ptr'' 把删除器作为其类型的一部分:<code cpp>
 // 自定义删除器 // 自定义删除器
 auto loggingDel = [](Widget *pw)  {      auto loggingDel = [](Widget *pw)  {     
行 170: 行 182:
   * 和''std::unique_ptr''不同, ''std::shared_ptr''设计之初就是针对单个对象的, 没有办法std::shared_ptr<T[]>, 不适用于直接指向数组, 应使用标准容器。   * 和''std::unique_ptr''不同, ''std::shared_ptr''设计之初就是针对单个对象的, 没有办法std::shared_ptr<T[]>, 不适用于直接指向数组, 应使用标准容器。
 === » 条目20 使用 std::weak_ptr 来指向可能悬空的共享资源 === === » 条目20 使用 std::weak_ptr 来指向可能悬空的共享资源 ===
-  * ''std::weak_ptr'' 从 ''std::shared_ptr'' 创建,但不参与共享资源的所有权引用计数 +  * ''std::weak_ptr'' 从 ''std::shared_ptr'' 创建,但不参与共享资源的所有权引用计数<code cpp> 
-  * ''wpt.expired()'' 可判断弱指针指向的资源是否已释放,即指针本身是否悬空(dangle); +auto spw = std::make_shared<Widget>(); // 引用计数+1 
-  * ''std::weak_ptr'' 本身不能解引用,要使用资源时,需使用''wpt.lock()'' 或直接构造''std::shared_ptr''来使用:<code c++>+std::weak_ptr<Widget> wpw(spw);        // 引用计数不变 
 +spw = nullptr;                         // 引用计数-1,资源被释放, wpw 悬空 
 +wpw.reset();                           // 引用计数不变, 置空 
 +</code> 
 +  * ''wpw.expired()'' 可判断弱指针指向的资源是否已释放,即指针本身是否悬空(dangle); 
 +  * ''std::weak_ptr'' 本身不能解引用,要使用资源时,需使用''wpw.lock()'' 或直接构造''std::shared_ptr''来使用:<code cpp>
 std::shared_ptr<Widget> spw1 = wpw.lock();  // 如果wpw过期,则 spw1 为 null std::shared_ptr<Widget> spw1 = wpw.lock();  // 如果wpw过期,则 spw1 为 null
 std::shared_ptr<Widget> spw3(wpw);          // 如果wpw过期,抛异常std::bad_weak_ptr std::shared_ptr<Widget> spw3(wpw);          // 如果wpw过期,抛异常std::bad_weak_ptr
行 180: 行 197:
     * 观察者列表:被观察者不关心观察者的死活,观察者注销不需要关心被观察者的列表注销;     * 观察者列表:被观察者不关心观察者的死活,观察者注销不需要关心被观察者的列表注销;
     * 避免std::shared_ptr环状结构:A、B两对象互相存有对方的shared_ptr指针,则永远不会被释放。     * 避免std::shared_ptr环状结构:A、B两对象互相存有对方的shared_ptr指针,则永远不会被释放。
 +=== » 条目21 比起new, 优先使用 std::make_unique 和 std::make_shared===
 +  * ''make''方式 比 ''new'' 方式提高了异常安全性:
 +    * 使用new 的版本''std::shared_ptr<Widget> pw(new Widget)'' 包含了两步操作:1.new 对象;2.构建指针
 +    * 考虑这个代码<code cpp>
 +processWidget(std::shared_ptr<Widget>(new Widget), computePriority());
 +</code> 由于没有规定函数实参构建顺序,编译器可能按如下顺序构建实参:
 +      - 执行 new Widget
 +      - 执行 computePriority()
 +      - 构造 std::shared_ptr
 +    * 此时,如果第二步 执行computePriority() 时产生了异常导致第三步未执行,那么第一步的 new 就产生了泄露。
 +  * 所以,如果使用 new 方式,应把 new 结果直接传递给智能指针构建,确保这中间没有其它语句。
 +  * ''std::make_shared'' 和 ''std::allocate_shared''方式生成的代码更快更小:对象与控制快内存一起分配、一起销毁。
 +  * 不适合使用 ''make'' 方式的情况: 
 +    - 需要自定义删除器; 
 +    - 希望用花括号初始化。
 +  * 不建议使用 ''std::shared_ptr'' 的其它情况
 +    - 有自定义内存管理的类;
 +    - 特别关注内存的系统;
 +    - 非常大的对象并且 ''std::weak_ptr'' 比对应的 ''std::shared_ptr''更长久(弱指针存在导致控制块不能销毁,导致make产生的对象内存块也没有销毁)。
 +  
 +=== » 条目22 使用 Pimpl 惯用法时,定义特制成员函数需要放在实现文件里===
 +  * **Pimpl**(pointer to implementation)惯用法,为了缩减编译时间而减少头文件依赖
 +  * 对于 ''std::unique_ptr'' pImpl 指针, 在头文件声明特制成员函数,实现则要在实现文件里定义,即使编译器默认实现是可接受的:<code cpp>
 +// 以下都在实现文件 widge.cpp 中:
 +Widget::~Widget() = default; // 防止报错
 +// 自定义析构导致拷贝与移动构造不能默认生成,手动生成需定义在实现文件
 +Widget::Widget(Widget&& rhs) = default; // 注意默认实现是浅拷贝,需深拷贝自己实现
 +Widget& Widget::operator=(Widget&& rhs) = default;
 +</code>
 +  * ''std::shared_ptr'' 指针没有上述限制,但不适用于 Pimpl 独享占有权语义。
 +
 +==== 章节5. 右值引用、移动语义和完美转发 ====
 +  * **形参永远是左值**,即使它的类型是右值引用(参考上方导言第三行)
 +=== » 条目23 理解 std::move 与 std::forward ===
 +  * ''std::move'' 无条件转换为一个右值,其本身并不移动任何东西:<code cpp>
 +// std::move 的简单实现:
 +template<typename T> // C++14; in namespace std
 +decltype(auto) move(T&& param)
 +{
 + using ReturnType = remove_reference_t<T>&&;
 + return static_cast<ReturnType>(param);
 +}</code>
 +  * 如果想移动实参,就不要把实参声明为 ''const'',  因为 const 实参会默认进入拷贝构造而不是移动构造
 +  * ''std::forward'' 仅当形参来源于右值实参时,才把它转为右值(作为下个函数的实参);
 +  * ''std::move'' 与 ''std::forward'' 在运行时 do nothing。
 +=== » 条目24 区分通值引用与右值引用 ===
 +  * 通值引用,两种情况:
 +    * 函数模板参数为 ''T&&''并且 ''T'' 需类型推导; 
 +    * 变量声明为 ''auto&&''
 +  * 如果类型声明格式不是标准的''type&&'',或者不存在类型推导,那么 ''type&&'' 为右值引用:<code cpp>
 +void f(Widget&& param);         // rvalue reference
 +Widget&& var1 = Widget();       // rvalue reference
 +auto&& var2 = var1;             // not rvalue reference
 +template<typename T>
 +void f(std::vector<T>&& param); // rvalue reference
 +template<typename T>
 +void f(T&& param);              // not rvalue reference
 +</code>
 +  * 通值引用如果是被右值初始化,那么就转为右值引用,左值则转为左值引用(详见类型推导)。
 +=== » 条目25 std::move 使用在右值引用,std::forward 使用在通值引用 ===
 +  * 要利用形参的右值性时,右值引用的形参使用 ''std::move'', 通值引用的形参使用 ''std::forward''
 +  * 一个函数内想多次利用同一个对象的右值性时,只在最后一次使用 ''std::move'', ''std::forward''(因为使用之后该对象就会失效)。
 +  * 按值返回的函数要返回右值引用或通值引用时,同样使用 ''std::move'' 或 ''std::forward''
 +  * 由于标准规定编译器存在返回值优化(return value optimization,RVO)以及在不优化场景下将 ''std::move'' 隐式应用于返回的局部对象,所以,不需要对要返回的局部对象(或值形参)手动使用 ''std::move'' 或 ''std::forward''
 +
 +=== » 条目26 避免重载通值引用 ===
 +  * 相比 ''int'' 重载,通值引用的函数会更匹配 ''short'' 参数: <code cpp>
 +template<typename T> void logAndAdd(T&& name){} // origon_func
 +void logAndAdd(int idx){} // overload_func
 +
 +short index;
 +logAndAdd(index); //Error! it match origon_func, not overload_func
 +</code> 通值引用的重载,会让通值引用比预期中更频繁得被调用。
 +   * 完美转发构造函数尤其如此,因为对于非const左值来说,它们通常比复制构造函数(复制构造函数参数声明为const)更匹配,而且它们可以劫持派生子类调用基类的复制与移动构造函数,转而让子类调用完美转发构造函数。
 +=== » 条目27 熟悉「重载通值引用」之外的替代方法 ===
 +
 ==== 翻译对照 ==== ==== 翻译对照 ====
 <WRAP tablewidth 50% > <WRAP tablewidth 50% >
-^ 英                         ^ 汉       ^ 备注                                +^ 英                        ^ 汉            ^ 备注                              
-| argument                  | 实参      |                                   | +| argument                  | 实参          |                                   | 
-| parameter                 | 形参/参数   |                                   |+| parameter                 | 形参/参数     |                                   |
 | basic guarantee           | 基本异常保障  | basic exception safty guarantee   | | basic guarantee           | 基本异常保障  | basic exception safty guarantee   |
-| strong guarantee          | 强异常保障   | strong exception safty guarantee +| strong guarantee          | 强异常保障    | strong exception safty guarantee 
-| built-in pointer          | 内置指针    | 相对于智能指针的原指针概念                     +| built-in pointer          | 内置指针      | 相对于智能指针的原指针概念        
-| raw pointer               | 原生指针                                       +| raw pointer               | 原生指针                                        
-| universal reference       | 通值引用    | 相对“左值引用”、“右值引用”来说                 +| universal reference       | 通值引用      | 相对“左值引用”、“右值引用”来说    
-| scoped enums              | 限定域枚举   |                                   |+| scoped enums              | 限定域枚举    |                                   |
 | unscoped enums            | 非限定域枚举  |                                   | | unscoped enums            | 非限定域枚举  |                                   |
-| forward-declared          | 前置声明    |                                   | +| forward-declared          | 前置声明      |                                   | 
-| override                  | 覆盖      |                                   | +| override                  | 覆盖                                            | 
-| maximally  generic  code  | 最泛化代码   |                                   |+| overload                  | 重载          |                                   | 
 +| maximally  generic  code  | 最泛化代码    |                                   |
 | special member function   | 特制成员函数  |                                   | | special member function   | 特制成员函数  |                                   |
-|                                   |                                   | +|                                         |                                   | 
-|                                   |                                   | +|                                         |                                   | 
-|                                   |                                   | +|                                         |                                   | 
-|                                   |                                   |+|                                         |                                   |
 </WRAP> </WRAP>
  • public/it/cplusplus/effective_modern_cplusplus.1657078355.txt.gz
  • 最后更改: 2022/07/06 11:32
  • oakfire