C++11新特性总结 (一)

1. 概述

最近在看C++ Primer5 恰好看到一半,总结一下C++11里面确实加了不少新东西,若是没有任何了解,别说本身写了,看别人写的代码估计都会有些吃力。C++ Primer5是学习C++11的比较好的书籍。这篇文章仅总结关于C++11中的新东西,老的东西再也不赘述。本文的全部代码仅仅值列出关键代码,而且全部特性都已经用编译器验证过,个人编译环境 gcc 5.3.1  g++ 5.3.1 ,听说 4.7以上的版本已经支持大部分C++11的特性,VS系列的编译器对C++11的支持状况不甚了解,若是没有合适的编译器,能够点击这里 C++shell 这是一个在线的C++编译系统,里面有多个选项能够选择C++98,C++11,C++14等,能够在这里面验证C++11的正确性。html

2. long long 类型

long long 类型实际上没有在C++ 98中存在,而以后被C99标准收录,其实如今市面上大多数编译器是支持 long long 的,可是这个类型正式成为C++的标准类型是在C++11中。标准要求long long至少是64位也就是8个字节。一个字面常量使用LL后缀表示long long类型,使用ULL后缀表示unsigned long long 类型。程序员

3. 列表初始化

C++11中全面加入了列表初始化的功能,包括对vector,map,值类型,struct等等均可以使用列表初始化,还能够在函数中返回一个花括号括起来的列表,而在这以前咱们只能对数组进行列表初始化:shell

//数组列表初始化
int xx[5]={1,2,3,4,5};
int yy[]={6,7,8,9,0};

//值类型进行初始化
int a{10}; 
int b={10};
int c={10.123}; // 编译器报错,g++ 5.3.1当列表初始化用于值类型的时候,若是有精度损失,编译器会报错。

//列表初始化还能够用结构体
typedef struct Str{
   int x;
   int y;
}Str;
Str s = {10,20};

//列表初始化类,必须是public成员,若是含有私有成员会失败
class Cls{
public:
   int x;
   int y;
};
Cls c  = {10,20};

//vector不只可使用列表初始化,还可使用列表进行赋值,数组不能用列表赋值
vector<int>v1={1,2,3,4,5,6,7,8,9}; // 初始化
vector<int>v2;
v2={3,4,5,6,7}; //赋值

//map列表初始化
map<string ,int> m = {
      {"x",1},
      {"y",2},
      {"z",3}
};

//用函数返回初始化列表只展现关键代码,相关头文件自行添加
//同理结构体,类,map的返回也可使用初始化列表返回
vector<int> getVector()
{
  return {1,2,3,4,5};
}

int main()
{
  vector<int> v = getVector();
  cout<<v[0]<<v[1]<<v.size()<<endl;
  return 0 ;
}

4. nullptr 空指针

C++11中新加入的字面值表示不指向任何对象的空指针,之前咱们经常用一个预约义的宏NULL来表示空指针,实际上NULL的值是0,新标准推荐使用nullptr而不是NULL数组

5. constexpr变量

咱们在定义常量的时候通常使用const来定义,一个常量必须在定义的时候进行初始化,而且以后不可更改。一个常量必须使用一个常量表达式进行初始化,而且在编译期间就能够获得常量的值,可是如何肯定一个表达式就是常量表达式呢,这个一般是由程序员本身肯定的,例如:安全

const int a =20; 
//20是一个字面值,固然也是一个常量表达式,因此用20来为a赋值是没有问题的
//然而下面的代码也能够经过编译,g++ 5.3.1
int a = 20 ;
const int x =  a; 
int b[x]={0};

为常量x赋值的是一个变量a,这样作应该是不合理的,可是编译器没有报告任何错误,固然这种错误是显而易见的,可是在复杂的系统中如何判断一个表达式是不是常量表达式是很困难的,例如这里的a咱们一眼就能够判断其并非一个常量表达式。为此C++11提供了一个新的关键字constexpr,使用该关键字定义的常量,由编译器检查为其赋值的表达式是不是常量表达式,例如上面的代码改为:函数

int a = 20 ;
constexpr int x =  a; 

编译器编译的时候就会报错说a并非常量。显然constexpr关键字将常量表达式的检查转交给编译器处理,而不是程序员本身,因此使用constexpr定义常量要比const安全。学习

6. constexpr函数

普通的函数通常是不能用来为constexpr常量赋值的,可是C++11容许定义一种constexpr的函数,这种函数在编译期间就能够计算出结果,这样的函数是能够用来为constexpr赋值的。定义constexpr函数须要遵照一些约定,函数的返回类型以及全部形参的类型都应该是字面值,通常状况下函数体中必须有且只有一条return语句。spa

constexpr int size()
{
	return 42;
}

constexpr int si = size();

执行初始化的时候编译器将函数的调用替换成结果值,constexpr函数体中也能够出现除了return以外的其余语句,可是这些语句在运行时不该该执行任何操做,例如空语句,using声明等。constexpr函数容许其返回值并不是是一个字面值,例如:指针

constexpr int size(int s)
{
	return s*4;
}

