《Effective Modern C++》笔记2

本文是《Effective Modern C++》笔记的后半部分,前半部分在 《Effective Modern C++》笔记

第五章 右值引用、移动语义和完美转发

条款23:理解std::move和std::forward

  • std::move实施的是无条件的向右值型别转换。就其本身而言,它不会执行移动操作

  • 仅当传入的实参被绑定为右值时,std::forward才针对该实参实施向右值型别的强制型别转换

  • 在运行期,std::move和std::forward都不会做任何操作

条款24:区分万能引用和右值引用

  • 如果函数模板形参具备T&&型别,并且 T 的型别系推导而来,或如果对象使用auto&&声明其型别,则该形参或对象就是个万能引用

  • 如果型别声明并不精确地具备type&&的形式,或者型别推导并未发生,则type&&就代表右值引用

  • 若采用右值初始化万能引用,就会得到一个右值引用。若采用左值初始化万能引用,就会得到一个左值引用

条款25:针对右值引用实施std::move,针对万能引用实施std::forward

  • 针对右值引用的最后一次使用实施std::move,针对万能引用的最后一次使用实施std::forward

  • 作为按值返回的函数的右值引用和万能引用,依上一条所述采取相同行为

  • 若局部对象可能适用于返回值优化,则请勿针对其实施std::move或std::forward

条款26:避免依万能引用型别进行重载

  • 把万能引用作为重载候选型别,几乎总会让该重载版本在始料未及的情况下被调用

  • 完美转发构造函数的问题尤为严重,因为对于非常量的左值型别而言,它们一般都会形成相对于复制构造函数的更佳匹配,并且它们还会劫持派生类中对基类的复制和移动构造函数的调用

条款27:熟悉依万能引用型别进行重载的替代方案

  • 如果不使用万能引用和重载的组合,则替代方案包括使用彼此不同的函数名字、传递const T&型别的形参、传值和标签分派

  • 经由std::enable_if对模板施加限制,就可以将万能引用和重载一起使用,不过这种技术控制了编译器可以调用到接受万能引用的重载版本的条件

  • 万能引用形参通常在性能方面具备优势,但在易用方面一般有劣势

条款28:理解引用折叠

  • 引用折叠会在四种语境中发生:模板实例化,auto型别生成,创建和运用typedef和别名声明,以及decltype

  • 当编译器在引用折叠的语境下生成引用的引用时,结果会变成单个引用。如果原始的引用中有任一为左值引用,则结果为左值引用,否则为右值引用

  • 万能引用就是在型别推导的过程会区别左右值,以及会发生引用折叠的语境中的右值引用

条款29:假定移动操作不存在、成本高、未使用

  • 假定移动操作不存在、成本高、未使用

  • 对于那些型别或对于移动语义的支持情况已知的代码,则无需作以上假设

条款30:熟悉完美转发的失败场景

  • 完美转发的失败情形,是源于模板型别推导失败,或推导结果是错误的型别

  • 会导致完美转发失败的实参种类有大括号初始化物、以值0或 NULL 表达的空指针、仅有声明的整型 static const 成员变量、模板或重载的函数名字以及位域

小结

本章主要介绍的是移动语义和完美转发,右值引用是他们得以实现的底层语言机制,在多数情况下完美转发是很好用的,不过后面几个小节的内容比较枯燥,看的也不是很明白,需要进一步好好消化。

第六章 lambda 表达式

条款31:避免默认捕获模式

  • 按引用的默认捕获会导致空悬指针问题

  • 按值的默认捕获极易受到空悬指针影响(尤其是 this),并会误导人们认为 lambda 式是自洽的。

  • 在C++14中更好的捕获方法是使用广义 lambda 捕获

条款32:使用初始化捕获将对象移入闭包

  • 使用 C++14 的初始化捕获将对象移入闭包

  • 在 C++11 中,经由手工实现的类或std::bind去模拟初始化捕获

条款33:对auto&&型别的形参使用decltype,以std::forward之

  • 对auto&&型别的形参使用decltype,以std::forward之

 auto f=
     [](auto &&param)
 {
     return
         func(normaliaze(std::forward<decltype(param)>(param)));
 };

