仓酷云

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 3826|回复: 9
打印 上一主题 下一主题

[C++基础] CentOS教程之每一个C++开辟者都应当利用的十个C++11特征

[复制链接]
跳转到指定楼层
楼主
发表于 2015-1-16 11:05:02 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
为什么我使用一个命令的时候,系统告诉我找不到该目录,我要如何限制使用者的权限等问题,这些问题其实都不是很难的。
  这篇文章会商了一系列一切开辟者都应当进修和利用的C++11特征,在新的C++尺度中,言语和尺度库都到场了良多新属性,这篇文章只会先容一些外相,但是,我信任有一些特性用法应当会成为C++开辟者的一样平常用法之一。你大概已找到良多相似先容C++11尺度特性的文章,这篇文章能够当作是那些经常使用特性形貌的一个汇合。
  目次:


  • auto关头字
  • nullptr关头字
  • 基于区间的轮回
  • Override和final
  • 强范例列举
  • 智能指针
  • Lambdas表达式
  • 非成员begin()和end()
  • static_assert宏和范例萃取器
  • 挪动语义



 auto关头字

  在C++11尺度之前,auto关头字就被用来标识一时变量语义,在新的尺度中,它的目标酿成了别的两种用处。auto如今是一品种型占位符,它会告知编译器,应当从初始化式中揣度出变量的实践范例。当你想在分歧的感化域中(比方,定名空间、函数内、for轮回中中的初始化式)声明变量的时分,auto能够在这些场所利用。
  1. autoi=42;//iisanintautol=42LL;//lisanlonglongautop=newfoo();//pisafoo*
复制代码
  利用auto常常意味着较少的代码量(除非你必要的范例是int这类只要一个单词的)。当你想要遍历STL容器中元素的时分,想想你会怎样写迭代器代码,老式的办法是用良多typedef来做,而auto则会年夜年夜简化这个历程。
  1. std::map<std::string,std::vector<int>>map;for(autoit=begin(map);it!=end(map);++it){}
复制代码
  你应当注重到,auto其实不能作为函数的前往范例,可是,你能用auto往取代函数的前往范例,固然了,在这类情形下,函数必需有前往值才能够。auto不会告知编译器往揣度前往值的实践范例,它会关照编译器在函数的末段往寻觅前往值范例。鄙人面的谁人例子中,函数前往值的组成是由T1范例和T2范例的值,经由+操纵符以后决意的。
  1. template<typenameT1,typenameT2>autocompose(T1t1,T2t2)->decltype(t1+t2){returnt1+t2;}autov=compose(2,3.14);//vstypeisdouble
复制代码
 nullptr关头字

  0已经是空指针的值,这类体例有一些坏处,由于它能够被隐式转换成整型变量。nullptr关头字代表值范例std::nullptr_t,在语义上能够被了解为空指针。nullptr可被隐式转换成任何范例的空指针,和成员函数指针和成员变量指针,并且也能够转换为bool(值为false),可是隐式转换到整型变量的情形不再存在了。
  1. voidfoo(int*p){}voidbar(std::shared_ptr<int>p){}int*p1=NULL;int*p2=nullptr;if(p1==p2){}foo(nullptr);bar(nullptr);boolf=nullptr;inti=nullptr;//error:Anativenullptrcanonlybeconvertedtoboolor,usingreinterpret_cast,toanintegraltype
复制代码
  为了向下兼容,0仍可作为空指针的值来利用。
 基于区间的轮回

  C++11增强了for语句的功效,以更好的撑持用于遍历汇合的“foreach”范式。在新的情势中,用户可使用for往迭代遍历C作风的数组、初始化列表,和一切非成员begin()和end被重载的容器。
  当你仅仅想猎取汇合/数组中的元从来做一些事变,而不存眷索引值、迭代器大概元素自己的时分,这类for的情势十分有效。
  1. std::map<std::string,std::vector<int>>map;std::vector<int>v;v.push_back(1);v.push_back(2);v.push_back(3);map["one"]=v;for(constauto&kvp:map){std::cout<<kvp.first<<std::endl;for(autov:kvp.second){std::cout<<v<<std::endl;}}intarr[]={1,2,3,4,5};for(int&e:arr){e=e*e;}
