探讨C++11新标准(一)-C++11的一些语法变化

前段时间面试的时候,别人问我对C++11了解多少,我当时脑袋一空,什么也想不起来,但是其实我在学习和工作过程中很多已经涉及到了C++11的内容,这篇文章就让我们一起总结C++11中的新标准吧。

c++11内容也不少,所以我分为三篇文章去探讨,第一篇主要探讨一些新的语法变化,
第二篇主要探讨移动语义和右值引用,
第三篇主要讲解类的变化。

1.新的类型

C++11新增了类型long long和unsigned long long以至此64位(或跟宽)的类型,新增了类型char16_t和char32_t以支持16位和32位的字符表示。

2.新增原始字符串表示方式,

原始字符串中有什么就输出什么。不会解析其中的转义字符。

cout<<"i am a \\n "; //输出 i am a \n 
cout<<R("i am a \\n"); //输出 i am a \\n  原始字符串输出

3.统一的初始化

3.1 增加了大括号初始化列表的适用范围

C++11扩大了用大括号括起的列表(初始化列表)的适用范围,使其可用于所有内置类型和用户的类型(即类对象)。使用初始化列表时,可添加等号(=),也可不添加:
intx=(5);
doubIey(2.75);
short quar[5] (4,5,2,76,1);
另外,列表初始化语法也可用于new表达式中:
int * ar=new int [4] [2,4,6,7; //C++11
创建对象时,也可使用大括号(而不是圆括号)括起的列表来调用构造函数:

classStump
{
privateint roots;
    	double weight;
publicStump(intr,doublew)roots(r)weight(w) {}
};
Stump s1 (315.6)//oldstyle
Stump s2 {1543.4}//C++11
gtump s3 = {1432.1)}//C++11
   

然而,如果类有将模板sdnitazerist作为参数的构造函数,则只有该构造函数可以使用列表初始化形式。

3.2 缩窄

初始化列表语法可防止缩窄,即禁止将数值赋给无法存储它的数值变量。常规初始化允许程序员执行可能没有意义的操作:
char c1 = 1.57e27; //double-to-char,undefinedbehavior
char c2 = 459585821;//int-to-char,undefinedbehavior
然而,如果使用初始化列表语法,编译器将禁止进行这样的类型转换,即将值存储到比它“窄”的变量中:
char c1 {1.57e27};//double-to-char, compile-timeerror
char c2 = {459585821};//int-to-char,outofrange,compile-timeerror
但允许转换为更宽的类型。另外,只要值在较窄类型的取值范围内,将其转换为较窄的类型也是允许的:
char c1 {66};//int-to-char, in range,allowed
double c2 = {66};// int-to-double,allowed

3.3 std::initializer_list

C++11提供了模板类ntazlt,可将其用作构造函数的参数。如果类有接受iiierlit作为参数的构造函数,则初始化列表语法就只能用于该构造函数。列表中的元素必须是同一种类型或可转换为同一种类型。STL容器提供了将italer_lit作为参数的构造函数:

	vector<int> al(10);
	vector<int> a2(10);
	vector<int> a3 (461);

头文件iniializerlist提供了对模板类initalizer_list的支持。这个类包含成员函数begin()和end(),可用于获悉列表的范围。除用于构造函数外,还可将initializer_list用作常规函数的参数:

#incIude <initia1izerIist>

double sum(std::initializer_list<double>il)int main()
{
	double total = sum({2.53.1, 4})//4 converted to 4.0
}
double sum(std::initializer_list<double>il)
{
	double tot=0for(auto p = il.begin(); p !=il.end();p++)
		tot+=*p;
	return tot;
}

4 声明

C++11提供了多种简化声明的功能,尤其在使用模板时。

4.1 auto

以前,关键字auto是一个存储类型说明符,C++11将其用于实现自动类型推断,这要求进行显式初始化,让编译器能够将变量的类型设置为初始值的类型:

auto a1 =112; a1 is type int
auto a2 = &a1; a2 is type int *;
double a3(double, int);
auto pf = a3; //pf is type double(*)(double, int);

关键字auto还可简化模板声明。例如,如果il是一个std::initializer_list对象,则可将下述代码:

