博客 阅读时间 5 分钟

增强您的 C++ 项目的 15 个必备 C++11 特性

分享本文
15 Essential C++11 Features to Enhance Your C++ Projects.

近年来,关于“C++ 复兴”的讨论不绝于耳。不可否认,微软在这次复兴中扮演了重要角色。

2011 年,微软在许多文章中宣告了 C++ 的回归,Herb Sutter 等微软 C++ 专家也做了许多演讲,解释 C++ 为何回归,并主要推荐现代 C++ 的用法。与此同时,C++11 标准获得批准,人们开始把 C++ 当作一门新语言来谈论。

到 2011 年,C++ 已经被使用了 30 多年。要让开发者相信现代 C++ 确实简化了使用 C++ 时许多令人沮丧的方面、并且已经有了一种改善 C++ 代码的现代方式,并不容易。

对于尚未切换到 C++11 的 C++ 开发者:您可以使用 clang-tidy 来获得代码库现代化改造的建议,或者试试CppDepend 的现代化功能。下面是开源 C++ 库中使用最广泛的 15 个特性。

让我们以Folly为例——它由 Facebook 于六年前发布:Folly 是一个庞大的可复用 C++ 库组件集合,在 Facebook 内部被广泛使用。下面是它的网站对其诞生动机的说明:

Folly(大致是 Facebook 开源库的缩写)是一个以实用性和效率为设计导向的 C++11 组件库。它是对 Boost 等库的补充(而非竞争),当然也包括std。事实上,只有当我们需要的东西不存在、或不满足所需的性能要求时,我们才会着手定义自己的组件。

让我们从它的源代码中探究 15 个 C++11 特性:

1. auto

C++11 引入了使用 auto 关键字的类型推导能力,这意味着编译器在变量声明处推导其类型。Folly 几乎所有的变量声明都使用 auto;下面是它源代码中的一个例子。

c1

使用 auto 关键字,可以让您少花时间写出那些编译器本来就知道的东西。

2. nullptr

常量 0 长期以来一直扮演着整数常量和空指针常量的双重角色。C++11 通过引入一个新关键字作为专门的空指针常量纠正了这一点:nullptr。

在 Folly 源代码中,所有空指针都用新关键字 nullptr 表示;没有任何地方使用常量 0。

3. shared_ptr

智能指针并不是一个新概念;许多库多年前就实现了它们,最流行的当属boost::shared_ptr。新鲜的是它们的标准化——再也不需要使用外部库来使用智能指针了。

Folly 大量使用标准化的共享指针;它的源代码中只剩下少数裸指针。

4. 强类型枚举

C++ 中的“传统”枚举会把它们的枚举值导出到外围作用域,如果同一作用域中的两个不同枚举定义了同名的枚举值,就可能导致命名冲突。

C++11 引入了 enum class 关键字。它们不再把枚举值导出到外围作用域。而且,我们现在还可以指定枚举的底层类型。

c2

5. static assert

C++11 引入了使用新关键字 static_assert 在编译期测试断言的新方式。这一特性非常适合为模板参数添加约束,正如下面的 Folly 源代码中的模板类所示:

c3

6. 可变参数模板

可变参数模板是可以接受任意数量、任意类型模板参数的模板。类和函数都可以是可变参数的。Folly 定义了许多可变参数模板;下面是 Folly 源代码中的两个可变参数模板函数:

c4

7. 基于范围的 for 循环

C++11 增强了 “for” 语句,以支持遍历集合的 “foreach” 范式。它让代码更简单、更干净。Folly 大量使用这一特性;下面是 Folly 的一个例子。

c6

8. 初始化列表

在 C++03 中,初始化列表只适用于数组;在 C++11 中,它们不再只服务于数组。接受{}列表的机制,是一个(通常是构造函数的)函数接受std::initializer_list<T>类型的参数。下面是一个接受 std::initializer_list 作为参数的函数的例子。

c7

下面是它的调用方式。

c8

9. noexcept

如果一个函数不会抛出异常,或者程序并未打算处理函数抛出的异常,那么该函数可以声明为noexcept。

下面是 Folly 源代码中的一个例子。

c9

10. move(移动语义)

C++11 引入了右值引用的概念(用 && 表示),以区分左值引用和右值引用。左值是有名字的对象,而右值是没有名字的对象(临时对象)。移动语义允许修改右值。

为此,C++11 引入了两个新的特殊成员函数:移动构造函数移动赋值运算符

c12c10

这里有一份很好的文档,更好地解释了移动语义的好处。

11. lambda

C++11 提供了创建匿名函数的能力,称为lambda 函数

Folly 在许多函数中使用它们;下面是它源代码中的一个例子:

c14

12. 显式默认化和删除特殊成员函数

在 C++03 中,对于自身没有定义这些函数的类,编译器会提供默认构造函数、复制构造函数、复制赋值运算符(operator=)和析构函数。程序员可以通过定义自定义版本来覆盖这些默认函数。

然而,对这些默认函数的生成几乎没有控制权。例如,要让一个类天生不可复制,需要声明一个私有的复制构造函数和复制赋值运算符,而不给出定义。

在 C++11 中,某些特性可以被显式禁用。例如,下面的类型是不可复制的,这让代码更简单、更干净。

c15

13. override 标识符

在 C++03 中,当您本意是重写基类函数时,可能会意外地创建一个新的虚函数。

override特殊标识符意味着编译器会检查基类,看是否存在具有完全相同签名的虚函数。如果不存在,编译器将报错。

Folly 大量使用这一新特性:

c18

14. std::thread

标准提供了一个线程类(std::thread),它接受一个函数对象——以及一组可选的要传递给它的参数——在新线程中运行。

在 C++11 中,使用线程更简单了;下面是定义新线程的新标准方式,取自 Folly 源代码:

c20

15. 无序容器

无序容器是一种哈希表。C++11 提供了四种标准无序容器:

  • unordered_map
  • unordered_set
  • unordered_multimap
  • unordered_multiset

Folly 在许多地方使用了这些新容器。

c21
分享本文