本文是《漫话C++0x》系列的第二篇,从本篇开始,我将正式为大家介绍C++0x新增的特性,帮助大家认识C++0x。由于C++0x增加的特性还是挺多的,限于篇幅原因,每一篇只能介绍大概4、5个特性,所以,要把所有的新特性介绍完,需要好几个篇幅。下面言归正传,开始今天要介绍的内容,首先从大家最常用到的基本特性讲起。
大家先看看下面的代码,一个我们比较熟悉的C++98中遍历map的例子:
1 2 3 4 5 |
std::map< std::string, std::vector<int> > mp; for (std::map< std::string, std::vector<int> >::iterator i = mp.begin(); i != mp.end(); ++i) { /// traverse the map } |
下面再来看一个C++0x改进版的同样的例子:
1 2 3 4 5 |
std::map< std::string, std::vector<int> > mp; for (auto i = mp.begin(); i != mp.end(); ++i) { /// traverse the map } |
通过对比,不难发现,用了auto之后,整个代码简洁了很多。其实auto可以用在很多地方,尤其是在复杂的多层嵌套模板中,用auto可以使代码简化不少,这个也是在这里选用map做例子的重要原因。另外auto前后还可以加各种修饰符,看下面的例子:
1 2 3 4 5 |
float data[BufSize]; /// data: float[BufSize] auto v3 = data; /// v3: float* auto& v4 = data; /// v4: float (&)[BufSize] auto x1 = 10; /// x1: int const auto *x2 = &x1; /// x2: const int* |
用过C++模板的朋友应该比较清楚,在C++98中,嵌套模板的闭合的多个”>”之间必须显式的加上空格,要不然编译会不通过,因为C++98的标准认为”>>”这个是右移运算符,用在模板中是非法的。值得大家高兴的是,C++0x已经把这个蹩脚的限制给取消了,以后”>>”也可以用在嵌套模板的闭合端了,没有限制了,请看下面的例子:
1 2 |
std::vector< std::list<int>> vi1; /// fine in C++0x, error in C++98 std::vector< std::list<int> > vi2; /// fine in C++0x and C++98 |
在具体介绍这个特性之前,我们首先来看一下,在C++98中,要遍历一个标准的容器是如何做的,这里以vector为例:
1 2 3 4 5 |
std::vector< int> v; for (std::vector< int>::iterator i = v.begin(); i != v.end(); ++i) { /// use iterator i to operate the element in v } |
下面再来看一下C++0x的改良版:
1 2 3 4 5 6 7 8 9 10 11 |
std::vector< int> v; for (int num: v) /// for (auto num: v) is also OK { /// num is the element in v } std::vector< int> v; for (int & num: v) /// for (auto & num: v) is also OK { /// num is the reference of the element in v } |
对比一下,上面的代码比之前的简洁了很多,对于写程序的人来说,能通过简洁的方式实现,决不会用冗长的方式,一方面简洁的方式开发效率要高,另一方面简洁的代码看着也赏心悦目。通过上面的例子,相信大家对基于范围的loop已经有了初步的认为,下面来介绍一下,什么样情况下可以使用基于范围的loop:
(1)首先,重中之重,基于范围的loop只能用在for loop中,不能用于while, do while的loop
(2)所有的C++0x中的容器(当然包括C++98中的容器)都可以使用
(3)可以用在初始化列表和正则表达式的匹配结果(这两个都是C++0x新特性,后面会介绍)
(4)用户自己定义的类型,如果支持begin(),end()及迭代器遍历也可以用
我们知道,在C++98中,空指针用NULL表示,而NULL只是define的一个值为0或0L的宏。在大多数情况下,NULL是没有问题的,但是在模板的参数自动推导中,NULL会被推导为int类型,这样就不能匹配指针类型了,请看下面的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
template< typename F, typename P > void DoAndCall(F func, P param) { /// do something func(param); } void Func(int * ptr); Func(0); /// ok Func(NULL); /// ok Func(nullptr); /// ok DoAndCall(Func, 0); /// error DoAndCall(Func, NULL); /// error DoAndCall(Func, nullptr); /// ok |
上面的例子中,传入NULL的时候,NULL被推导为int类型,而Func需要的是一个int *类型的参数,这时候编译就会有问题。也正是因为NULL的这个缺陷,C++0x引入了nullptr这个新的关键字,nullptr的类型是std::nullptr_t,它既具有原来NULL做为空指针的所有特性,还很好的解决了上面提到的问题。
c++98支持char和wchar两种字符类型,c++0x为了更好的支持unicode,新增了char16_t和char32_t类型,关于char16_t和char32_t的用法,和char/wchar很类似,看下面的例子:
1 2 3 4 5 6 7 8 9 10 |
'x' /// char 'x' L'x' /// wchar_t 'x' u'x' /// char16_t 'x', UCS-2 U'X' /// char32_t 'x' UCS-4 /// std::basic_string typedefs for all character types: std::string s1; /// std::basic_string< char> std::wstring s2; /// std::basic_string< wchar_t> std::u16string s3; /// std::basic_string< char16_t> std::u32string s4; /// std::basic_string< char32_t> |
在C++98中,有控制字符和转义字符的概念,像’\n’用来回车等,但有的时候,我们就想把像控制字符这样的特殊字符原样的传输出,不做特殊处理,这时候就需要转义,显得比较麻烦,尤其在正则表达式中,格外突出。C++0x增加了raw string这个概念,也叫原字符串,这样的字符串没有特殊字符,所有的字符都是普通字符,请看下面的例子:
1 2 |
std::string noNewlines(R"(\n\n)"); std::string cmd(R"(ls /home/docs | grep ".pdf")"); |
在上面的例子中,我们看到raw string的语法是 R”(…)”,但是有时候,在正则表达式中,”)会做为匹配的特征,这时候该怎么办呢?C++0x也考虑了这一点,raw string的分隔符可以灵活指定,看下面的例子:
1 2 |
std::regex re1(R"!("operator\(\)"|"operator->")!"); /// "operator()"| "operator->" std::regex re2(R"xyzzy("\([A-Za-z_]\w*\)")xyzzy"); /// "(identifier)" |
上面的例子中,第一个例子,用”!(…)!”做了分隔,第二个例子中,用”xyzzy(…)xyzzy”做了分隔,都是因为正规表达式本身就有)”。用户可以指定各种各样的分隔符,原则就是不跟raw string text中的字符不相冲突即可。
以上即是今天的所有内容,不知道大家有没觉得C++0x带来了让我们眼前一亮的内容,如果是这样子的话,那请继续关注本系列后面的文章吧,更多精彩的内容在后面,谢谢!
我来评几句
登录后评论已发表评论数()