C++11新特性

前面一段时间啃完了《C++ Primer》第5版,对于书中的C++11新特性,摘抄在这里做个笔记。
 

一、C++基础

1、列表初始化

C++11新特性_第1张图片

作为C++11新标准的一部分,用花括号来初始化变量得到了全面应用,而在此之前,这种初始化的形式仅在某些受限的场合下才能使用。

现在,无论是初始化对象还是某些时候为对象赋新值,都可以使用这样一组由花括号括起来的初始值了。

当用于内置类型的变量时,这种初始化形式有一个重要特点:如果我们使用列表初始化且初始值存在丢失信息的风险,则编译器将报错:

 

C++11新标准还提供了另外一种为vector对象的元素赋初值的方法,即列表初始化。此时,用花括号括起来的0个或多个初始元素值被赋给vector对象:

 

关联容器的列表初始化:

C++11新特性_第2张图片

 

pair的列表初始化:

C++11新特性_第3张图片

 

自定义类的列表初始化:

C++11新特性_第4张图片

 

2、空指针

空指针不指向任何对象,几个生成空指针的方法:

C++11新特性_第5张图片

得到空指针最直接的办法就是用字面值nullptr来初始化指针,这也是C++11新标准刚刚引入的一种方法。nullptr是一种特殊类型的字面值,它可以被转换成任意其他的指针类型。

过去的程序还会用到一个名为NULL的预处理变量(preprocessor variable)来给指针赋值,这个变量在头文件cstdlib中定义,它的值就是0。

 

3、constexpr和常量表达式

常量表达式是指值不会改变并且在编译过程就能得到计算结果的表达式。显然,字面值属于常量表达式,用常量表达式初始化的const对象也是常量表达式。

一个对象(或表达式)是不是常量表达式由它的数据类型和初始值共同决定,例如:

C++11新特性_第6张图片

尽管staff_size的初始值是个字面值常量,但由于它的数据类型只是一个普通int而非const int,所以它不属于常量表达式。另一方面,尽管sz本身是一个常量,但它的具体值直到运行时才能获取到,所以也不是常量表达式。

在复杂系统中,很难分辨一个初始值到底是不是常量表达式。所以导致在实际使用中,对象的定义和使用根本就是两回事。

C++11新标准规定,允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。声明为constexpr的变量一定是一个常量,而且必须用常量表达式初始化:

指针和constexpr:

p和q的类型相差甚远,p是一个指向常量的指针,而q是一个常量指针,其中的关键在于constexpr把它所定义的对象置为了顶层const。

与其他常量指针类似,constexpr指针既可以指向常量也可以指向一个非常量:

C++11新特性_第7张图片

对顶层常量、底层常量、常量和指针、引用结合一直还比较模糊。

 

4、constexpr函数

constexpr函数是指能用于常量表达式(参见2.4.4节,第58页)的函数。定义constexpr函数的方法与其他函数类似,不过要遵循几项约定:函数的返回类型及所有形参的类型都得是字面值类型(参见2.4.4节,第59页),而且函数体中必须有且只有一条return语句:

constexpr函数体内也可以包含其他语句,只要这些语句在运行时不执行任何操作就行。例如,constexpr函数中可以有空语句、类型别名(参见2.5.1节,第60页)以及using声明。

 

5、类型别名

传统的方法是使用关键字typedef:

新标准规定了一种新的方法,使用别名声明(alias declaration)来定义类型的别名:

 

6、auto类型说明符

有时在声明变量的时候,可能并不清楚的知道表达式的类型。

C++11新标准引入了auto类型说明符,用它就能让编译器替我们去分析表达式所属的类型。和原来那些只对应一种特定类型的说明符(比如double)不同,auto让编译器通过初始值来推算变量的类型。显然,auto定义的变量必须有初始值:

使用auto也能在一条语句中声明多个变量。因为一条声明语句只能有一个基本数据类型,所以该语句中所有变量的初始基本数据类型都必须一样:

编译器推断出来的auto类型有时候和初始值的类型并不完全一样,编译器会适当地改变结果类型使其更符合初始化规则。