复制代码
 Override和final

  我常常会发明虚函数在C++中会引发良多成绩,由于没有一个强迫的机制来标识虚函数在派生类中被重写了。virtual关头字并非强迫性的,这给代码的浏览增添了一些坚苦,由于你大概不能不往看承继干系的最顶层以确认这个办法是否是虚办法。我本人常常勉励开辟者在派生类中利用virtual关头字,我本人也是这么做的,这可让代码更容易读。但是,有一些不分明的毛病仍旧会呈现,上面这段代码就是个例子。
  1. classB{public:virtualvoidf(short){std::cout<<"B::f"<<std::endl;}};classD:publicB{public:virtualvoidf(int){std::cout<<"D::f"<<std::endl;}};
复制代码
  D::f本应当重写B::f,可是这两个函数的署名其实不不异,一个参数是short,另外一个则是int,因而,B::f仅仅是别的一个和D::f定名不异的函数,是重载而不是重写。你有大概会经由过程B范例的指针挪用f(),而且期盼输入D::f的了局,可是打印出来的了局倒是B::f。
  这里另有别的一个不分明的毛病:参数是不异的,可是在基类中的函数是const成员函数,而在派生类中则不是。
  1. classB{public:virtualvoidf(int)const{std::cout<<"B::f"<<std::endl;}};classD:publicB{public:virtualvoidf(int){std::cout<<"D::f"<<std::endl;}};
复制代码
  又一次,这两个函数的干系是重载而非重写,因而,假如你想经由过程B范例的指针来挪用f(),程序会打印出B::f,而不是D::f。
  侥幸的是,有一种办法能够来形貌你的企图,两个新的、专门的标识符(不是关头字)增加进了C++11中:override,能够指定在基类中的虚函数应当被重写;final,能够用来指定派生类中的函数不会重写基类中的虚函数。第一个例子会酿成:
  1. classB{public:virtualvoidf(short){std::cout<<"B::f"<<std::endl;}};classD:publicB{public:virtualvoidf(int)override{std::cout<<"D::f"<<std::endl;}};
复制代码
  这段代码会触发一个编译毛病(假如你利用override标识符实验第二个例子,也会失掉不异的毛病。):
D::f:有override标识符的函数并没有重写任何基类函数
  另外一方面,假如你想要一个函数永久不克不及被重写(顺着承继条理往下都不克不及被重写),你能够把该函数标识为final,在基类中和派生类中都能够这么做。假如其实派生类中,你能够同时利用override和final标识符。
  1. classB{public:virtualvoidf(int){std::cout<<"B::f"<<std::endl;}};classD:publicB{public:virtualvoidf(int)overridefinal{std::cout<<"D::f"<<std::endl;}};classF:publicD{public:virtualvoidf(int)override{std::cout<<"F::f"<<std::endl;}};
复制代码
  用final声明的函数不克不及被F::f重写。
 强范例列举

  “传统”的C++列举范例有一些弱点:它会在一个代码区间中抛出列举范例成员(假如在不异的代码域中的两个列举范例具有不异名字的列举成员,这会招致定名抵触),它们会被隐式转换为整型,而且不成以指定列举的底层数据范例。
  经由过程引进一种新的列举范例,这些成绩在C++11中被办理了,这类新的列举范例叫做强范例列举。这类范例用enumclass关头字来标识,它永久不会在代码域中抛出列举成员,也不会隐式的转换为整形,同时还能够具有效户指定的底层范例(这个特性也被到场了传统列举范例中)。
  1. enumclassOptions{None,One,All};Optionso=Options::All;
复制代码
 智能指针

  有大批的文章先容过智能指针,因而,我仅仅想提一提智能指针的援用计数和内存主动开释相干的器材:


  • unique_ptr:当一块内存的一切权并非共享的时分(它其实不具有拷贝机关函数),可使用,可是,它能够被转换为别的一个unique_ptr(具有挪动机关函数)。

  • shared_ptr:当一块内存的一切权能够被共享的时分,可使用(这就是为何它叫这个名)。

  • weak_ptr:具有一个shared_ptr办理的指向一个实体工具的援用,可是并没有做任何援用计数的事情,它被用来冲破轮回援用干系(设想一个干系树,父节点具有指向子节点的援用(shared_ptr),可是子节点也必需持有指向父节点的援用;假如第二个援用也是一个自力的援用,一个轮回就发生了,这会招致任何工具都永久没法开释)。
  换句话说,auto_ptr已过期了,应当不再被利用了。
  甚么时分该利用unique_ptr,甚么时分该利用shared_ptr,取决于程序对内存一切权的需求,我保举你读一读这里的会商。
  上面第一个例子演示了unique_ptr的用法,假如你想要把工具的把持权转交给另外一个unique_ptr,请利用std::move(我将会在最初一段会商这个函数)。在把持权交代后,让出把持权的智能指针会酿成null,假如挪用get(),会前往nullptr。
  1. std::map<std::string,std::vector<int>>map;for(autoit=begin(map);it!=end(map);++it){}0