int a = 20;
const int b = 30;
constexpr int c = 40;
constexpr int si = size(a);  //error a是一个变量因此函数返回的是一个可变的值
constexpr int si1 = size(20); //ok 函数返回的其实是一个常量
constexpr int si2 = size(b);  //ok
constexpr int si3 = size(c);  //ok

由上可知constexpr函数并不必定返回常量,若是应用于函数的参数是一个常量表达式则返回常量,不然返回变量,而该函数调用究竟是一个常量表达式仍是很是量表达式则由编译器来判断。这就是constexpr的好处。htm

7. using类型别名

类型别名其实早在C语言中就有了,通常状况下咱们使用关键字typedef来声明一个类型的别名,在C++11中增长了另外一种声明类型别名的方法就是使用using关键字,using关键字在C++11之前通常用来引用命名空间。

typedef int INT;  // 右侧符号表明左侧
using INT2 = int; // 左侧符号表明右侧

INT a = 20;
INT2 b = 30;

8. auto类型指示符

咱们定义一个变量的时候首先必须肯定该变量的类型,而不少时候并非咱们先须要一个变量而后为该变量赋值合适的数据,而是咱们有一个值可是咱们殊不知道该用什么类型的变量存储它,特别是C++的模版使用的很是普遍,有时候要定义一个变量,其类型是很复杂的会带有模版的类型参数,例如一个最多见的例子:

map<string ,int> m ; 
map<string,int>::iterator it = m.begin();

上面的例子中咱们定义了一个map<string,int>::iterator类型的变量来存放 m.begin()的值,这个例子相对来讲还不算困难可是我在开始使用map容器的时候也曾经被搞晕过,若是map是一个 map<string,double> 类型则须要定义一个  map<string.double>::iterator it 来存放了。特别是若是map和vector之间互相嵌套的状况就更容易弄错了。定义这种类型变量的另外一个缺点就是一个类型的名字每每会很长,试想一下程序代码中通篇都是这种变量声明,恐怕不会有几我的看着舒服吧。不过不要紧C++11为咱们定义了一个新的关键字 auto 用来定义变量,而变量的类型由编译器自动根据赋值的表达式推导出来,不须要咱们显示定义了。由于auto定义的变量的类型由编译器根据赋值的表达式推导,因此auto定义的变量必须有初始值,不然编译器无法肯定该变量的类型。

auto x = 20; // x 是int
auto y = 3.14; // y 是double
map<string ,int> m ; 
auto it = m.begin(); //it 是map<string,int>::iterator

这样是否是方便了很多,并且程序看起来更加简洁了
auto能够在一条语句中声明多个变量,可是要保证语句中的基础数据类型只有一个,例如:

auto i=10,*p=&i; // OK i是int,p是int*
auto a=10,b=3.14; // Error 类型是int仍是double ?

9. decltype类型指示符

有时候会有这样的需求,咱们须要知道一个表达式的类型,并使用该类型去定义一个变量,例如:

int a = 10;
int b = 20;
auto c = a + b; // OK a+b的类型是int,此时c的类型是int,而且c的值是 a+b

auto能够解决部分问题,例如咱们定义的变量的类型就是表达式 a+b 的类型,可是若是咱们仅仅须要定义一个与表达式 a+b 的类型相同的变量,可是咱们又不但愿将表达式a+b的值赋值给刚刚定义的变量,咱们但愿赋另一个值或者是仅仅定义变量而不赋值呢。 这就须要用到C++11 提供的另外一个类型说明符 decltype了。decltype做用于一个表达式,而且返回该表达式的类型,在此过程当中编译器分析表达式的类型,并不会计算表达式的值。例如

int a = 10;
int b = 20;
decltype(a+b) c = 50; // OK c的类型就是 a+b 的类型int

对于引用类型decltype有一些特别的地方:

int a = 20 ;
int &b = a;
decltype(b) c ;  // Error c是引用类型必须赋值
decltype(b) d = a; // OK  d是引用类型,指向a

能够看到decltype若是做用于一个引用类型,其获得的仍是一个引用类型。咱们知道一个引用类型在使用的时候通常会看成其关联的那个变量的同义词处理,例如若是使用 cout<<b<<endl; 其中b实际上至关于a,可是decltype做用于引用类型的时候会保留引用性质。

若是一个表达式是一个解指针引用的操做,decltype获得的也是一个引用类型:

int a = 20 ;
int *p = &a;
decltype(*p) c = a;  // c的类型是int&
c = 50;
cout<<a<<endl;  // 输出50

当decltype做用于一个变量的时候,变量加不加括号是有区别的,例如:

int a = 20;
decltype(a) b = 30; //ok b的类型是 int
decltype((a)) c = a ; // ok c的类型是int& 其关联变量 a

加上括号以后编译器会把(a)看成是一个表达式处理,而变量是一种能够做为赋值语句左值的表达式,因此会解释成引用类型。

C++11新特性(二)

若是您以为这篇文章对您有帮助,须要您的【赞】,让更多的人也能看见哦

相关文章
相关标签/搜索