首先,正如我们所熟知的,使用引用其实是使用引用的对象,特别是当引用被用作初始值时,真正参与初始化的其实是引用对象的值。此时编译器以引用对象的类型作为auto的类型:

其次,auto一般会忽略掉顶层const(参见2.4.3节,第57页),同时底层const则会保留下来,比如当初始值是一个指向常量的指针时:

C++11新特性_第8张图片

string的size_type:

在C++11中就可以使用auto或者decltype,而不用关注size_type的细节。

 

如果我们提供了一个括号包围的初始化器,就可以使用auto从此初始化器来推断我们想要分配的对象的类型。但是,由于编译器要用初始化器的类型来推断要分配的类型,只有当括号中仅有单一初始化器时才可以使用auto:

 

7、decltype类型指示符

有时会遇到这种情况:希望从表达式的类型推断出要定义的变量的类型,但是不想用该表达式的值初始化变量。为了满足这一要求,C++11新标准引入了第二种类型说明符decltype,它的作用是选择并返回操作数的数据类型

decltype处理顶层const和引用的方式与auto有些许不同。如果decltype使用的表达式是一个变量,则decltype返回该变量的类型(包括顶层const和引用在内):

特别:decltype((variable))(注意是双层括号)的结果永远是引用,而decltype(variable)结果只有当variable本身就是一个引用时才是引用。

 

8、范围for

如果想对string对象中的每个字符做点儿什么操作,目前最好的办法是使用C++11新标准提供的一种语句:范围for(range for)语句。这种语句遍历给定序列中的每个元素并对序列中的每个值执行某种操作,其语法形式是:

C++11新特性_第9张图片

其中,expression部分是一个对象,用于表示一个序列。declaration部分负责定义一个变量,该变量将被用于访问序列中的基础元素。每次迭代,declaration部分的变量会被初始化为expression部分的下一个元素值。

 

C++11中处理多维数组:

在这里给一个二维数组赋初值:

C++11新特性_第10张图片

与前面对比:

C++11新特性_第11张图片

 

9、迭代器

C++11新特性_第12张图片

const_iterator和常量指针差不多,能读取但不能修改它所指的元素值。相反,iterator的对象可读可写。

begin和end返回的具体类型由对象是否是常量决定,如果对象是常量,begin和end返回const_iterator;如果对象不是常量,返回iterator:

有时候这种默认的行为并非我们所要。如果对象只需读操作而无须写操作的话最好使用常量类型(比如const_iterator)。为了便于专门得到const_iterator类型的返回值,C++11新标准引入了两个新函数,分别是cbegin和cend:

 

数组操作:

尽管能计算得到尾后指针,但这种用法极易出错。为了让指针的使用更简单、更安全,C++11新标准引入了两个名为begin和end的函数。这两个函数与容器中的两个同名成员功能类似,不过数组毕竟不是类类型,因此这两个函数不是成员函数。正确的使用形式是将数组作为它们的参数:

案例:

C++11新特性_第13张图片

特别要注意,尾后指针不能执行解引用和递增操作。

 

10、算术运算

除法:C++语言的早期版本允许结果为负值的商向上或向下取整,C++11新标准则规定商一律向0取整(即直接切除小数部分)。

 

11、initializer_list形参

如果函数的实参数量未知但是全部实参的类型都相同,我们可以使用initializer_list类型的形参。initializer_list是一种标准库类型,用于表示某种特定类型的值的数组。initializer_list类型定义在同名的头文件中,它提供的操作:

C++11新特性_第14张图片

和vector一样,initializer_list也是一种模板类型。定义initializer_list对象时,必须说明列表中所含元素的类型:

示例:

C++11新特性_第15张图片

作用于initializer_list对象的begin和end操作类似于vector对应的成员。begin()成员提供一个指向列表首元素的指针,end()成员提供一个指向列表尾后元素的指针。

 

12、列表初始化返回值

C++11新标准规定,函数可以返回花括号包围的值的列表。类似于其他返回结果,此处的列表也用来对表示函数返回的临时量进行初始化。

