乐趣区

不知道C这七大特性绝对枉为圈中人

作为一种计算机语言,C++ 经历了许多发展变化。

当然,这些改变并不是一蹴而就的。C++ 曾经缺乏活力与创新,因此很不受欢迎。

但是在 C ++ 标准委员会决定加速发展这个语言之后,形势发生了改变。

2011 年起,C++ 一跃成为了具有活力、不断演进、广受喜爱的计算机语言。
最后,如果大家如果在自学遇到困难,想找一个 C ++ 的学习环境,可以加入我们的 C ++ 学习圈,点击我加入吧,会节约很多时间,减少很多在学习中遇到的难题。


C++ 蜕变后也并没有简单多少,仍是最难的编程语言之一。但是,C++ 确实比之前更加人性化了。

本文要讲的是的 C ++ 的一些新特性(以有 8 年历史的 C ++11 为例),相信每个程序员都会对这个话题感兴趣。

注:本文略过了一些高级特性。

  1. 关键字 auto

当 C ++11 第一次引入 auto 时,程序员们纷纷喜极而泣!

auto 的意义是使 C ++ 编译器可以在编译时推导数据类型,这样就不用每次都要声明数据类型了。当数据类型为 map<string,vector<pair<int,int>>> 时尤为便捷。

不知道 C ++ 这七大特性,绝对枉为圈中人

没有 initializer,就无法声明数据类型(见第五行)。这是说得通的。第五行指令并没有让编译器推导数据类型。

起初,auto 的功能比较有限。在之后新版本的 C ++ 中,auto 的功能越来越强大。

不知道 C ++ 这七大特性,绝对枉为圈中人

第七行和第八行中使用了括号初始化 (bracketedinitialization),这也是 C ++11 的新特性之一。

请注意使用 auto 时,编译器必须能够推导数据类型。

一个有趣的问题是:如果写下 autoa = {1, 2, 3} 会发生什么?这是个编译错误吗?是一个矢量吗?

不知道 C ++ 这七大特性,绝对枉为圈中人

实际上,C++11 引入了 std::initializer_list<type>. 如果声明 auto,花括号初始化列表会被当做轻量级容器。

最终,正如前文所言,当数据结构复杂时,编译器类型推导很有帮助:

不知道 C ++ 这七大特性,绝对枉为圈中人

别忘了检查第 25 行!auto [v1,v2] = itr.second 纯粹是 C ++17 的新特性。这个特性叫做结构化绑定。在旧版本 C ++ 中,程序员需要单独获取每个变量。但是结构化绑定给这一过程带来了便利。此外,如果想获得数据使用引用 (reference),只需要加上一个 symbol–auto&[v1,v2] = itr.second.

  1. Lambda 表达式

C++11 引入了 lambda 表达式,这类似于 JavaScript 里的匿名函数。它们都是函数对象,没有名字,且基于简洁的语法在不同作用域上捕获变量。它们也可以被分配给变量。

如果需要在代码中进行一些小而快的操作,又不愿意为此单独写一个函数,那么 Lambdas 很有用。另一种常见用法是将 lambdas 作为比较函数。

不知道 C ++ 这七大特性,绝对枉为圈中人

以上例子可以说明很多问题。

首先,请注意花括号初始化是如何提升权重的。然后是通用的 begin(),end()(这也是 C ++11 的新增部分)。接着是作为数据比较器的 lambda 函数。lambda 函数的参数被声明为 auto(这是 C ++14 的新增部分)。在 C ++14 之前是不能对于函数参数使用 auto 的。

正如现代 C ++ 的 awesome 库中定义的那样:

· []—不捕获任何对象。所以不能在 lambda 表达式内使用全局作用域的局部变量,只能使用参数。

· [=]— 按值捕获作用域中的局部对象(局部变量,参数)。只可使用不可修改。

· [&]—按引用捕获作用域中的局部对象(局部变量,参数)。可以被修改。例子如下。

