C++11, 他改变了C++

栏目:影视资讯  时间:2022-11-08
手机版

  #Cpp#

  C++作为一个功能强大的语言,其语言标准在近20多年里持续保持着迭代,其中变化最大、影响最深的要属C++11标准,这个标准使得C++语言一下子变得现代了起来。那么C++11具体进行了哪些改动呢,我们一起来看看吧。

  auto和decltype

  在C++11之前,auto关键字是指的C语言的自动变量,意思是能自动地控制生命周期,会在内存栈上分配空间,随着函数的调用和返回一并产生和消亡,是与static关键字相反的关键字,后者声明的变量存储在静态存储区。而C++11及之后,auto关键字代表着自动类型推导,它能根据变量要初始化的类型来推导变量的类型,从而免去了写数据类型的麻烦。这个在数据类型非常长或者数据类型不确定的情况下十分有用。比如声明一个C++标准库容器vector的迭代器类型,std::vector<int>::iterator iter = v.begin(),其中v是std::vector<int>类型,用auto关键字可以直接简写为 auto iter = v.begin(),简单了不少,还能防止出错。

  另一个具有相似作用的关键字是decltype,不过相比auto,decltype关键字更为灵活,这个关键字可以简单看作是一个编译时期的函数,它的返回值就是表达式参数的得到的变量类型,不过这个类型不能是void,否则会引起报错。

  自动推导类型并输出类型名using定义别名

  C++11之前,定义别名的方式主要是用继承自C语言的typedef,或者使用宏。这两种办法或多或少有些问题,这两种办法最主要的问题是不能声明带模板的别名,除此之外,宏的话还会有命名冲突等问题。而到了C++11,using关键字可以完全代替typedef,除此之外也可以声明带模板的别名了

  列表初始化

  在C++11之前,如果我们声明一个std::vector<int>类型,我们不能像初始化数组,如int a[] = {1, 2, 3, 4} 这样初始化vector,而在C++11之后,像std::vector<int> a = {1, 2, 3, 4}这样初始化变得可行了,原因是新添加了一个语法,列表初始化。

  在C语言里,一个struct也是能像上面那样进行初始化的,C++为了兼容C,将C语言的数据类型称为POD类型,并规定POD类型才能进行类似memcpy的操作,可以简单理解为C++11解除了这个限制,不过实际上是C++11规定列表可以对应构造函数参数。

  如上图所示的程序,在C++11,我们就可以用这个方法声明Array类,如Array<4> arr = {1, 2, 3, 4}; 上图还用到了一个C++11关键字nullptr,这个是空指针的意思。因为要形式上兼容C语言,同时又要有函数重载特性,所以C++取消了隐式类型转换,NULL被声明为0,而C语言NULL是(void *)(0),两者含义不同,在函数重载的时候可能会出问题,因此提出了nullptr关键字,本质上是一个类对象,重载了各种运算符,起到空指针的作用。

  新型for循环

  C++11添加了新型的for循环,该循环是按容器的范围进行循环,要求容器有迭代器,有begin函数end函数。本质上下面两种循环是一样的。

  而第二种循环方式就是C++11的新型for循环,如果需要修改容器v中的值,需要把int a改成int &a。自定义的类也可以进行新型for循环,不过必须要有迭代器和begin,end函数,其中迭代器要么为指针类型,要么重载了自增和解引用运算符。

  lambda表达式

  lambda表达式又可以称之为匿名函数,最大的作用是用于多线程通信,同时也可以用于临时创建一个函数。比如我要调用标准库的std::sort()函数用于排序,但是这个函数默认是从小往大进行排序,如果要排序的是一个自定义类型,或者我们要从大往小进行排序,那么就可以将函数作为第三个参数来指定比较的方法。这个函数既可以是函数名、函数指针,也可以是lambda表达式。

  lambda表达式的语法较为复杂,基本结构为[](){},三部分组成第一个括号指定了要使用的外部函数内的变量,省略即为不使用,等于号表示全部传值使用,意思为不影响被使用的变量值,&表示传引用使用,即使用变量本身,如[=, &n]表示传引用使用n,其他的全部采用传值使用,[v, &n]表示传值使用v,传引用使用n。小括号指定了lambda表达式的参数,和函数参数无异。大括号内就相当于函数体。"->"可以用来指定返回值类型,如果省略不写,编译器会自行推定返回类型,仅在返回void或者只有一个return的时候有效。

  智能指针

  我们知道,C语言的内存管理都是手动的,而C++具有构造函数和析构函数,因此很自然地想到把指针封装成一个类,从而实现内存的自动管理。C++98/03也就是C++11的前一个版本提供了auto_ptr,来实现垃圾自动回收,而C++11在废除了auto_ptr的同时提供了新的智能指针,shared_ptr、unique_ptr和weak_ptr。从字面意义上看,第一个就是可以共享空间的指针,第二个就是不能共享空间的指针,第三个是弱一点的指针,实际它们的功能也大抵如此。

  智能指针采用引用计数,也就是弄个数判断智能指针赋值了多少次,用这种办法判断该智能指针指向的内存空间是否应该收回,shared_ptr这个智能指针可以将值赋给其他的shared_ptr,在赋值的时候同时引用计数增加,而shared_ptr对象销毁的时候,引用计数也随之减少。当减少至0的时候将delete这块内存空间。unique_ptr则可以看作不能分享空间的shared_ptr,当unique_ptr销毁时,其指向的内存空间就会被delete。而weak_ptr是服务于shared_ptr的,用来判断服务的shared_ptr所指的内存空间是否有效。这三种智能指针在一定程度上方便了内存的管理,不过对于小型程序,还是手动管理比较快。

  右值引用

  C++将常量和常变量或者表达式称为右值,将变量称为左值。为什么有这样看起来摸不到头脑的定义呢?其实随便写一条赋值语句就明白了,比如int a = b + 1; 哦原来放到左边就是左值,放到右边就是右值,所以像b + 1 = a 或者 5 = b 之类的语句是不合语法的。

  右值引用就是对右值的引用,C++11提出了这个语法概念,实现移动语义。对于一个临时对象而言,其不能被赋值,也就是说vector<int> () = a是错的,而a = vector<int> ()是对的,因此临时对象就是一个右值,因此在用临时对象对a进行赋值的时候回调用右值引用的移动构造函数,如vector<int>(vector<int> &&),如果赋值的是一个左值,就会调用常左值引用的移动构造函数如vector<int>(const vector<int>&)。注意左值可以用std::move强行转换成右值。

  constexpr关键字

  这个关键字用于声明常量表达式,不同于const,const声明的是常变量,是运行时声明的,而constexpr相当于是取代了可以作为"符号常量"的宏的部分功能,作用在编译时期,主要作用是为了提供编译器优化。假如一个函数的返回值被声明为constexpr的,那么相当于这个函数的返回值可能在编译期就可以算出来,从而提高运行时的效率。注意这里是”可能“,实际上是否真的可以在编译期算出来取决于编译器。

  结语

  如果充分利用上面提到的特性来写C++11代码,那么对于同一个项目,可能代码结构和具体语法细节要大变样,因此C++11之后和C++11前甚至可以被认为是两种语言,而实际上C++的特性还远不止这些,后面还有C++14、C++17以及C++20,C++14类似于C++03,是C++11基础上的小改动,C++17在C++14的基础上做出了更多的改动,而C++20的是不少于C++11的改动,甚至有网友说,C with class 的时代已经过去,现在是C with concept的时代。不过有关C++20以及文章中没有提及的C++11的特性,笔者了解甚少,感兴趣的朋友可以自行在网络上搜索。

  举报/反馈

上一篇:尼斯湖水怪,一个既神秘又恐怖的生物,你告诉我是这种生物?
下一篇:三国志11特技详解十:藤甲、强运、血路、护卫,都有神马作用?

最近更新影视资讯