复制代码
  第二个例子演示了shared_ptr的用法。只管语义分歧,由于一切权是共享的,但用法都差未几。
  1. std::map<std::string,std::vector<int>>map;for(autoit=begin(map);it!=end(map);++it){}1
复制代码
  第一个声明等价于这个。
  1. std::map<std::string,std::vector<int>>map;for(autoit=begin(map);it!=end(map);++it){}2
复制代码
  make_shared是一个非成员函数,具有给共享工具分派内存,而且只分派一次内存的长处,和显式经由过程机关函数初始化的shared_ptr比拟较,后者必要最少两次分派内存。这些分外的开支有大概会招致内存溢出的成绩,鄙人一个例子中,假如seed()抛出一个非常,则暗示产生了内存溢出。
  1. std::map<std::string,std::vector<int>>map;for(autoit=begin(map);it!=end(map);++it){}3
复制代码
  假如利用make_shared,则能够避开相似成绩。第三个例子展现了weak_ptr的用法,注重,你必需经由过程挪用lock()来猎取shared_ptr中指向工具的援用,以此来会见工具。
  1. std::map<std::string,std::vector<int>>map;for(autoit=begin(map);it!=end(map);++it){}4
复制代码
  假如你试图在一个已过时的weak_ptr上挪用lock(被弱援用的工具已被开释了),你会失掉一个空的shared_ptr。
 Lambdas表达式

  匿名的办法,也叫做lambda表达式,被加进了C++11尺度里,而且立即失掉了开辟者们的器重。这是一个从函数式言语中自创来的,十分壮大的特性,它让一些其他的特性和壮大的库得以完成。在任何函数工具、函数、std::function中呈现的中央,你都能够用lambda表达式,你能够在这里浏览一下lambda的语法。
  1. std::map<std::string,std::vector<int>>map;for(autoit=begin(map);it!=end(map);++it){}5
复制代码
  有一点庞大的是递回lambda表达式。设想一个代表斐波那契函数的lambda表达式,假如你试图用auto来写这个函数,你会失掉编译毛病:
  1. std::map<std::string,std::vector<int>>map;for(autoit=begin(map);it!=end(map);++it){}6
复制代码
  1. std::map<std::string,std::vector<int>>map;for(autoit=begin(map);it!=end(map);++it){}7
复制代码
  这个成绩是因为auto会依据初始化式来揣度工具范例,而初始化式却包括了一个援用本人的表达式,因而,仍旧必要晓得它的范例,这是一个轮回成绩。为懂得决这个成绩,必需冲破这个无穷轮回,显式的用std::function来指定函数范例。
  1. std::map<std::string,std::vector<int>>map;for(autoit=begin(map);it!=end(map);++it){}8
复制代码
 非成员begin()和end()

  你大概已注重到了,我在下面的例子中已利用了非成员begin()和end()函数,这些是新加到STL中的器材,提拔了言语的尺度性和分歧性,也使更多的泛型编程酿成了大概,它们和一切的STL容器都是兼容的,但却不单单是复杂的重载,因而你能够随便扩大begin()和end(),以便兼容任何范例,针对C范例数组的重载也一样是撑持的。
  让我们举一个后面写过的例子,在这个例子中,我试图打印输入一个vector,而且找到它的第一个奇数值的元素。假如std::vector用C作风数组来取代的话,代码大概会像以下如许:
  1. std::map<std::string,std::vector<int>>map;for(autoit=begin(map);it!=end(map);++it){}9
复制代码
  假如你利用非成员begin()和end(),代码能够如许写:
  1. template<typenameT1,typenameT2>autocompose(T1t1,T2t2)->decltype(t1+t2){returnt1+t2;}autov=compose(2,3.14);//vstypeisdouble0
复制代码
  这段代码基础上和利用std::vector那段代码一样,这意味着我们能够为一切撑持begin()和end()的范例写一个泛型函数来到达这个目标。
  1. template<typenameT1,typenameT2>autocompose(T1t1,T2t2)->decltype(t1+t2){returnt1+t2;}autov=compose(2,3.14);//vstypeisdouble1
复制代码
 static_assert宏和范例萃取器

  static_assert会实行一个编译器的的断言,假如断言为真,甚么都不会产生,假如断言为假,编译器则会显现一些特定的毛病信息。
  1. template<typenameT1,typenameT2>autocompose(T1t1,T2t2)->decltype(t1+t2){returnt1+t2;}autov=compose(2,3.14);//vstypeisdouble2