C++11新特性_第16张图片

如果函数返回的是内置类型,则花括号包围的列表最多包含一个值,而且该值所占空间不应该大于目标类型的空间。如果函数返回的是类类型,由类本身定义初始值如何使用。

 

13、尾置返回类型

返回数组指针:因为数组不能被拷贝,所以函数不能返回数组。不过,函数可以返回数组的指针或引用。

声明一个返回数组指针的函数:

在C++11新标准中还有一种可以简化上述func声明的方法,就是使用尾置返回类型(trailing return type)。任何函数的定义都能使用尾置返回,但是这种形式对于返回类型比较复杂的函数最有效,比如返回类型是数组的指针或者数组的引用。尾置返回类型跟在形参列表后面并以一个->符号开头。为了表示函数真正的返回类型跟在形参列表之后,我们在本应该出现返回类型的地方放置一个auto:

使用decltype:如果我们知道函数返回的指针将指向哪个数组,就可以使用decltype关键字声明返回类型。

C++11新特性_第17张图片

注意:decltype并不负责把数组类型转换成对应的指针,所以decltype的结果是个数组,要想表示arrPtr返回指针还必须在函数声明时加一个*符号。

 

 

 

 

二、C++标准库

1、文件流创建

在C++11中,ifile可以是string的参数,也可以是C风格字符数组。

旧版本的标准库只允许C风格字符数组。

 

2、顺序容器

forward_list和array是新C++标准增加的类型。与内置数组相比,array是一种更安全、更容易使用的数组类型。与内置数组类似,array对象的大小是固定的。因此,array不支持添加和删除元素以及改变容器大小的操作。forward_list的设计目标是达到与最好的手写的单向链表数据结构相当的性能。因此,forward_list没有size操作,因为保存或计算其大小就会比手写链表多出额外的开销。对其他容器而言,size保证是一个快速的常量时间的操作。

 

array的使用:

array多维:

 

顺序容器的操作:

C++11新特性_第18张图片

在C++11中,接受元素个数或范围的insert版本返回指向第一个新加入元素的迭代器。(在旧版本的标准库中,这些操作返回void。)如果范围为空,不插入任何元素,insert操作会将第一个参数返回。

新标准引入了三个新成员——emplace_front、emplace和emplace_back,这些操作构造而不是拷贝元素。这些操作分别对应push_front、insert和push_back,允许我们将元素放置在容器头部、一个指定位置之前或容器尾部。

 

在新标准库中,我们可以调用shrink_to_fit来要求deque、vector或string退回不需要的内存空间。此函数指出我们不再需要任何多余的内存空间。但是,具体的实现可以选择忽略此请求。也就是说,调用shrink_to_fit也并不保证一定退回内存空间。

 

3、lambda表达式

一个lambda表达式表示一个可调用的代码单元。我们可以将其理解为一个未命名的内联函数。与任何函数类似,一个lambda具有一个返回类型、一个参数列表和一个函数体。但与函数不同,lambda可能定义在函数内部。一个lambda表达式具有如下形式

其中,capture list(捕获列表)是一个lambda所在函数中定义的局部变量的列表(通常为空);return type、parameter list和function body与任何普通函数一样,分别表示返回类型、参数列表和函数体。但是,与普通函数不同,lambda必须使用尾置返回来指定返回类型。

可以忽略参数列表和返回类型,但必须永远包含捕获列表和函数体:

lambda的调用方式与普通函数的调用方式相同,都是使用调用运算符:

与普通函数不同,lambda不能有默认参数。因此,一个lambda调用的实参数目永远与形参数目相等。

捕获列表其实是申明要用到的所在函数的局部变量。空捕获列表表明此lambda不使用它所在函数中的任何局部变量。

举例:lambda所在函数有一个 sz 变量,如果该lambda表达式要使用这个 sz 变量,那么需要写在捕获列表中,如果不捕获,那么就不能使用。

捕获分为 值捕获 和 引用捕获。引用捕获:

C++11新特性_第19张图片

 

隐式捕获:

