这篇文章将从C++的模板空格、自动类型推导、基于范围的循环等等几个方面探讨C++11的新特性。
模板空格
在C++98当中,我们如果要定义一个优先队列,可能需要使用以下的代码
#include <iostream>
#include <vector>
#include <deque>
#include <queue>
using std::cout;
using std::endl;
using std::vector;
using std::deque;
using std::priority_queue;
int main ()
{
priority_queue<int, vector<int>,std::greater<int> > queue; //1
}
注意到,在C++98当中,在注释1所在的行,greater和>之间需要一个空格,否则C++编译器将会把两个连在一起的>运算符识别为一个>>运算符。
而在C++11中,我们可以不写这个空格
#include <iostream>
#include <vector>
#include <deque>
#include <queue>
using std::cout;
using std::endl;
using std::vector;
using std::deque;
using std::priority_queue;
int main ()
{
priority_queue<int, vector<int>,std::greater<int>> queue; //1
}
nullptr
C++11当中添加了关键字nullptr来表示空指针,而在C++98当中,我们只能够使用0或者NULL来表示空指针,而在有些时候,C++会将0和NULL识别为一个整数,而不是指针,从而导致程序与我们的预期不符,如下例:
#include <iostream>
using std::cout;
using std::endl;
void fun(int * p)
{
cout << "pointer!\n";
}
void fun(int x)
{
cout << "integer!\n";
}
int main ()
{
fun(NULL); // 1
}
当我们调用fun(NULL)的时候(注释1),与两个重载的函数都匹配,可能会导致编译不同通过,而我们使用nullptr来调用,就会调用指针作为参数的那个函数。
#include <iostream>
using std::cout;
using std::endl;
void fun(int * p)
{
cout << "pointer!\n";
}
void fun(int x)
{
cout << "integer!\n";
}
int main ()
{
fun(nullptr); // 1
}
程序输出poinnter!,与我们的预期是一致的。
自动类型推断
C++11还可以使用auto关键字进行自动的类型推断。
#include <iostream>
using std::cout;
using std::endl;
int main ()
{
auto x = 1;
auto y = 3.1415926;
auto z = 1ll;
auto a = x;
auto lambda = [x,y,z]() mutable -> int{
z *= y;
return x * 5;
};
}
根据赋值号右边的数据类型,auto可以推断出我们定义的左值的类型,比如,通过1这个整数,推断出x的类型为int,通过3.1415936这个浮点数,推断出y是一个double,通过1ll这个long long,推断出z是一个long long,通过x是int,推断出a也是int。最后,我们一般是很难写出一个λ表达式的类型的,于是借助auto可以让编译器帮我们自动的推断。
循环
C++11还提供了一种和java类似的for循环,如下所示
#include <iostream>
#include <string>
using std::cout;
using std::endl;
using std::string;
int main ()
{
string strings[5]{"hello world!","hello C++11!","weixin_45644430","$$$$","#######"};
for(auto str : strings) //1
cout << str << endl;
}
在上述代码当中,我们首先定义了一个字符串数组,然后遍历输出它。
在注释1所在的行,在for循环中,我们定义了一个str,我们没有明确地指定它的类型,而是使用了auto关键字,让编译器为我们推导str的类型,这里,编译器会为我们推出str的类型是string。
在循环的过程当中,会遍历strings数组当中的每一个string实例,然后将它赋值给str,然后在输出str。
如果我们要修改str会如何?看下面的代码:
#include <iostream>
#include <string>
using std::cout;
using std::endl;
using std::string;
int main ()
{
string strings[5]{"hello world!","hello C++11!","weixin_45644430","$$$$","#######"};
for(auto str : strings) //1
str += "---";
for(auto str : strings) //2
cout << str << endl;
}
在注释1所在的循环中,我们对str进行了修改,让它添加了后缀"—",然后在注释2所在的循环当中,我们遍历并输出str,结果我们发现strings数组中的字符串并没有被改变。
正如我之前所说,程序会遍历strings数组当中的每一个string实例,然后将它赋值给str,然后我们修改str,实际上修改的是strings数组中的字符串的一个拷贝,strings数组中的字符串并没有被修改。想要修改strings数组当中的字符串,我们需要将str改为引用,如下所示:
#include <iostream>
#include <string>
using std::cout;
using std::endl;
using std::string;
int main ()
{
string strings[5]{"hello world!","hello C++11!","weixin_45644430","$$$$","#######"};
for(auto & str : strings) //1
str += "---";
for(const auto & str : strings) //2
cout << str << endl;
}
在注释1处,我们将str改为了引用,使得我们可以直接修改strings当中的字符串,在注释2处,我们输出修改后的字符串,这里我们不对strings中的字符串进行改动,因此我们将引用声明为const。