for(std::initializer_list<double>::iterator p =  il.begin();p != il.end();p++)

替换为如下代码:

for(auto p = il.begin();p != il.end();p++) 

4.2 decltype

关键字decltype将变量的类型声明为表达式指定的类型。下面的语句的含义是,让y的类型与x相同,其中X是一个表达式

注意: decltype后面的括号中不能填写数据类型。例如 decltype(int)为错误写法。

decltype(x)y;
下面是几个示例:

double x
int n;
decltype(x*n)q;  
decltype(&x) pd; 

在定义模板时特别有用,因为只有等到模板被实例化时才能确定类型:
这在定义模板时特别有用,因为只有等到模板被实例化时才能确定类型

template<typename T,typenameU)
voidef(Tt,Uu)
{
	decltype(T*U) tu;
}

其中tu将为表达式TU的类型,这里假定定义了运算TU.例如,如果T为char,U为shor,则tu将为it,这是由整型算术自动执行整型提升导致的。
decltype的工作原理比auto复杂,根据使用的表达式,指定的类型可以为引用和const。下面是几个示例:

int j = 3int &k = j;
const int n = j;
decltype(n) i1;//i1 type const int &
decltype(j) i2;// i2 type int
decltype((j)) i3 // i3 type int & 当我第一次接触这种写法的时候 这种写法是最让我匪夷所思的。decltype(k+1) i4; //i4 type int

4.3 返回类型后置

C++11新增了一种函数声明语法:在函数名和参数列表后面(而不是前面)指定返回类型:

double Fun1(double, int);
auto Fun2(double, int) ->double; //new style ,return type is double;

就常规函数的可读性而言,这种新语法好像是倒退,但让您能够使用dectype来指定模板函数的返回类型:

template<typename T1, typename T2>
auto fun(T1 a, T2 b) -> decltype(a * b)
{
 return a + b;
}

这里解决的问题是,在编译器遇到Fun的参数列表前,T和U还不在作用域内,因此必须在参数列表后使用decltype。这种新语法使得能够这样做。

4.4 模板别名:using =

对于冗长或复杂的标识符,如果能够创建其别名将很方便。以前,C++为此提供了typedef:

typedef std::vector<std::string>::iterator itType;

C++11提供了另一种创建别名的语法,这在第14章讨论过

using itType = std::vector<std::string>::iterator

差别在于,新语法也可用于模板部分具体化,但typedef不能:

template<typenameT>
using arr12=std::array<T,12>

上述语句具体化模板array<T,int>(将参数int设置为12)。例如,对于下述声明:

std::array<double12>al;
std::array<std::string,12>a2;

可将它们替换为如下声明:

arr12<double> al;
arr12<std::string> a2;

4.5 nullptr

空指针是不会指向有效数据的指针。以前,C++在源代码中使用0表示这种指针,但内部表示可能不同。这带来了一些问题,因为这使得0即可表示指针常量,又可表示整型常量。C++11新增了关键字nullptr,用于表示空指针;它是指针类型,不能转换为整型类型。为向后兼容,C++11仍允许使用0来表示空指针,因此表达式nullptr == 0为tue,但使用nullptr而不是0提供了更高的类型安全。
例如,可将0传递给接受int参数的函数,但如果您试图将nullptr传递给这样的函数,编译器将此视为错误。因此,出于清晰和安全考虑,请使用nullptr——如果您的编译器支持它。

5 智能指针

如果在程序中使用new从堆(自由存储区)分配内存,等到不再需要时,应使用delete将其释放。C++
引入了智能指针auto_ptr,以帮助自动完成这个过程。随后的编程体验(尤其是使用STL时)表明,需要三种智能指针:unique_ptr、sharedptr和weak_ptr.有更精致的机制。基于程序员的编程体验和BOOST库提供的解决方案,C++11摒弃了auto_ptr,并新增了所有新增的智能指针都能与STL容器和移动语义协同工作。

6 异常规范方面的修改

void f501(int)throw(bad_dog);
以前,C++提供了一种语法,可用于指出函数可能引发哪些异常(参见第15章):

void Fun(long, long) throw()void Fun1(int) throw(bad_log);