除了显式列出我们希望使用的来自所在函数的变量之外,还可以让编译器根据lambda体中的代码来推断我们要使用哪些变量。为了指示编译器推断捕获列表,应在捕获列表中写一个&或=。&告诉编译器采用捕获引用方式,=则表示采用值捕获方式。

C++11新特性_第20张图片

如果我们希望对一部分变量采用值捕获,对其他变量采用引用捕获,可以混合使用隐式捕获和显式捕获:

C++11新特性_第21张图片

当我们混合使用隐式捕获和显式捕获时,捕获列表中的第一个元素必须是一个&或=。此符号指定了默认捕获方式为引用或值。

当混合使用隐式捕获和显式捕获时,显式捕获的变量必须使用与隐式捕获不同的方式。即,如果隐式捕获是引用方式(使用了&),则显式捕获命名变量必须采用值方式,因此不能在其名字前使用&。类似的,如果隐式捕获采用的是值方式(使用了=),则显式捕获命名变量必须采用引用方式,即,在名字前使用&。

默认情况下,对于一个值被拷贝的变量,lambda不会改变其值。如果我们希望能改变一个被捕获的变量的值,就必须在参数列表首加上关键字mutable。

 

4、智能指针

智能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象。

shared_ptr允许多个指针指向同一个对象;

unique_ptr则“独占”所指向的对象。

标准库还定义了一个名为weak_ptr的伴随类,它是一种弱引用,指向shared_ptr所管理的对象。

类似vector,智能指针也是模板。因此,当我们创建一个智能指针时,必须提供额外的信息——指针可以指向的类型。

默认初始化的智能指针中保存着一个空指针。

智能指针的使用方式与普通指针类似。解引用一个智能指针返回它指向的对象。如果在一个条件判断中使用智能指针,效果就是检测它是否为空:

make_shared函数

最安全的分配和使用动态内存的方法是调用一个名为make_shared的标准库函数。此函数在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr。

C++11新特性_第22张图片

当进行拷贝或赋值操作时,每个shared_ptr都会记录有多少个其他shared_ptr指向相同的对象:

一旦一个shared_ptr的计数器变为0,它就会自动释放自己所管理的对象。

C++11新特性_第23张图片

C++11新特性_第24张图片

每个shared_ptr都有一个关联的计数器,通常称其为引用计数(reference count)。无论何时我们拷贝一个shared_ptr,计数器都会递增。例如,当用一个shared_ptr初始化另一个shared_ptr,或将它作为参数传递给一个函数以及作为函数的返回值时,它所关联的计数器就会递增。

 

不要将内置指针和智能指针混合使用:

C++11新特性_第25张图片

 

智能指针与异常:

如果使用智能指针,即使程序块过早结束,智能指针类也能确保在内存不再需要时将其释放,:

C++11新特性_第26张图片

原因是无论是正常处理结束还是异常退出,局部变量sp都会被销毁,在sp被销毁的时候会检查引用计数。

而如果使用的是内置指针,则这里不会被自动释放。

 

如果智能指针指向的动态内存没有析构函数,可以给它传递一个删除器来确保shared_ptr中保存的指针进行释放操作。

C++11新特性_第27张图片

 

智能指针陷阱:

不使用相同的内置指针值初始化(或reset)多个智能指针。

不delete get()返回的指针。

不使用get()初始化或reset另一个智能指针。

如果你使用get()返回的指针,记住当最后一个对应的智能指针销毁后,你的指针就变为无效了。

如果你使用智能指针管理的资源不是new分配的内存,记住传递给它一个删除器。

 

unique_ptr:

一个unique_ptr“拥有”它所指向的对象。与shared_ptr不同,某个时刻只能有一个unique_ptr指向一个给定对象。当unique_ptr被销毁时,它所指向的对象也被销毁。

 

weak_ptr:

是一种不控制所指向对象生存期的智能指针,它指向由一个shared_ptr管理的对象。将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。

由于对象可能不存在,我们不能使用weak_ptr直接访问对象,而必须调用lock。此函数检查weak_ptr指向的对象是否仍存在。如果存在,lock返回一个指向共享对象的shared_ptr。与任何其他shared_ptr类似,只要此shared_ptr存在,它所指向的底层对象也就会一直存在。例如:

 

 

