读了《C++ 的门门道道 | 技术头条》这篇文章以后有不少共鸣,能够说是近期看过的最好的技术 tips 文章了。按照这篇文章里面讲到的几点,结合工做上实际遇到的问题,我也来讲一下个人感觉。html
<!-- more -->c++
成员变量忘了初始化是一个至关经典的错误,甚至《Effective C++》中还专门列了一条来说这个事情。在工做中,我就看到过这种错误,同事对一个新增的功能加上了开关控制的逻辑,可是忘了对这个开关的标识进行初始化,致使了一条分支逻辑失效。并且由于 C++ 没有默认初始值,那它的初始值是随机的,因此致使线上的表现是几率性复现,增长了 debug 的难度。固然增长 Coverity 扫描提前发现这个问题,当时项目上线比较急就直接跳过了这一步。程序员
从 C++11 开始支持在声明成员变量的时候直接初始化,有了这个特性以后,我已经养成了全部成员变量都直接在声明的时候初始化。算法
class Ad { private: unsinged int lifetime = 10000; };
这个坑即使是有点经验的程序员也会踩到,有一次线上事故就是一个稳定跑了好久的逻辑,忽然出现了 core,并且是持续地 core 在 sort 上。花了很长时间排查,最后才意识到实现新增的 sort 比较函数没有保证严格弱序(strict weak order),比较两个对象的属性时用了 <=
。这里就涉及到 C++ 中 sort 的实现。细节以后会写一篇文章来说,简单说来就是 STL sort 核心排序算法是快排,在依据 pivot 调整元素位置时采用的实现方式以下:shell
while (true) { while (__comp(*__first, __pivot)) ++__first; --__last; while (__comp(__pivot, *__last)) --__last; if (!(__first < __last)) return __first; std::iter_swap(__first, __last); ++__first; }
重点就在于 while (__comp(*__first, __pivot)) ++__first;
,当整个容器里的元素都相等时,就会致使 __first 这个迭代器越界,程序就 core 了。函数
原文关于如何避免操做符短路讲得很好了,其实咱们还能够利用操做符短路来简化代码。对我更经常使用的场景:if (!stack.empty() && stack.top() == 0)
,这偏偏是利用短路来合并判断。性能
这个有个经典场景,在 vector 里面,我要找到首个递增序列的最后一个元素,很容易写成这样的代码:优化
while (i < ve.size() - 1 && ve[i] <= ve[i + 1]) i++;
这里若是传入的 ve 是个空 vector,那么就会成为超大循环,由于 vector::size()
返回的是 unsigned int
,根据数值类型传递,ve.size() - 1
的类型也是 unsigned int
,那么就会返回一个很大的数,致使 while
陷入超大循环。spa
vector 能够说是在平常开发中使用频率最高的容器了,支持下标访问,动态扩容,二分查找的效率,C++11 以后支持移动构造,这些优势都让它很是好用。vector 的坑都集中在它的动态扩容上,理解它动态扩容的机制能够在开发中避开这些坑。debug
vector 动态扩容的两个特色:
根据这两个特色,结合 vector 的其余特性获得的:
组里有一个项目升级到 C++11 以后,一窝蜂地使用 unordered_map,可是其实对于小数据量,好比本次请求命中的一些配置,其实数据量基本都在 10 项之内,那其实用 map 就彻底够用了,unorder_map 查找的效率固然是高的,可是也要认识到它维护一个哈希表额外付出的性能代价。
由于一开始设计的数值类型过于严格而致使的重写,我遇到过不止一次了。
有些人写代码的时候有一种倾向,就是能省则省,能用 int 的毫不用 long,能用 short 的毫不用 int。可是其实有些状况下 short 并不能节省空间(字节对齐),还致使过分「优化」,致使逻辑变化以后要重写,或者实际的取值不符合设计致使溢出。
什么是「箭头型代码」?见下图:
这种代码其实在业务复杂的场景下并很多见,酷壳上有一篇文件专门讲过如何重构这种代码:《如何重构“箭头型”代码》。
我在实际项目中应用比较可能是利用 while (0) 来规避这种代码。
在项目常常遇到的场景是对一连串条件进行判断,不符合条件的分支须要打印日志,示例代码以下:
if (conditionA()) { if (conditionB()) { if (conditionC()) { if (conditionD()) { // do something } else { // log } } else { // log } } else { // log } } else { // log }
这种状况下用 do-while(0)
能够进行很是好的重构,重构以后的代码以下:
do { if (!conditionA()) { // log break; } if (!conditionB()) { // log break; } if (!conditionC()) { // log break; } if (!conditionD()) { // log break; } } while (0);