复制代码
  1. template<typenameT1,typenameT2>autocompose(T1t1,T2t2)->decltype(t1+t2){returnt1+t2;}autov=compose(2,3.14);//vstypeisdouble3
复制代码
  当和范例萃取一同利用的时分,static_assert会变得加倍有效,这些是一系列能够在编译期供应分外信息的类,它们被封装在了头文件内里,在这个头文件里,有多少分类:用来创立编译期常量的helper类,用来编译期猎取范例信息的范例萃取类,为了能够把现存范例转换为新范例的范例转换类。
  鄙人面谁人例子里,add函数被计划成只能处置基础范例。
  1. template<typenameT1,typenameT2>autocompose(T1t1,T2t2)->decltype(t1+t2){returnt1+t2;}autov=compose(2,3.14);//vstypeisdouble4
复制代码
  但是,假如你这么写的话,其实不会呈现编译毛病。
  1. template<typenameT1,typenameT2>autocompose(T1t1,T2t2)->decltype(t1+t2){returnt1+t2;}autov=compose(2,3.14);//vstypeisdouble5
复制代码
  程序实践打印了4.14和“e”,可是假如我们增加一些编译器断言,这两行代码城市发生编译毛病。
  1. template<typenameT1,typenameT2>autocompose(T1t1,T2t2)->decltype(t1+t2){returnt1+t2;}autov=compose(2,3.14);//vstypeisdouble6
复制代码
  1. template<typenameT1,typenameT2>autocompose(T1t1,T2t2)->decltype(t1+t2){returnt1+t2;}autov=compose(2,3.14);//vstypeisdouble7
复制代码
 挪动语义

  这是又一个很主要,而且触及到良多C++11手艺特性的话题,关于这个话题不单单能写一段,更能写一系列文章。因而,我在这里其实不会形貌太多手艺细节,假如你还没有对这个话题很熟习,我会勉励你往翻阅一些分外的材料。
  为了辨别指向左值的援用和指向右值的援用,C++11引进了右值援用(用&&来暗示)的观点。左值是指一个着名字的工具,而右值则是一个没着名字的工具(一时工具)。挪动语义同意修正右值(之前思索到它的不成改动性,因而和constT&types的观点有些搅浑)。
  一个C++类/布局体有一些隐式成员函数:默许机关函数(当且仅当别的一个机关函数没有被显式的界说),拷贝机关函数,一个析构函数,和一个拷贝赋值操纵符。拷贝机关函数和拷贝赋值操纵符一样平常会实行按位拷贝(大概浅拷贝),比方,一一按位拷贝变量。这意味着假如你有一个包括指向某个工具的指针的类,它们只会把指针的地点举行拷贝,其实不会拷贝指针指向的工具。这在某些情形下是能够的,可是关于尽年夜多半情形,你必要的是深拷贝,也就是对指针指向的工具举行拷贝,而不是指针自己的值,在这类情形下你不能不显式的写一个拷贝机关函数和拷贝赋值操纵符来实行深拷贝。
  那末,假如你想要初始化大概复制的源数据是个右值范例(一时的)会怎样?你仍旧不能不拷贝它的值,可是很快,这个右值就会消散,这意味着一些操纵的开支,包含分派内存和最初拷贝数据,这些都是不用要的。
  我们引进了挪动机关函数和挪动赋值操纵符,这两个特别的函数承受一个T&&范例的右值参数,这两个函数能够修正工具,相似于把援用指向的工具“偷”来。举一个例子,一个容器的详细完成(比方vector大概queue)大概会包括一个指向数组元素的指针,我们能够为这些元素分派另外一个数组空间,从一时空间中拷贝数据,然后当一时数据生效的时分再删除这段内存,我们也能够间接用这个一时的数据来实例化,我们只是拷贝指向数组元素的指针地点,因而,这节俭了一次分派内存的开支,拷贝一系列元素而且稍后开释失落的开支。
  上面这个例子展现了一个假造缓冲区的完成,这段缓冲区由一个名字标识(只是为了能更好的注释),有一个指针(用std::unique_ptr封装起来),指向一个范例为T的数组,也有一个存储数组巨细的变量。
  1. template<typenameT1,typenameT2>autocompose(T1t1,T2t2)->decltype(t1+t2){returnt1+t2;}autov=compose(2,3.14);//vstypeisdouble8
