每个开发者都应该了解的一些C特性

C++是一种强大的编程语言,但也因为其复杂性一直让用户望而却步。后来,C++决定做出改变,然后发展至今,成了编程社区最受欢迎的语言之一。C++有一些新特性非常好用,本文对此进行了介绍,比如auto、lambda、constexpr、tuple、智能指针等。

作为一门编程语言,C++已经进化了很多。

当然,这些改变不是一夜之间发生的。曾几何时,C++缺乏活力,导致人们不太喜欢这门语言。

但是,当C++标准委员会决定加快转变时,情况就不同了。

自年以来,C++已经成为一种不断发展的动态语言,而这正是很多人所期许的。

不要误以为是这门语言变得简单了,实际并没有。它仍然是被广泛使用的最难编程语言之一。但是相比于之前的版本,确实对用户更加友好了。

今天,我们深入发掘一下每位开发者都应该了解的新特性(这些新特性从C++11时开始出现,距今已有八年历史了)。注意,本文略过了一些高级特性,可能会在以后的内容中详细探讨。

auto概念

当C++11第一次引入auto,一切都变得更简单了。

auto的概念是让c++编译器在编译时自动推断数据的类型,而不是每次都要求你手动声明类型。如果你的数据类型是mapstring,vectorpairint,intstring,vectorpair这样的,事情会变得非常方便。/string,vectorpair

看一下第五行。没有initializer时你不能声明某些东西,这不难理解。像第五行这样,编译器是无法推断数据类型的。

最初,auto的使用是非常受限的。在之后的版本中,auto变得更加强大!

第7和第8行中,我使用了花括号初始化。这个特性也是C++11中新加入的。

记住,当使用auto时,必须确保你的编译器可以通过某种方式推断数据类型。

现在问题来了,如果我写autoa={1,2,3}会发生什么?会有编译错误吗?这是向量吗?

实际上,C++11引入了std::initializer_listtype,如果声明为auto,那么初始化列表会被认为是这种轻量级容器。

最后,就像前面提到的,当你使用复杂的数据类型时,编译器推断数据类型会非常有用。

不要忘记查看第25行!表达式auto[v1,v2]=itr.second是C++17的新特性。这被称为结构化绑定。在之前的版本中,每个变量必须要分别进行提取,然而结构化绑定会使这个过程方便很多。

另外,如果你想通过引用获取数据,只需要添加一个像auto[v1,v2]=itr.second这样的符号,非常简洁。

lambda表达式

C++11引入了lambda表达式,该表达式和JavaScript中的匿名函数非常相似。它们是没有命名的函数对象,并且基于一些简洁的语法在不同的作用域捕获变量,它们还可以分配给变量。

当你想在代码中快速实现一些小功能但并不想为此单独编写整个函数时,lambda非常有用。另一种非常普遍的应用是将其作为比较函数。

上面的例子中有很多细节。

首先,要注意到列表初始化为你节省了多少代码。然后是通用的begin()和end(),它们同样也是C++11中新添加的。然后是作为数据比较器的lambda函数。lambda函数的参数被声明为auto,这是c++14中新增的。在此之前,是不可以用auto作为函数参数的。

这里使用方括号[]作为lambda表达式的开始。它定义了lambda函数的作用域,即它对局部变量和对象有多少权限。

下面是一些现代c++中的相关定义:

[]代表空。因此你不可以在lambda表达式中使用任何外部作用域的局部变量。只可以使用参数。[=]代表可通过值获取作用域内的局部对象(局部变量和参数),即你只可以使用但不可修改。[]代表可通过引用获取作用域内的局部对象(局部变量和参数),即你可以像下面例子中一样修改它。[this]代表可通过值获取this指针。[a,b]代表通过值获取对象a,通过引用获取对象b。因此,如果你想在lambda函数中将数据转换成其他形式,你可以像下面这段代码一样,利用作用域来使用lambda。

在上面的例子中,如果你在lambda表达式中使用[factor]取值的方式获取了局部变量,你就不能在第五行中修改factor,因为你没有权利这样做。不要滥用你的权限!

最后,注意这里var是引用。这保证了在lambda函数内的任何改变都会真正改变vector。

if或switch语句里的初始状态

当我了解了c++17的这个特性之后我非常喜欢。

显然,现在你可以在if/switch语句块内初始化变量并且进行条件检查了。这对保持代码的紧凑和简洁是非常有帮助的。通常形式如下:

if(init-statement(x);condition(x)){//dosomestuffhere}else{//elsehasthescopeofx//dosomeotherstuff}

编译时执行constexpr

constexpr非常酷!

假设你有一些表达式要计算,并且它的值一旦初始化就不会改变。你可以预先计算该值并且作为宏来使用。或者像C++11中提供的,你可以使用constexpr。

编程人员倾向于尽可能减少程序的运行时间。因此如果某些操作可以让编译器来做,就可以减轻运行时的负担,从而提高时间效率。

上面的代码是constexpr的一个常见例子。

由于我们声明fibonacci计算函数为constexpr,编译器会在编译时预先计算fib(20)的值。所以编译结束后,它可以把constlonglongbigval=fib(20)替换为constlonglongbigval=;

需要注意的是,传递的参数是const值。这是声明为constexpr的函数非常重要的一点,传递的参数同样要是constexpr或者const。否则,该函数会像普通函数一样执行,即不会在编译时预先计算。

变量也同样可以是constexpr。这种情况下,你应该可以猜到,这些变量同样也是编译时计算的。否则,会出现编译错误。

有趣的是,在之后的c++17中,又引入了constexpr-if和constexpr-lambda。

tuple

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

有时候,使用std::array会比使用tuple更加方便。array和普通C类型的array非常相似,但具有C++标准库的一些特性。这种数据结构是C++11中新增的。

类模版参数推断

名字有点长。从c++17开始,参数推断也适用于标准类模版。此前,该特性只支持函数模版。

因此,

std::pairstd::string,intuser={M,25};//previousstd::pairuser={M,25};//C++17

类型推断是隐式完成的。这对tuple来说变得更加方便。

//previousstd::tuplestd::string,std::string,intuser(M,Chy,25);//deductioninaction!std::tupleuser2(M,Chy,25);

如果你不熟悉C++模版,那么上述特性可能对你来说不是很好理解。

智能指针

指针也可能并不好用。

由于C++给编程人员提供了很大的自由度,有时这种自由可能反而会成为绊脚石。在多数情况下,都是指针在起反面作用。

幸运的是,C++11引入了智能指针,它比之前的原始指针更加方便,可以通过适当地指针释放帮助开发者避免内存泄漏,同时也提供了额外的安全机制。

一开始我想在这篇文章中详细探讨一下智能指针,但显然重要的细节非常多,值得单开一篇来写,因此近期应该会出一篇相关文章。




转载请注明:http://www.aierlanlan.com/tzrz/1032.html

  • 上一篇文章:
  •   
  • 下一篇文章: