前言¶
更新于: 2018.03.16
C++的变化¶
相对于C, C++是一个变化很快的语言, 当然与其复杂性也有关系. 和我一样, 可能很多人对C++的印象和理解还停留在C++98标准, 即第一个ISO C++标准. 然而, 从那时候开始, 标准委员会又做了很多工作, 对C++标准进行了多次更新. 从C++98到目前的更新包括:
- 1998 ISO C++标准发布
- 2002 C++0x, 一个修订版本
- 2003 C++0x 错误修正版, 引入正则表达式, 无序容器, 资源管理指针
- 2006 ISO C++性能技术报告发布, 重要是与嵌入式系统开发相关的问题
- 2009 C++0x特性完成, 引入统一初始化, 移动语义, 可变模板参数, lambda表达式, 类型别名, 一种适合并发的内存模型及其他新特性. 标准库增加了线程, 锁机制等
- 2011 C++11标准发布
- 2012 第一个完整的C++11实现出现
- 2014 C++14标准发布
- 2017 C++17标准发布
如果平时工作没有用到新特性, 或有兴趣对这些更新进行关注, 那么看到以新标准编写的C++代码甚至会有一种看新语言的错觉, 正如我看某些hyperscan源码一样.
本系列文章不打算涉及C++的所有特性, 这个看<<C++程序设计语言>>这本书就行了; 也不打算涉及C++11以后的特性, 只是帮助对C++98熟悉的开发人员学习C++11的新特性, 尤其是需要改变C++98使用老习惯的部分. 当然, 可能还是会说一下C++中非常重要, 但又非常难掌握的部分.
下面我打算引用Bjarne Stroustrup在<<C++程序设计语言>>第四版中对C++开发人员和C开发人员的建议, 理解这些建议会对学习C++很有帮助.
对C++程序员的建议¶
- 使用构造函数建立不变式(invariant)
- 配合使用构造/析构函数来简化资源管理
- 避免裸的new和delete
- 使用容器和算法而不是内置数组和专用代码
- 优先使用标准库特性而不是自己开发的代码
- 使用异常而非错误代码来报告不能局部处理的错误
- 使用移动语义来避免拷贝大对象
- 使用unique_ptr来引用多态类型的对象
- 使用shared_ptr来引用共享对象, 即不止有一个所有者负责析构的对象
- 使用模板来保持静态类型安全(消除类型转换)并避免类层次的不必要使用
对C程序员的建议¶
程序员对C掌握得越好, 似乎就越难避免用C风格编写C++程序, 从而失去了C++的很多潜在优势.
- 不要将C++看作增加了一些特性的C++. 你可以这样来使用C++, 但这将导致次最优的结果.为了真正发挥C++相对于C的优势, 你需要采用不同的设计和实现风格
- 不要用C++来写C程序: 这通常会令可维护性和性能都非常不好
- 将C++标准库作为学习新技术和新程序设计风格的老师. 注意它与C标准库的差异
- C++几乎从来不需要宏替换. 作为替代, 使用const, constexpr, enum或enum class来定义明示常量; 使用inline来避免函数调用的开销; 使用template来指明函数族和类型族; 使用namespace来避免名字冲突
- 在真正需要一个变量时再声明它, 且声明后立即进行初始化. 声明可以出现在语句可以出现的任何位置, 以及for语句初始化部分和条件中
- 不要使用malloc(). new运算符可以完成相同的工作, 而且完成得更好. 同样, 不要使用realloc(), 尝试用vector. 但注意不要简单地用”祼的”new和delete来代替malloc()和free()
- 避免使用void*, 联合以及类型转换, 除非在某些函数和类的深层实现中. 使用这些特性会限制你从类型系统中得到支持, 而且会损害性能. 在大多数情况下, 一次类型转换就暗示着一个设计错误. 如果你必须使用显式类型转换, 尝试使用命名的显式类型转换(如static_cast), 这能更精确地表达你的意图
- 尽量减少数组和C风格字符串的使用. 与这种传统的C风格代码比, 通常可以用C++标准库中的string, array和vector写出更简单也更易维护的代码. 一般而言, 如果标准库中已经提供了相应的功能, 就尽量不要自己重新构造代码
- 除非是在非常专门的代码中(例如内存管理器), 或者进行简单的数组遍历(例如++p), 否则要避免对指针进行算术运算
- 不要认为用C风格(回避诸如类, 模板和异常等C++特性)辛苦写出的代码会比一个简短的替代程序(例如用标准库特性写出的代码)更高效. 实际情况通常正好相反
C++11主要特性¶
下面是Bjarne Stroustrup列出的他认为用处最广的一些C++11特性:
- 默认操作的控制, =delete和=default
- 从初始化器推断对象的类型, =auto
- 推广的常量表达式求值, constexpr
- 类内成员初始化器
- 继承的构造函数
- lambda表达式, 一种在表达式中隐式定义函数对象以供使用的方法
- 移动语义, 一种零拷贝传输信息的方法
- noexcept, 一种声明函数不能抛出异常的方法
- nullptr, 空指针的一个更合适的名字
- 范围for语句
- final和override, 覆盖控制
- 类型别名, 有时类似于typedef, 特别是一种通过绑定另一个模板的某些实参来定义一个新模板的方法
- enum class, 类型和限定作用域的枚举
- 通用和统一的初始化, 包括任意长度的初始化器列表和防止窄化转换
- 可变参数模板, 一种向模板传递任意数量任意类型实参的机制
参考¶
- Bjarne Stroustrup, C++程序设计语言(第4版)
- http://zh.cppreference.com/w/cpp/language/history