复制代码
  默许拷贝机关函数和复制赋值操纵符应当看起来很相似,关于C++11尺度来讲,新的器材是依据挪动语义计划的挪动机关函数和挪动赋值操纵符。假如你运转这段代码,你会看到,当b4被机关的时分,挪用了挪动机关函数。而当b1被分派一个值的时分,挪动赋值操纵符被挪用了,缘故原由则是getBuffer()前往的值是一个一时的右值。
  你大概注重到了一个细节,现在始化name变量和指向buffer的指针的时分,我们在挪动机关函数中利用了std::move。name变量是一个字符串范例,std::string撑持挪动语义,unique_ptr也是一样的,但是,假如我们利用_name(temp._name),复制机关函数将会被挪用,但关于_buffer来讲,这倒是不成能的,由于std::unique_ptr并没有拷贝机关函数,可是为何std::string的挪动机关函数在这类情形下没有被挪用?由于即便为Buffer挪用挪动机关函数的工具是一个右值范例,在机关函数的外部却实践是个左值范例,为何?由于他有一个名字“temp”,而一个着名字的工具是左值范例。为了让它再一次酿成右值范例(也为了能够得当的挪用挪动机关函数),我们必需利用std::move。这个函数的感化只是把一个左值范例的援用转换成右值范例援用。
  更新:固然这个例子的目标是展现下怎样完成挪动机关函数和挪动赋值操纵符,但完成的详细细节大概会有所分歧,别的一个完成的计划是7805758成员在批评中提到的办法,为了能让人人更简单看到,我把它写在了注释中。
  1. template<typenameT1,typenameT2>autocompose(T1t1,T2t2)->decltype(t1+t2){returnt1+t2;}autov=compose(2,3.14);//vstypeisdouble9
复制代码
 结论

  关于C++11有良多的器材,以上内容只是良多内容的开端先容,这篇文章文章展现了一系列C++中心手艺和尺度库特性的用法,可是,我保举你最少对个中一些特性往做一些分外、深切的浏览。
  英文出处:TenC++11FeaturesEveryC++DeveloperShouldUse
在学习初期,你一定会遇到很多困难,或者说各种困难,所以你最好先将你linux中的重要内容备份,因为,在你学习的过程中,很可能将系统搞废(eg:源混乱等);
再见西城 该用户已被删除
沙发
发表于 2015-1-18 07:47:52 | 只看该作者
如在实验三中,有道实验“编程对一维数组求最大值、最小值、平均值”然而在课本上也有一道类似的,于是我按照书本上的语言编程上去,然后再改下,在运行时居然可以了。顿时,心里有种喜悦的满足感。
admin 该用户已被删除
板凳
发表于 2015-1-25 20:38:51 | 只看该作者
经过这周的实训,我收获颇多,从中发现了自己的不足之处,也增长了很多见识。在此,要感谢我们的指导老师邹锋,感谢他一周以来的耐心教导。
若天明 该用户已被删除
地板
发表于 2015-2-4 04:04:29 | 只看该作者
算法这一块,理解泛型的思路,标准库里针对于容器的算法,接口都大致一样。
海妖 该用户已被删除
5#
发表于 2015-2-9 15:50:38 | 只看该作者
一开始你问得问题他一定可以解决,所以这个时候你如坐春风,一日千里;慢慢得你一定可以提出强人解决不了得问题(不管那方面技术),这个时候可以说你已经入门了,把强人解决不了任务解决掉,解决掉你自己一定很有成就感;
变相怪杰 该用户已被删除
6#
发表于 2015-2-9 15:50:31 | 只看该作者
说起来,C++Primer和那本Plus思路不一样,这本书第3章就开始教你用标准库,用string对象与vector容器,所以对基础薄弱的同学来说上手有点难。
蒙在股里 该用户已被删除
7#
发表于 2015-2-27 09:09:16 | 只看该作者
见过太多得帖子问如何学习C++,也听过很多人说找不到方法,其实所有人一开始都有这样得困惑,我只想写出自己的心得。
透明 该用户已被删除
8#
发表于 2015-3-9 04:00:00 | 只看该作者
北邮历来都是学C++的,干嘛大家都搞得象是学C出身,然后狂鄙视学C++的,只能说明一点,他们两者都顺手拈来,已经完全融会贯通。
精灵巫婆 该用户已被删除
9#
发表于 2015-3-16 22:05:56 | 只看该作者
有首歌曲这样唱:说到不如做到,要做就做最好。
莫相离 该用户已被删除
10#
发表于 2015-3-23 08:13:35 | 只看该作者
有首歌曲这样唱:说到不如做到,要做就做最好。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|仓酷云 鄂ICP备14007578号-2

GMT+8, 2024-12-28 16:26

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表