· [this]—按值捕获 this 指针。

· [a, &b]—按值捕获对象 a,按引用捕获对象 b。

所以,如果想在 lambda 函数内部将数据转换为其他格式,可以利用作用域的优势来运用 lambda. 比如:

不知道 C ++ 这七大特性,绝对枉为圈中人

在上面这个例子中,如果在 lambda 表达式中按值捕获([factor])局部变量,则不能改变第五行的 factor. 原因很简单——没有权限。

最终,请注意示例中使用了 val 作为引用 (reference). 这确保了 lambda 函数内部的任何变化都会改变 vector.

不知道 C ++ 这七大特性,绝对枉为圈中人

学完现代 C ++ 后,她们乐开了花!(摄影:Ian Schneider 图源:Unsplash)

  1. if/switch 内的初始化语句

C++17 的这个特性十分讨喜:

不知道 C ++ 这七大特性,绝对枉为圈中人

很明显,现在可以同时在 if/switch 句块内进行变量初始化和条件检查。这有助于保持代码简洁精炼。通用形式为:

if(init-statement(x);condition(x)) {
// do some stuff here
} else {
// else has the scope of x
// do some other stuff
}

  1. 在编译时使用 constexpr

constexpr 很棒!假如要评估一些表达式,且它的值一旦初始化就不会改变,那么可以预运算其值并将之作为宏。或者利用 C ++11 提供的 constexpr.

程序员倾向于尽量减少程序运行时间。所以,如果能让编译器进行一些操作并减小程序运行的压力,那么就可以缩短运行时间。

不知道 C ++ 这七大特性,绝对枉为圈中人

以上代码是 constexpr 的常见例子之一。既然声明斐波那契数列函数为 constexpr,那么编译器就可以在编译时预运算 fib(20). 所以编译之后,可以用 constlong long bigval = 2432902008176640000 来替代 const longlong bigval = fib(20).

请注意,传递参数是一个 const 值。这是被声明为 constexpr 的函数的一个重点——传递参数应该是 constexpr 或 const。否则这里的函数会和普通函数一样,也就是说编译时不进行预运算。

变量也可以是 constexpr. 在这种情况下,这些变量在编译时必须可评估;否则会出现编译错误。

有趣的是,后来在 C ++17 中引入了 constexpr-if 和 constexpr-lambda.

  1. Tuples 元组

与 pair 非常类似, tuple 是各种数据类型的固定大小值的集合。

不知道 C ++ 这七大特性,绝对枉为圈中人

有时,相比于 tuple,使用 std::array 更方便。array 类似于带有 C ++ 标准库的功能的 plain C 阵列。这个数据结构是 C ++11 新增的。

  1. 类模板参数推导

这个特性的名字还挺啰嗦。从 C ++17 开始,标准类模板也可以进行模板参数推导。之前,模板参数推导只支持函数模板。结果就是:

std::pair<std::string,int> user = {“M”, 25}; // previous
std::pair user = {“M”, 25}; // C++17

这种推导是“隐性的”。这对于 tuple 来说就更方便了。

// previous
std::tuple<std::string, std::string, int> user (“M”,”Chy”, 25);
// deduction in action!
std::tuple user2(“M”, “Chy”, 25);

以上这一特性对不熟悉 C ++ 模板的人来说没有太大用处。

  1. 智能指针

指针有时很恐怖。由于 C ++ 语言为程序员提供了很大程度的自由,所以有时很容易搬起石头砸自己的脚。而且很多情况下,麻烦是由指针造成的。

幸运的是,C++11 引入了智能指针,智能指针比普通指针便捷得多。它们通过适时释放内存来帮助程序员防止内存泄漏。它们还有助于代码达到异常安全等级。

C++ 为最新版本的计算机语言引入了许多全新的特性。如果你感兴趣,可以进行深入了解。

不知道 C ++ 这七大特性,绝对枉为圈中人

退出移动版