与auto_pr一样,从实际经验出发,异常规范的效果没有预期的好。因此,C++11放弃了异常规范,但是C++委员会指出函数不会引发异常有一定的价值,他们为此添加了关键字noexcep:

void Fun2(char ,char) noexcept
注意: vs2013貌似不支持noexcep关键字:  提示以下错误: error C3646: “noexcept”: 未知重写说明符

7 作用内枚举

传统的C++枚举提供了一种创建名称常量的方式,但其类型检查相当低级。另外,枚举名的作用域为枚举定义所属的作用域,这意味着如果在同一个作用域内定义两个枚举,它们的枚举成员不能同名。最后
枚举可能不是可完全移植的,因为不同的实现可能选择不同的底层类型。为解决这些问题,C++11新增了一种枚举。这种枚举使用class或struct定义:

enum old1{yes,no,maybe}enum class New1 {never, sometimers, often, always};
enum struct New2 {never,lever,sever}

新枚举要求进行显式限定,以免发生名称冲突。因此,引用特定枚举时,需要使用New:never和New2::never等。

8 模板和STL方面的修改

为改善模板和标准模板库的可用性,C++11做了多个改进;有些是库本身,有些与易用性相关。本章前面提到了模板别名和适用于STL的智能指针。

8.1 基于范围的for循环

对于内置数组以及包含方法begin()和end()的类(如std::string)和STL容器,基于范围的for循环(第5章和第16章讨论过)可简化为它们编写循环的工作。这种循环对数组或容器中的每个元素执行指定的操作:

double prices[5]=(4.9910.996.877.998.49)for(doublex:prices)
	std::cout<<X<<std::endl;
 

其中,x将依次为prices中每个元素的值。x的类型应与数组元素的类型匹配。一种更容易、更安全的方式是,使用auto来声明x,这样编译器将根据prices声明中的信息来推断x的类型:

double prices[5]= (4.9910.996.877.998.49)for(auto x :prices)
   	 std::cout<<X<<std::endl;

如果要在循环中修改数组或容器的每个元素,可使用引用类型

std::vector<int>vi(6)for(auto & x:vi) 
    x = std::rand()

8.2 新的STL容器

C+11新增加了STL容器forward_list,unordered_map,unordered_multimap,unorderedset。容器forward_it是一种单向链表,只能沿一个方向遍历;与双向链接的list容器相比它更简单,在占用存储空间方面更经济。其他四种容器都是使用哈希表实现的。
C++11还新增了模板aray。要实例化这种模板,可指定元素类型和固定的元素数:

std::array<int360> ar;

这个模板类没有满足所有的常规模板需求。例如,由于长度固定,您不能使用任何修改容器大小的方法,如putback(),但aray确实有方法begin()和end(),这让您能够对amay对象使用众多基于范围的STL算法。

8.3 新的STL方法

C++11新增了STL方法cbegin()和cend()。与begin()和end()一样,这些新方法也返回一个迭代器,指向容器的第一个元素和最后一个元素的后面,因此可用于指定包含全部元素的区间。另外,这些新方法将元素视为const。与此类似,crbegin()和crend()是rbegin()和rend()的const版本。
更重要的是,除传统的复制构造函数和常规赋值运算符外,STL容器现在还有移动构造函数和移动赋值运算符。移动语义将在本章后面介绍。

8.4 velaray升级

模板valarray独立于STL开发的,其最初的设计导致无法将基于范围的STL算法用于valamay对象。C++11添加了两个函数(begin()和end()),它们都接受valaray作为参数,并返回迭代器,这些代器分别指向valaray对象的第一个元素和最后一个元素后面。这让您能够将基于范围的STL算法用于valamzy

8.5 摒弃export

C++98新增了关键字export,旨在提供一种途径,让程序员能够将模板定义放在接口文件和实现文件,其中前者包含原型和模板声明,而后者包含模板函数和方法的定义。实践证明这不现实,因此C++11让了这种用法,但仍保留了关键字export,供以后使用。

8.6 尖括号

为避免与运算符>>混淆,C++要求在声明嵌套模板时使用空格将尖括号分开:

std::vector<std::list<int>   > vl;

C++11不再这样要求:

std::vector<std::list<int>vl;
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值