三、类设计

1、构造函数=default、=delete

类 Sales_data 的定义中:

该构造函数不接受任何实参,所以它是一个默认构造函数。

定义这个构造函数的目的仅仅是因为我们既需要其他形式的构造函数,也需要默认的构造函数。我们希望这个函数的作用完全等同于之前使用的合成默认构造函数。

在C++11新标准中,如果我们需要默认的行为,那么可以通过在参数列表后面写上= default来要求编译器生成构造函数。其中,= default既可以和声明一起出现在类的内部,也可以作为定义出现在类的外部。和其他函数一样,如果= default在类的内部,则默认构造函数是内联的;如果它在类的外部,则该成员默认情况下不是内联的。

 

因为编译器会生成合成版本,如果为了控制禁止拷贝等操作。

在新标准下,我们可以通过将拷贝构造函数和拷贝赋值运算符定义为删除的函数(deleted function)来阻止拷贝。删除的函数是这样一种函数:我们虽然声明了它们,但不能以任何方式使用它们。在函数的参数列表后面加上=delete来指出我们希望将它定义为删除的:

C++11新特性_第28张图片

 

注意:析构函数不能是删除函数

值得注意的是,我们不能删除析构函数。如果析构函数被删除,就无法销毁此类型的对象了。对于一个删除了析构函数的类型,编译器将不允许定义该类型的变量或创建该类的临时对象。而且,如果一个类有某个成员的类型删除了析构函数,我们也不能定义该类的变量或临时对象。因为如果一个成员的析构函数是删除的,则该成员无法被销毁。而如果一个成员无法被销毁,则对象整体也就无法被销毁了。

对于删除了析构函数的类型,虽然我们不能定义这种类型的变量或成员,但可以动态分配这种类型的对象。但是,不能释放这些对象:

C++11新特性_第29张图片

 

在新标准发布之前,类是通过将其拷贝构造函数和拷贝赋值运算符声明为private的来阻止拷贝:

希望阻止拷贝的类应该使用=delete来定义它们自己的拷贝构造函数和拷贝赋值运算符,而不应该将它们声明为private的。

 

2、委托构造函数

C++11新标准扩展了构造函数初始值的功能,使得我们可以定义所谓的委托构造函数(delegating constructor)。一个委托构造函数使用它所属类的其他构造函数执行它自己的初始化过程,或者说它把它自己的一些(或者全部)职责委托给了其他构造函数。

C++11新特性_第30张图片

当一个构造函数委托给另一个构造函数时,受委托的构造函数的初始值列表和函数体被依次执行,然后控制权才会交还给委托者的函数体。

 

3、移动构造函数 和 std::move

通过使用新标准库引入的两种机制,我们就可以避免string的拷贝。

首先,有一些标准库类,包括string,都定义了所谓的“移动构造函数”。

第二个机制是一个名为move的标准库函数,它定义在utility头文件中。目前,关于move我们需要了解两个关键点。首先,当reallocate在新内存中构造string时,它必须调用move来表示希望使用string的移动构造函数。

C++11新特性_第31张图片

 

关于string的移动构造函数如何工作的细节,以及有关实现的任何其他细节,目前都尚未公开。但是,我们知道,移动构造函数通常是将资源从给定对象“移动”而不是拷贝到正在创建的对象。而且我们知道标准库保证“移后源”(moved-from)string仍然保持一个有效的、可析构的状态。对于string,我们可以想象每个string都有一个指向char数组的指针。可以假定string的移动构造函数进行了指针的拷贝,而不是为字符分配内存空间然后拷贝字符。

 

标准库容器、string和shared_ptr类既支持移动也支持拷贝。IO类和unique_ptr类可以移动但不能拷贝。

 

4、右值

为了支持移动操作,新标准引入了一种新的引用类型——右值引用(rvaluereference)。所谓右值引用就是必须绑定到右值的引用。我们通过&&而不是&来获得右值引用。