条款34:优先选用 lambda 式,而非std::bind

  • lambda 式比起使用std::bind而言,可读性更好、表达力更强,可能运行效率也更高

  • 仅在 C++11中,std::bind在实现移动捕获,或是绑定到具备模板化的函数调用运算符的对象的场合中,可能尚有余热可以发挥

小结

本章主要介绍了几个使用 lambda 表达式的时候需要注意的问题以及 lambda 表达式的优势,在现代 C++ 中应该掌握并熟练使用。

第七章 并发 API

条款35:优先选用基于任务而非基于线程的程序设计

  • std::thread的 API 未提供直接获取异步运行函数返回值的途径,而且如果哪些函数抛出异常,程序就会终止

  • 基于线程的程序设计要求手动管理线程耗尽、超订、负载均衡,以及新平台适配

  • 经由应用了默认启动策略的std::async进行基于任务的程序设计,大部分这类问题都能找到解决之道

条款36:如果异步是必要的,则指定std::launch::async

  • std::async的默认启动策略既允许任务以异步方式执行,也允许任务以同步方式运行

  • 如此的弹性会导致使用thread_local变量时的不确定性,隐含着任务可能永远不会执行,还会影响运用了基于超时的 wait 调用的程序逻辑

  • 如果异步是必要的,则指定std::launch::async

条款37:使std::thread型别对象在所有路径皆不可联结

  • 使std::thread型别对象在所有路径皆不可联结

  • 在析构时调用join可能导致难以调试的性能异常

  • 在析构时调用detach可能导致难以调试的未定义行为

  • 在成员列表的最后声明std::thread型别对象

条款38:对变化多端的线程句柄析构函数行为保持关注

  • 期值的析构函数在常规情况下,仅会析构期值的成员变量

  • 指涉到经由std::aysnc启动的未推迟任务的共享状态的最后一个期值会保持阻塞,直至该任务结束

条款39:考虑针对一次性事件通信使用以 void 为模板型别实参的期值

  • 如果仅为了实现平凡事件通信,基于条件变量的设计会要求多余的互斥量,这会给相互关联的检测和反应任务带来约束,并要求反应任务校验事件确实发生

  • 使用标志位可以避免上述问题,但这一设计基于轮询而非阻塞

  • 条件变量和标志位可以一起使用,但这样的通信机制设计结果不够自然

  • 使用std::promise型别对象和期值就可以回避这些问题,但是为了共享状态需要使用堆内存,而且仅限于一次性通信

条款40:对并发使用std::atomic,对特种内存使用volatile

  • std::atomic用于多线程访问的数据,且不用互斥量。它是撰写并发软件的工具。

  • volatile用于读写操作不可以被优化掉的内存。它是在面对特种内存时使用的工具

小结

本章主要介绍并发编程相关操作,由于第一次接触,看起来迷迷糊糊的,后续用到再回头看看

第八章 微调

条款41:针对可复制的形参,在移动成本低并且一定会被复制的前提下,考虑将其按值传递

  • 对于可复制的、在移动成本低廉的并且一定会被复制的形参而言,按值传递可能会和按引用传递的具备相近的效率,并可能生成更少量的代码

  • 经由构造复制形参的成本可能比经由赋值复制形参高出很多

  • 按值传递肯定会导致切片问题,所以基类型别特别不适用于按值传递

条款42:考虑置入而非插入

  • 从原理上说,置入函数应该有时比对应的插入函数高效,而且不应该有更低效的可能

  • 从实践上说,置入函数在以下几个前提成立时,极有可能运行的更快:

  1. 待添加的值是以构造而非赋值方式加入容器

  1. 传递的实参型别与容器持有之物的型别不同

  1. 容器不会由于存在重复值而拒绝待添加的值

  • 置入函数可能会执行在插入函数中被拒绝的型别转换

小结

本章主要介绍按值传递和置入使用时的适用场景,在使用过程中要格外注意。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值