右值引用有一个重要的性质——只能绑定到一个将要销毁的对象。因此,我们可以自由地将一个右值引用的资源“移动”到另一个对象中。

C++11新特性_第32张图片

 

左值持久;右值短暂

考察左值和右值表达式的列表,两者相互区别之处就很明显了:左值有持久的状态,而右值要么是字面常量,要么是在表达式求值过程中创建的临时对象。

变量是左值

 

虽然不能将一个右值引用直接绑定到一个左值上,但我们可以显式地将一个左值转换为对应的右值引用类型。我们还可以通过调用一个名为move的新标准库函数来获得绑定到左值上的右值引用,此函数定义在头文件utility中。move函数使用了我们将在16.2.6节中描述的机制来返回给定对象的右值引用。

move调用告诉编译器:我们有一个左值,但我们希望像一个右值一样处理它。我们必须认识到,

调用move就意味着承诺:除了对rr1赋值或销毁它外,我们将不再使用它。在调用move之后,我们不能对移后源对象的值做任何假设。

 

类似string类(及,如果我们自己的类也同时支持移动和拷贝,那么也能从中受益。为了让我们自己的类型支持移动操作,需要为其定义移动构造函数和移动赋值运算符。这两个成员类似对应的拷贝操作,但它们从给定对象“窃取”资源而不是拷贝资源

 

类似拷贝构造函数,移动构造函数的第一个参数是该类类型的一个引用。不同于拷贝构造函数的是,这个引用参数在移动构造函数中是一个右值引用。与拷贝构造函数一样,任何额外的参数都必须有默认实参。

C++11新特性_第33张图片

 

由于一个移后源对象具有不确定的状态,对其调用std::move是危险的。当我们调用move时,必须绝对确认移后源对象没有其他用户。

5、noexcept

noexcept是我们承诺一个函数不抛出异常的一种方法。我们在一个函数的参数列表后指定noexcept。

 

对于一个函数来说,noexcept说明要么出现在该函数的所有声明语句和定义语句中,要么一次也不出现。

 

可能出现这样一种情况:尽管函数声明了它不会抛出异常,但实际上还是抛出了。一旦一个noexcept函数抛出了异常,程序就会调用terminate以确保遵守不在运行时抛出异常的承诺。上述过程对是否执行栈展开未作约定,因此noexcept可以用在两种情况下:一是我们确认函数不会抛出异常,二是我们根本不知道该如何处理异常。

 

6、标准库function类型

C++中的可调用对象:函数、函数指针、lambda表达式、bind创建的对象、重载了函数调用运算符的类;

上面这些不同类型可能具有相同的调用形式:

C++11新特性_第34张图片

上面的类型不同,但是调用形式都如下:

在C++11中,我们可以使用一个名为function的新的标准库类型来统一上面。

C++11新特性_第35张图片

 

7、显示类型转换

类型转换运算符是类的一种特殊成员函数,负责将一个类类型的值转换成其他类型:

在实践中,类很少提供类型转换运算符;在大多数情况下,如果类型转换自动发生,可能会有比较意外的结果。

举个例子:

早期版本中,如果类想定义一个向bool的类型转换,则它常常遇到一个问题:因为bool是一种算术类型,所以类类型的对象转换成bool后就能被用在任何需要算术类型的上下文中。这样的类型转换可能引发意想不到的结果,特别是当istream含有向bool的类型转换时,下面的代码仍将编译通过:

因为istream本身并没有定义<<,所以本来代码应该产生错误。然而,该代码能使用istream的bool类型转换运算符将cin转换成bool,而这个bool值接着会被提升成int并用作内置的左移运算符的左侧运算对象。这样一来,提升后的bool值(1或0)最终会被左移42个位置。这一结果显然与我们的预期大相径庭。

 

C++11新标准引入了显式的类型转换运算符:

C++11新特性_第36张图片

 

8、派生类继承

C++11新标准允许派生类显式地注明它使用某个成员函数覆盖了它继承的虚函数。具体做法是在形参列表后面、或者在const成员函数的const关键字后面、或者在引用成员函数的引用限定符后面添加一个关键字override。

这么做的好处是在使得程序员的意图更加清晰的同时让编译器可以为我们发现一些错误。

 

C++11新标准提供了一种防止继承发生的方法,即在类名后跟一个关键字final:

C++11新特性_第37张图片

 

“继承”的构造函数:

在C++11新标准中,派生类能够重用其直接基类定义的构造函数。

一个类只初始化它的直接基类,出于同样的原因,一个类也只继承其直接基类的构造函数。类不能继承默认、拷贝和移动构造函数。如果派生类没有直接定义这些构造函数,则编译器将为派生类合成它们。

C++11新特性_第38张图片

 

在C++11新标准中,允许派生类从它的一个或几个基类中继承构造函数。但是如果从多个基类中继承了相同的构造函数(即形参列表完全相同),则程序将产生错误:

C++11新特性_第39张图片

如果一个类从它的多个基类中继承了相同的构造函数,则这个类必须为该构造函数定义它自己的版本:

C++11新特性_第40张图片

 

 

四、高级主题

1、tuple

tuple是类似pair的模板。每个pair的成员类型都不相同,但每个pair都恰好有两个成员。不同tuple类型的成员类型也不相同,但一个tuple可以有任意数量的成员。每个确定的tuple类型的成员数目是固定的,但一个tuple类型的成员数目可以与另一个tuple类型不同。

当我们希望将一些数据组合成单一对象,但又不想麻烦地定义一个新数据结构来表示这些数据时,tuple是非常有用的。

 

2、正则表达式

C++正则表达式库(RE库),它是新标准库的一部分。

C++11新特性_第41张图片

 

3、随机数

在以前版本中,依赖于一个简单的C库函数rand来生成随机数。

rand函数有一些问题:即使不是大多数,也有很多程序需要不同范围的随机数。一些应用需要随机浮点数。一些程序需要非均匀分布的数。而程序员为了解决这些问题而试图转换rand生成的随机数的范围、类型或分布时,常常会引入非随机性。

C++11中引入了随机数引擎类(random-number engines)和随机数分布类(random-numberdistribution)。

C++11新特性_第42张图片

分布:

C++11新特性_第43张图片

 

4、内联命名空间

内联命名空间(inlinenamespace)。和普通的嵌套命名空间不同,内联命名空间中的名字可以被外层命名空间直接使用。也就是说,我们无须在内联命名空间的名字前添加表示该命名空间的前缀,通过外层命名空间的名字就可以直接访问它。

C++11新特性_第44张图片

 

5、枚举

C++11新标准引入了限定作用域的枚举类型(scoped enumeration)。

包含两种枚举:限定作用域的和不限定作用域的。

 

定义限定作用域的枚举类型的一般形式是:首先是关键字enum class(或者等价地使用enum struct),随后是枚举类型名字以及用花括号括起来的以逗号分隔的枚举成员(enumerator)列表,最后是一个分号:

不限定作用域的枚举:

 

限定作用域枚举类型是为了弥补不限定作用域枚举类型的不足而出现的,不限定作用域的枚举类型不是类型安全的,主要表现在如下几个方面:

不限定作用域的枚举类型中的枚举成员被视为整数,两种不同的枚举类型之间可以进行比较。两种不同类型的数据进行比较,可能带来数据类型转换,引起数据表示不完整。

不限定作用域枚举所使用的整数类型及其大小都由实现方法定义,皆无法明确指定。

不限定作用域枚举类型的枚举成员与枚举类型外部数据处在同一个作用域范围内,多个枚举类型不能有同名的枚举成员。

 

在C++11新标准中,我们可以在enum的名字后加上冒号以及我们想在该enum中使用的类型:

C++11新特性_第45张图片

在C++11新标准中,我们可以提前声明enum。enum的前置声明(无论隐式地还是显示地)必须指定其成员的大小:

因为不限定作用域的enum未指定成员的默认大小,因此每个声明必须指定成员的大小。对于限定作用域的enum来说,我们可以不指定其成员的大小,这个值被隐式地定义成int。

 

你可能感兴趣的:(C++11新特性)