C++11新功能使用详细介绍
2012-10-23 12:04
471 查看
C++11 正则表达式
基础知识介绍
C++11开始支持正则表达式,使得处理文本更加简洁方便。C++11 支持六种正则表达式语法:ECMAScript, basic(POSIX Basic Regular Expressions), extended(POSIX Extended Regular Expressions ), awk(POSIX awk) , grep(POSIX grep ), egrep(POSIX grep –E)。其中ECMAScript最为强大。
闲话不多说,首先来看正则表达式有哪些基本类型。
basic_regex: 这是一个包含一个正则表达式的模板类。通常有两种特化方式:
a) typedef basic_regex<char> regex;
b) typedef basic_regex<wchar_t> wregex;
1 . match_results: 这个类包含了与给定正则表达式匹配的序列。当empty()成员返回true或者size()成员返回0,表明没有找到匹配项。否则,当empty()返回false,size()返回值>=1 表明发生了匹配。此外:match[0]: 代表整个匹配序列 ;match[1]:代表第一个匹配子序列 ;match[2]: 代表第二个匹配子序列,以此类推。match_results有如下特化方式:
a) typedef match_results<const char*> cmatch;
b) typedef match_results<const wchar_t*> wcmatch;
c) typedef match_results<string::const_iterator> smatch;
d) typedef match_results<wstring::const_iterator> wsmatch;
2.sub_match: 该模板类用来表示与一个已标记的子表达式匹配的序列。这个匹配是通过一个迭代器对来表示的,该迭代器对表明了已匹配的正则表达式的一个范围。可以特化为下面几种情况:
a) typedef sub_match<const char*> csub_match;
b) typedef sub_match<const wchar_t*> wcsub_match;
c) typedef sub_match<string::const_iterator> ssub_match;
d) typedef sub_match<wstring::const_iterator> wssub_match;
以上介绍了一种常用的类型,叙述可能比较抽象,后面会结合例子来介绍这些类型的用法,还是会比较好理解。
然后来认识一下操作正则表达式的一些常用算法。
3.regex_match 判断一个正则表达式(参数 e)是否匹配整个字符序列 str. 它主要用于验证文本。注意,这个正则表达式必须匹配被分析串的全部,否则函数返回 false. 如果整个序列被成功匹配,regex_match 返回 True.
template <class charT, class Allocator, class traits >
bool regex_match(
const charT* str,
match_results<const charT*,Allocator>& m,
const basic_regex<charT, traits >& e,
match_flag_type flags = match_default );
4.regex_replace 在整个字符序列中查找正则表达式e的所有匹配。这个算法每次成功匹配后,就根据参数fmt对匹配字符串进行格式化。缺省情况下,不匹配的文本不会被修改,即文本会被输出但没有改变。
template <class traits,class charT>
basic_string<charT> regex_replace(
const basic_string<charT>& s,
const basic_regex<charT,traits >& e,
const basic_string<charT>& fmt,
match_flag_type flags = match_default);
5.regex_search 类似于 regex_match, 但它不要求整个字符序列完全匹配。你可以用 regex_search 来查找输入中的一个子序列,该子序列匹配正则表达式 e.
template <class charT,class Allocator, class traits>
bool regex_search(
const charT* str,
match_results<const charT*,Allocator>& m,
const basic_regex<charT,traits >& e,
match_flag_type flags = match_default);
迭代器介绍:正则表达式迭代器用来遍历这个正则表达式序列,通过一个迭代器区间来表示匹配的区间。
1. regex_iterator:
a) typedef regex_iterator<const
char*> cregex_iterator;
b) typedef regex_iterator<const
wchar_t*> wcregex_iterator;
c) typedef
regex_iterator<string::const_iterator> sregex_iterator;
d) typedef
regex_iterator<wstring::const_iterator> wsregex_iterator;
2.regex_token_iterator:
a) typedef
regex_token_iterator<const char*> cregex_token_iterator;
b) typedef regex_token_iterator<const
wchar_t*> wcregex_token_iterator;
c) typedef
regex_token_iterator<string::const_iterator> sregex_token_iterator;
d) typedef
regex_token_iterator<wstring::const_iterator> wsregex_token_iterator;
C++11 正则表达式——实例1
该实例通过一个函数is_email_valid 来检查一个email地址是否是一个正确的格式。如果格式正确则返回true。
#include <regex>
#include <iostream>
#include <string>
using namespace std;
bool is_email_valid(const string&
email)
{
const regex pattern("(\\w+)(\\.|_)?(\\w*)@(\\w+)(\\.(\\w+))+");
return regex_match(email, pattern);
}
int main()
{
string email1 =
"marius.bancila@domain.com";
string email2 =
"mariusbancila@domain.com";
string email3 =
"marius_b@domain.co.uk";
string email4 = "marius@domain";
cout << email1 << "
: " << (is_email_valid(email1) ?
"valid" :
"invalid") << endl;
cout << email2 << "
: " << (is_email_valid(email2) ?
"valid" :
"invalid") << endl;
cout << email3 << "
: " << (is_email_valid(email3) ?
"valid" :
"invalid") << endl;
cout << email4 << "
: " << (is_email_valid(email4) ?
"valid" :
"invalid") << endl;
return 0;
}
运行结果
略
这里对is_email_valid()函数中的正则表达式做一个简短的说明,如果对于正则表示不是很清楚的同学就能很容易理解了。
const regex pattern("(\\w+)(\\.|_)?(\\w*)@(\\w+)(\\.(\\w+))+");
首先注意‘()’表示将正则表达式分成子表达式,每个‘()’之间的内容表示一个子表达式;‘\’是一个转义字符,‘\\’表示扔掉第二个‘\’的转义特性,‘\w+’表示匹配一个或多个单词,‘+’表示重复一次或者多次,因此第一个子表达式的意思就是匹配一个或者多个单词;接着看第二个子表达式,‘|’表示选择,出现‘.’或者‘_’,后面的‘?’表示该子表示出现一次或者零次,因此第二个子表示表示‘.’或‘_’出现不出现都匹配。第三个子表达式表示出现一个单词,‘*’表示任意个字符。后面的子表达式根据已经介绍的内容,已经可以容易理解,就不再赘述。通过对正则表达式匹配模式串的分析,可以容易理解运行结果。
下面一个例子通过正则表达式识别和打印IP地址的各个部分:
#include <regex>
#include <iostream>
#include <string>
using namespace std;
void show_ip_parts(const string&
ip)
{
const regex pattern("(\\d{1,3}):(\\d{1,3}):(\\d{1,3}):(\\d{1,3})");
match_results<string::const_iterator>
result;
bool valid
= regex_match(ip, result, pattern);
cout << ip << " \t:
" << (valid ? "valid" : "invalid")
<< endl;
// if the IP address matched the
regex, then print the parts
if(valid)
{
cout << "b1: "
<< result[1] << endl;
cout << "b2: "
<< result[2] << endl;
cout << "b3: "
<< result[3] << endl;
cout << "b4: "
<< result[4] << endl;
}
}
int main()
{
show_ip_parts("1:22:33:444");
show_ip_parts("1:22:33:4444");
show_ip_parts("100:200");
return 0;
}
运行结果:
是对正则表达式的模式串做一个说明:首先还是通过‘()’将这个串分成几个子表达式,其中\d表示匹配一个数字,{,}表示数字的个数,例如{1,3}可以理解为匹配一个小于1000的数字(1-3位数都符合匹配要求)。
程序中还使用了match_results类,用来保存匹配的每一个子序列。调用regex_match(ip,result,pattern),表示将ip中与模式串pattern匹配的结果放在result中。
result最后可以通过下标来访问各个匹配的子表达式。
C++11 正则表达式——实例2
下面来介绍和regex_match()很像的regex_search()的使用实例,regex_match()要求正则表达式必须与模式串完全匹配,regex_search()只要求存在匹配项就可以。
#include <regex>
#include <iostream>
#include <string>
using namespace std;
int main()
{
const tr1::regex
pattern("(\\w+day)");
// the source text
string weekend = "Saturday and
Sunday";
smatch result;
bool match = regex_search(weekend,
result, pattern);
if(match)
{
for(size_t i = 1; i <
result.size(); ++i)
{
cout << result[i] <<
endl;
}
}
cout<<endl;
return 0;
}
运行结果:
上面这个例子只能返回第一个匹配的项,如果要返回所有匹配的子序列,可以使用下面的方式:
#include <regex>
#include <iostream>
#include <string>
int main()
{
// regular expression
const regex
pattern("\\w+day");
// the source text
string weekend = "Saturday and
Sunday, but some Fridays also.";
const
sregex_token_iterator end; //需要注意一下这里
for (sregex_token_iterator
i(weekend.begin(),weekend.end(), pattern); i != end ; ++i)
{
cout << *i << endl;
}
cout<<endl;
return 0;
}
运行结果:
下面的例子将元音字母打头的单词前面的a替换为an:
#include <regex>
#include <iostream>
#include <string>
int main()
{
// text to transform
string text = "This is a element
and this a unique ID.";
// regular expression with two
capture groups
const regex pattern("(\\ba
(a|e|i|u|o))+");
// the pattern for the
transformation, using the second
// capture group
string replace = "an $2";
string
newtext = regex_replace(text, pattern, replace);
cout << newtext << endl;
cout << endl;
return 0;
}
运行结果:
还是来说明一下,这里主要使用了regex_replace(text, pattern, replace),意思是将text的内容按照pattern进行匹配,匹配成功的使用replace串进行替换,并将替换后的结果作为函数值返回。需要注意的是string
replace = "an $2"; 这里‘$2’表示模式串的第二个子表达式,
也就是以a,e,i,o,u开头的单词。
C++11 正则表达式——实例3
下面一个例子将进行年月日格式的转换,将DD-MM-YYYY
–> YYYY-MM-DD,其中‘.’或者‘/’都能正确识别。
#include <regex>
#include <iostream>
#include <string>
using namespace std;
string format_date(const string&
date)
{
const
regex pattern("(\\d{1,2})(\\.|-|/)(\\d{1,2})(\\.|-|/)(\\d{4})");
string replacer =
"$5$4$3$2$1";
return regex_replace(date,
pattern, replacer);
}
int main()
{
string date1 = "1/2/2008";
string date2 =
"12.08.2008";
cout << date1 << "
-> " << format_date(date1) << endl;
cout << date2 << "
-> " << format_date(date2) << endl;
cout << endl;
return 0;
}
运行结果:
说明,这个例子也很有实用价值,这里用到的正则表达式的匹配模式前面都已经进行过说明就不在分析。
相信通过以上例子,对正则表达式的运用已经有了一个不错的了解,下面再来添加一个实例,加深一下理解。
下面一个例子用来查找给定文本中new的个数和delete的个数是否相等:
#include <iostream>
#include <string>
#include <regex>
using namespace std;
int main() {
// "new" and
"delete" 出现的次数是否一样?
regex
reg("(new)|(delete)");
smatch m;
string s= "Calls to new must be
followed by delete. \
Calling simply new results in a
leak!";
int new_counter=0;
int delete_counter=0;
string::const_iterator it=s.begin();
string::const_iterator end=s.end();
while (regex_search(it,end,m,reg))
{
// 是 new 还是 delete?
m[1].matched ? ++new_counter :
++delete_counter;
it=m[0].second;
}
if (new_counter!=delete_counter)
cout << "Leak
detected!\n";
else
cout << "Seems
ok...\n";
cout << endl;
}
运行结果:
运行结果表明,new和delete的数量不相等,也就是发生了“内存泄露”。
为了帮助理解,上面对于match_results类型的下标操作的意义,请看ISOIEC14882
C++11的说明:
#include <iostream>
#include <string>
#include <regex>
using namespace std;
class regex_callback {
int sum_;
public:
regex_callback() : sum_(0) {}
template <typename T> void operator()(const
T& what) {
sum_+=atoi(what[1].str().c_str());
}
int sum() const {
return sum_;
}
};
int main() {
regex reg("(\\d+),?");
string
s="1,1,2,3,5,8,13,21";
sregex_iterator
it(s.begin(),s.end(),reg);
sregex_iterator end;
regex_callback c;
int sum=for_each(it,end,c).sum();//for_each返回的是这个函数对象,因此可以调用sum
cout<<sum<<endl;
cout<<endl;
}
运行结果:
#include <iostream>
#include <string>
#include <regex>
using namespace std;
int main()
{
regex reg("/");
vector<string> vec;
string
s="Split/Vulue/Teather/Neusoft/Write/By/Lanwei";
sregex_token_iterator
it(s.begin(),s.end(),reg,-1);//// -1逆向匹配,就是匹配除了'/'之外的
sregex_token_iterator end ;
while(it!=end)
vec.push_back(*it++);
copy(vec.begin(),vec.end(),ostream_iterator<string>(
cout,"\n"));
}
运行结果:
C++11 多线程
注:vs2010对C++11支持不是很好,暂不支持C++11多线程,vs2011中已加入了对C++11的支持,增强了对C++11的支持。
从程序员的角度来看,C++11 最重要的特性就是并发了。C++11 提供了 thread 类,也提供了 promise 和 future 用以并发环境中的同步,用 async() 函数模板执行并发任务,和 thread_local
存储声明为特定线程独占的数据。
C++11开始支持多线程编程,之前多线程编程都需要系统的支持,在不同的系统下创建线程需要不同的API如pthread_create(),Createthread(),beginthread()等,使用起来都比较复杂,C++11提供了新头文件<thread>、<mutex>、<atomic>、<future>等用于支持多线程。
使用C++11开启一个线程是比较简单的,下面来看一个简单的例子:
#include <thread>
#include <iostream>
using namespace std;
void hello()
{
cout << "Hello from thread
" << endl;
}
int main()
{
thread t1(hello);
t1.join();
cout<<"Main
Thread"<<endl;
return 0;
}
运行结果:
说明,通过thread 类直接申明一个线程t1,参数是这个线程执行的回调函数的地址,通过jion()方法阻塞主线程,直到t1线程执行结束为止。
C++11支持Lambda表达式,因此一个新线程的回调函数也可以是有一个Lambda表达式的形式,但是注意如果使用Lambda表达式最好不要使用引用的方式,应该使用值传递的方式来访问数据,在多线程中使用引用容易造成混乱。下面这个例子稍微复杂,创建了多个子线程,并使用了get_id()方法来获取当前线程的id。
#include <thread>
#include <iostream>
#include <vector>
int main()
{
vector<thread> threads;
for(int i = 0; i < 5; ++i){
threads.push_back(thread([](){
cout << "Hello from lamda
thread " << this_thread::get_id() << endl;
}));
}
for(auto& thread : threads){
thread.join();
}
cout<<"Main
Thread"<<"\t"<<this_thread::get_id()<<endl;
return 0;
}
运行结果:
上述代码中,使用vector来存放每个线程,线程的回调函数通过Lambda表达式产生,注意后面join的使用方式。
可以通过sleep_for来使线程睡眠一定的时间:
#include <thread>
#include <iostream>
#include <mutex>
using namespace std;
int main()
{
mutex m;
thread t1([&m]()
{
this_thread::sleep_for
(chrono::seconds(10));
for(int i=0;i<10;i++)
{
m.lock();
cout << "In t1 ThreadID :
" << this_thread::get_id() << ":" << i
<< endl;
m.unlock ();
}
} );
thread t2([&m]()
{
this_thread::sleep_for
(chrono::seconds(1));
for(int i=0;i<10;i++)
{
m.lock ();
cout << "In t2 ThreadID :
" << this_thread::get_id() << ":" << i
<< endl;
m.unlock();
}
} );
t1.join();
t2.join();
cout<<"Main
Thread"<<endl;
return 0;
}
运行结果:
可以看出,由于线程t1睡眠的时间较长,t2先执行了。
延时有这几种类型:nanoseconds、microseconds、milliseconds、seconds、minutes、hours。
在使用多线程的程序中操作共享数据的时候一定要小心,由于线程的乱序执行,可能会得到意想不到的结果。通过下面的程序来看:
#include <thread>
#include <iostream>
#include <vector>
#include <mutex>
struct Counter {
mutex mutex;
int value;
Counter() : value(0) {}
void increment(){
// mutex.lock(); 【1】表示没有使用锁
++value;
// mutex.unlock(); 【1】
}
void decrement(){
mutex.lock();
--value;
mutex.unlock();
}
};
int main(){
Counter counter;
vector<thread> threads;
for(int i = 0; i < 5; ++i){
threads.push_back(thread([&](){
for(int i = 0; i < 10000; ++i){
counter.increment();
}
}));
}
for(auto& thread : threads){
thread.join();
}
cout << counter.value <<
endl;
return 0;
}
运行结果:
【1】
运行结果:(使用了锁)
说明:由于创建线程是使用lambda表达式,并使用引用的方式访问counter这个变量,当没有使用lock来保护的时候(情况【1】),执行的结果可能不像预期的5000(程序的意思是每个线程使counter中的value自加1000次,5个线程运行结束的时候应该是5000),当没有使用锁的时候自加的操作可能被其他线程打断,因此结果可能会小于5000。
C++11 lambda表达式
C++11引入了lambda表达式,使得程序员可以定义匿名函数,该函数是一次性执行的,既方便了编程,又能防止别人的访问。
Lambda表达式的语法通过下图来介绍:
各部分说明:
1Lambda表达式的引入标志,表示该lambda表达式“捕获”(lambda表达式在一定的scope可以访问的数据)数据时的方式。即:以何种方式将外部变量传入Lambda。
注意:区别于 Lambda表达式的参数列表中的参数
参数说明:
1、 空
没有使用任何函数对象参数。
2、 = 函数体内可以使用Lambda所在范围内的所有可见的局部变量(包括Lambda所在类的this),并以值传递的方式捕获,除非专门指出。
3、 &
函数体内可以使用Lambda所在范围内的所有可见的局部变量(包括Lambda所在类的this),并以引用的方式
4、 this
函数体内可以使用Lambda所在类中的成员变量
5、 a
将a按值进行传递,函数体内不能修改传递进来的a的拷贝,因为默认情况下函数式const的。要修改,可以添加mutaple修饰符。
6、 &a
将a按引用进行传递
7、 a, &b 将a按值传递,b按引用传递
8、 =,&a,&b 除a和b按引用传递外,其余参数都按值传递。注:=必须在前
9、 &,a,b
除a和b按值传递外,其余参数都按引用传递。注:&必须在前
2Lambda表达式的参数列表
-----------------可以省略
也就是一般函数的形参列表不过多加了一些限制:
不能有预设值
不能有可变长度的参数表 不能有没有名字的参数表
不需要传参数进Lambda时可以省略
3Mutable 标识 ---------- 可以省略
扩展Lambda的功能,其中mutable表示在函数体内Lambda将拷贝一份所用到的外部变量,他与外部变量值一样且可以改变,但并不能改变外部变量
4异常标识- ----------------可以省略
扩展Lambda的功能,表示在Lambda表达式内抛出异常。这其实是一般函数就可以加的功能,可以用来指定这个函数会不会丢出例外状况、丢出那种类型的例外状况
5返回值
----------------可以省略
当返回值为void,或者函数体中只有一处return的地方时,可以省略
6“函数”体,也就是lambda表达式需要进行的实际操作,不能省略,但可以为空
将上图的代码片段补充完整:
int x = 10;
int y = 3;
int z ;
z = [=]()mutable throw() -> int {
int n = x + y; x = y ; y = n; return n;}();
cout<<z<<endl;
cout<<"x:"<<x<<"\t"<<"y:"<<y<<endl;
运行结果为:
13
x: 10 y: 3
因为是以值传递的方式访问x,y所以x,y的值并没有发生改变
现在我们对lambda表达式的基本语法已经有一些了解,下面来举几个例子。
指定参数的传递
首先这个例子说明如何向lambda表达式里面传递参数:
#include <iostream>
using namespace std;
int main()
{
int n = [] (int x, int y) { return x
+ y; }(5, 4);
cout << n << endl;
}
运行结果为:9
通过这个例子我们可以看出,通过“函数体”后面的‘()’传入参数。
普通调用
接下来这个例子可以看出,可以像调用函数一样使用lambda表达式,但是感觉这种方式和普通函数的定义与调用就差不多了,这里只是学习使用方式而已。
#include <iostream>
using namespace std;
int main()
{
auto f = [] (int x, int y) { return x
+ y; };
cout << f(21, 12) <<
endl;
}
运行结果为:33
Lambda表达式与STL算法一起使用
自己写测试代码的时候经常用到排序、输出数组什么的,通过下面列举的几个算法也比较方便:
例1:
#include <iostream>
#include <algorithm>
#include <ctime>
using namespace std;
int main()
{
int a[10] = {0};
srand(time(NULL));
generate(a,a+10,[]()->int { return
rand() % 100; });
cout<<"before sort:
"<<endl;
for_each(a, a+10, [&](int i){
cout<< i <<" "; });
cout<<endl;
cout<<"After
sort"<<endl;
sort(a,a+10);
for_each(a, a+10, [&](int i){
cout<< i <<" "; });
return 0;
}
例2:计数字符串中有几个大写字母
#include <iostream>
#include<algorithm>
using namespace std;
int main()
{
char s[]="Hello World!";
int Uppercase = 0; //modified by the lambda
for_each(s, s+sizeof(s), [&Uppercase] (char c) { if (isupper(c)) Uppercase++; } );
cout<< Uppercase<<" uppercase letters in: "<< s<<endl;
}
Lambda表达式的嵌套
#include <iostream>
using namespace std;
int main()
{
int m = [](int x)
{ return [](int y) { return y * 2;
}(x) + 3; }(5);
cout << m << endl;
}
运行结果:13
以上代码在VC10和VC11上都能顺利编译通过。感觉lambda表达式还是比较有意思的语法,也是我接触的第一个VC11扩展。
C++11 tuple
tuple元组定义了一个有固定数目元素的容器,其中的每个元素类型都可以不相同,这与其他容器有着本质的区别.是对pair的泛化。
首先来介绍元组的创建和元组元素的访问。通过make_tuple()创建元组,通过get<>()来访问元组的元素。通过下面这段程序来认识这两个函数的用法:
#include <iostream>
#include <tuple>
#include <functional>
using namespace std;
int main()
{
auto t1 = make_tuple(10,
"Test", 3.14);
cout << "The value of t1
is "
<< "(" <<
get<0>(t1) << ", " << get<1>(t1)
<< ", " <<
get<2>(t1) << ")\n";
int n = 1;
auto t2 = make_tuple(ref(n), n);//ref表示引用
n = 7;
cout << "The value of t2
is "
<< "("
<<get<0>(t2) << ", " <<get<1>(t2)
<< ")\n";
}
运行结果为:
The value of t1 is (10, Test, 3.14)
The value of t2 is (7, 1)
接下来介绍tie()函数。 tie()函数可以将变量连接到一个给定的tuple上,生成一个元素类型全是引用的tuple,相当于make_tuple(ref(a),ref(b),…)。可以通过tie()函数的使用方便的对tuple进行“解包”操作。看下面的代码:
#include <iostream>
#include <tuple>
using namespace std;
int main ()
{
int myint;
char mychar;
float myfloat;
tuple<int,float,char> mytuple;
mytuple = make_tuple (10, 2.6, 'a');
// packing values into tuple
//tie (myint, ignore, mychar) =
mytuple; // unpacking tuple into variables 【1】
tie (myint,myfloat, mychar) =
mytuple;
cout << "myint contains:
" << myint << endl;
cout << "mychar contains:
" << mychar << endl;
cout << "myfloat contains:
"<< myfloat <<endl;
get<0>(mytuple) = 100;//修改tuple的值
cout <<"After assignment
myint contains: "<< get<0>(mytuple) << endl;
return 0;
}
运行结果:
myint contains: 10
mychar contains: a
myfloat contains: 2.6
After assignment myint contains: 100
注:正如【1】处我们可以使用ignore,从而不用关联tuple中的第二个元素.
最后介绍一个tuple_cat()函数,通过该函数可以将多个tuple连接起来形成一个tuple(注:在VC11中只能连接两个tuple并不是真正的多个tuple)。
#include <iostream>
#include <utility>
#include <string>
#include <tuple>
int main ()
{
tuple<float,string> mytuple
(3.14,"pi");
pair<int,char> mypair (10,'a');
auto myauto = tuple_cat ( mytuple,
mypair );
cout << "myauto contains:
" << endl;
cout << get<0>(myauto)
<< endl;
cout << get<1>(myauto)
<< endl;
cout << get<2>(myauto)
<< endl;
cout << get<3>(myauto)
<< endl;
return 0;
}
运行结果:
myauto contains:
3.14
pi
10
a
make it simple, make it happen
自动类型推导和
decltype
在 C++03 中,声明对象的同时必须指明其类型,其实大多数情况下,声明对象的同时也会包括一个初始值,C++11 在这种情况下就能够让你声明对象时不再指定类型了:
auto x=0; //0 是 int 类型,所以 x 也是 int 类型
auto c='a'; //char
auto d=0.5; //double
auto
national_debt=14400000000000LL;//long long
这个特性在对象的类型很大很长的时候很有用,如:
void func(const vector<int>
&vi)
{
vector<int>::const_iterator
ci=vi.begin();
}
那个迭代器可以声明为:
auto ci=vi.begin();
C++11 也提供了从对象或表达式中“俘获”类型的机制,新的操作符 decltype 可以从一个表达式中“俘获”其结果的类型并“返回”:
const vector<int> vi;
typedef decltype (vi.begin()) CIT;
CIT another_const_iterator;
统一的初始化语法
vs2010未实现
C++ 最少有 4 种不同的初始化形式,如括号内初始化:
std::string s("hello");
int m=int(); //default initialization
还有等号形式的:
std::string s="hello";
int x=5;
对于 POD 集合,又可以用大括号:
int arr[4]={0,1,2,3};
struct tm today={0};
最后还有构造函数的成员初始化:
struct S {
int x;
S(): x(0) {} };
这么多初始化形式,不仅菜鸟会搞得很头大,高手也吃不消。更惨的是 C++03 中居然不能初始化 POD 数组的类成员,也不能在使用 new[] 的时候初始 POD 数组。 C++11 就用大括号一统天下了:
class C
{
int a;
int b;
public:
C(int i, int j);
};
C c {0,0}; //C++11 only. 相当于 C c(0,0);
int* a = new int[3] { 1, 2, 0 };
/C++11 only
class X {
int a[4];
public:
X() : a{1,2,3,4} {} //C++11, 初始化数组成员
};
还有一大好事就是对于容器来说,终于可以摆脱 push_back() 调用了,C++11中可以直观地初始化容器了:
// C++11 container initializer
vector <string> vs={
"first", "second", "third"};
map singers =
{ {"Lady Gaga", "+1
(212) 555-7890"},
{"Beyonce Knowles",
"+1 (212) 555-0987"}};
而类中的数据成员初始化也得到了支持:
class C
{
int a=7; //C++11 only
public:
C();
};
deleted 函数和 defaulted 函数
vs2010未实现
像以下形式的函数:
struct A
{
A()=default; //C++11
virtual ~A()=default; //C++11
};
叫做 defaulted 函数,=default; 指示编译器生成该函数的默认实现。这有两个好处:一是让程序员轻松了,少敲键盘,二是有更好的性能。
与 defaulted 函数相对的就是 deleted 函数:
int func()=delete;
这货有一大用途就是实现 noncopyabe 防止对象拷贝,要想禁止拷贝,用 =deleted 声明一下两个关键的成员函数就可以了:
struct NoCopy
{
NoCopy & operator =( const NoCopy
& ) = delete;
NoCopy ( const NoCopy & ) =
delete;
};
NoCopy a;
NoCopy b(a); //编译错误,拷贝构造函数是 deleted 函数
nullptr
nullptr 是一个新的 C++ 关键字,它是空指针常量,它是用来替代高风险的 NULL 宏和 0 字面量的。nullptr 是强类型的:
void f(int); //#1
void f(char *);//#2
//C++03
f(0); //调用的是哪个 f?
//C++11
f(nullptr) //毫无疑问,调用的是 #2
所有跟指针有关的地方都可以用 nullptr,包括函数指针和成员指针:
const char *pc=str.c_str(); //data
pointers
if (pc!=nullptr)
cout<<pc<<endl;
int (A::*pmf)()=nullptr; //指向成员函数的指针
void (*pmf)()=nullptr; //指向函数的指针
委托构造函数
C++11 中构造函数可以调用同一个类的另一个构造函数:
class M //C++11 delegating
constructors
{
int x, y;
char *p;
public:
M(int v) : x(v), y(0), p(new char
[MAX]) {} //#1 target
M(): M(0)
{cout<<"delegating ctor"<<end;} //#2 delegating
#2 就是所谓的委托构造函数,调用了真正的构造函数 #1。
右值引用
在 C++03 中的引用类型是只绑定左值的,C++11 引用一个新的引用类型叫右值引用类型,它是绑定到右值的,如临时对象或字面量。
增加右值引用的主要原因是为了实现 move 语义。与传统的拷贝不同,move 的意思是目标对象“窃取”原对象的资源,并将源置于“空”状态。当拷贝一个对象时,其实代价昂贵且无必要,move 操作就可以替代它。如在 string 交换的时候,使用 move 意义就有巨大的性能提升,如原方案是这样的:
void naiveswap(string &a, string
& b)
{
string temp = a;
a=b;
b=temp;
}
这种方案很傻很天真,很慢,因为需要申请内存,然后拷贝字符,而 move 就只需要交换两个数据成员,无须申请、释放内存和拷贝字符数组:
void moveswapstr(string& empty,
string & filled)
{
//pseudo code, but you get the idea
size_t sz=empty.size();
const char *p= empty.data();
//move filled's resources to empty
empty.setsize(filled.size());
empty.setdata(filled.data());
//filled becomes empty
filled.setsize(sz);
1filled.setdata(p);
1}
要实现支持 move 的类,需要声明 move 构造函数和 move 赋值操作符,如下:
class Movable
{
Movable (Movable&&); //move
constructor
Movable&&
operator=(Movable&&); //move assignment operator
};
C++11 的标准库广泛使用 move 语义,很多算法和容器都已经使用 move 语义优化过了。
新的智能指针类
C++98 定义的唯一的智能指针类 auto_ptr 已经被弃用,C++11 引入了新的智能针对类 shared_ptr 和 unique_ptr。它们都是标准库的其它组件兼容,可以安全地把智能指针存入标准容器,也可以安全地用标准算法“倒腾”它们。
新算法
C++11标准库定义了新的算法,即模拟集合论领域的操作符all_of(),any_of(),none_of()。下列代码清单里,对[first,first+n]所限定的范围内,使用谓词方法ispositive()遍历检查每个元素,并且采用all_of(),any_of(),none_of()方法,来测定该范围整体的性质。
//are all of the elements positive?
all_of(first, first+n, ispositive());
//false
//is there at least one positive
element?
any_of(first, first+n,
ispositive());//true
// are none of the elements positive?
none_of(first, first+n,
ispositive()); //false
一类新型copy_n算法也出笼了。利用copy_n()复制含有5个成员的数组到另一数组,可说是小菜一碟。
#include
int source[5]={0,12,34,50,80};
int target[5];
//copy 5 elements from source to
target
copy_n(source,5,target);
算法iota负责创建有限的递增值序列,通过向*first表示的第一子项指定初始值,那么该值则被施加前缀++操作,以生成后序数据。
include
int a[5]={0};
char c[3]={0};
iota(a, a+5, 10); //changes a to
{10,11,12,13,14}
iota(c, c+3, 'a'); //{'a','b','c'}
C++11仍然缺少一些实用库,例如XML应用接口,sockets,图形用户界面,reflection——当然,还有合理的自动化废物回收器。新标准确实提供了大量新功能,使得C++更形可靠,高效,易于学习和使用。
基础知识介绍
C++11开始支持正则表达式,使得处理文本更加简洁方便。C++11 支持六种正则表达式语法:ECMAScript, basic(POSIX Basic Regular Expressions), extended(POSIX Extended Regular Expressions ), awk(POSIX awk) , grep(POSIX grep ), egrep(POSIX grep –E)。其中ECMAScript最为强大。
闲话不多说,首先来看正则表达式有哪些基本类型。
basic_regex: 这是一个包含一个正则表达式的模板类。通常有两种特化方式:
a) typedef basic_regex<char> regex;
b) typedef basic_regex<wchar_t> wregex;
1 . match_results: 这个类包含了与给定正则表达式匹配的序列。当empty()成员返回true或者size()成员返回0,表明没有找到匹配项。否则,当empty()返回false,size()返回值>=1 表明发生了匹配。此外:match[0]: 代表整个匹配序列 ;match[1]:代表第一个匹配子序列 ;match[2]: 代表第二个匹配子序列,以此类推。match_results有如下特化方式:
a) typedef match_results<const char*> cmatch;
b) typedef match_results<const wchar_t*> wcmatch;
c) typedef match_results<string::const_iterator> smatch;
d) typedef match_results<wstring::const_iterator> wsmatch;
2.sub_match: 该模板类用来表示与一个已标记的子表达式匹配的序列。这个匹配是通过一个迭代器对来表示的,该迭代器对表明了已匹配的正则表达式的一个范围。可以特化为下面几种情况:
a) typedef sub_match<const char*> csub_match;
b) typedef sub_match<const wchar_t*> wcsub_match;
c) typedef sub_match<string::const_iterator> ssub_match;
d) typedef sub_match<wstring::const_iterator> wssub_match;
以上介绍了一种常用的类型,叙述可能比较抽象,后面会结合例子来介绍这些类型的用法,还是会比较好理解。
然后来认识一下操作正则表达式的一些常用算法。
3.regex_match 判断一个正则表达式(参数 e)是否匹配整个字符序列 str. 它主要用于验证文本。注意,这个正则表达式必须匹配被分析串的全部,否则函数返回 false. 如果整个序列被成功匹配,regex_match 返回 True.
template <class charT, class Allocator, class traits >
bool regex_match(
const charT* str,
match_results<const charT*,Allocator>& m,
const basic_regex<charT, traits >& e,
match_flag_type flags = match_default );
4.regex_replace 在整个字符序列中查找正则表达式e的所有匹配。这个算法每次成功匹配后,就根据参数fmt对匹配字符串进行格式化。缺省情况下,不匹配的文本不会被修改,即文本会被输出但没有改变。
template <class traits,class charT>
basic_string<charT> regex_replace(
const basic_string<charT>& s,
const basic_regex<charT,traits >& e,
const basic_string<charT>& fmt,
match_flag_type flags = match_default);
5.regex_search 类似于 regex_match, 但它不要求整个字符序列完全匹配。你可以用 regex_search 来查找输入中的一个子序列,该子序列匹配正则表达式 e.
template <class charT,class Allocator, class traits>
bool regex_search(
const charT* str,
match_results<const charT*,Allocator>& m,
const basic_regex<charT,traits >& e,
match_flag_type flags = match_default);
迭代器介绍:正则表达式迭代器用来遍历这个正则表达式序列,通过一个迭代器区间来表示匹配的区间。
1. regex_iterator:
a) typedef regex_iterator<const
char*> cregex_iterator;
b) typedef regex_iterator<const
wchar_t*> wcregex_iterator;
c) typedef
regex_iterator<string::const_iterator> sregex_iterator;
d) typedef
regex_iterator<wstring::const_iterator> wsregex_iterator;
2.regex_token_iterator:
a) typedef
regex_token_iterator<const char*> cregex_token_iterator;
b) typedef regex_token_iterator<const
wchar_t*> wcregex_token_iterator;
c) typedef
regex_token_iterator<string::const_iterator> sregex_token_iterator;
d) typedef
regex_token_iterator<wstring::const_iterator> wsregex_token_iterator;
C++11 正则表达式——实例1
该实例通过一个函数is_email_valid 来检查一个email地址是否是一个正确的格式。如果格式正确则返回true。
#include <regex>
#include <iostream>
#include <string>
using namespace std;
bool is_email_valid(const string&
email)
{
const regex pattern("(\\w+)(\\.|_)?(\\w*)@(\\w+)(\\.(\\w+))+");
return regex_match(email, pattern);
}
int main()
{
string email1 =
"marius.bancila@domain.com";
string email2 =
"mariusbancila@domain.com";
string email3 =
"marius_b@domain.co.uk";
string email4 = "marius@domain";
cout << email1 << "
: " << (is_email_valid(email1) ?
"valid" :
"invalid") << endl;
cout << email2 << "
: " << (is_email_valid(email2) ?
"valid" :
"invalid") << endl;
cout << email3 << "
: " << (is_email_valid(email3) ?
"valid" :
"invalid") << endl;
cout << email4 << "
: " << (is_email_valid(email4) ?
"valid" :
"invalid") << endl;
return 0;
}
运行结果
略
这里对is_email_valid()函数中的正则表达式做一个简短的说明,如果对于正则表示不是很清楚的同学就能很容易理解了。
const regex pattern("(\\w+)(\\.|_)?(\\w*)@(\\w+)(\\.(\\w+))+");
首先注意‘()’表示将正则表达式分成子表达式,每个‘()’之间的内容表示一个子表达式;‘\’是一个转义字符,‘\\’表示扔掉第二个‘\’的转义特性,‘\w+’表示匹配一个或多个单词,‘+’表示重复一次或者多次,因此第一个子表达式的意思就是匹配一个或者多个单词;接着看第二个子表达式,‘|’表示选择,出现‘.’或者‘_’,后面的‘?’表示该子表示出现一次或者零次,因此第二个子表示表示‘.’或‘_’出现不出现都匹配。第三个子表达式表示出现一个单词,‘*’表示任意个字符。后面的子表达式根据已经介绍的内容,已经可以容易理解,就不再赘述。通过对正则表达式匹配模式串的分析,可以容易理解运行结果。
下面一个例子通过正则表达式识别和打印IP地址的各个部分:
#include <regex>
#include <iostream>
#include <string>
using namespace std;
void show_ip_parts(const string&
ip)
{
const regex pattern("(\\d{1,3}):(\\d{1,3}):(\\d{1,3}):(\\d{1,3})");
match_results<string::const_iterator>
result;
bool valid
= regex_match(ip, result, pattern);
cout << ip << " \t:
" << (valid ? "valid" : "invalid")
<< endl;
// if the IP address matched the
regex, then print the parts
if(valid)
{
cout << "b1: "
<< result[1] << endl;
cout << "b2: "
<< result[2] << endl;
cout << "b3: "
<< result[3] << endl;
cout << "b4: "
<< result[4] << endl;
}
}
int main()
{
show_ip_parts("1:22:33:444");
show_ip_parts("1:22:33:4444");
show_ip_parts("100:200");
return 0;
}
运行结果:
是对正则表达式的模式串做一个说明:首先还是通过‘()’将这个串分成几个子表达式,其中\d表示匹配一个数字,{,}表示数字的个数,例如{1,3}可以理解为匹配一个小于1000的数字(1-3位数都符合匹配要求)。
程序中还使用了match_results类,用来保存匹配的每一个子序列。调用regex_match(ip,result,pattern),表示将ip中与模式串pattern匹配的结果放在result中。
result最后可以通过下标来访问各个匹配的子表达式。
C++11 正则表达式——实例2
下面来介绍和regex_match()很像的regex_search()的使用实例,regex_match()要求正则表达式必须与模式串完全匹配,regex_search()只要求存在匹配项就可以。
#include <regex>
#include <iostream>
#include <string>
using namespace std;
int main()
{
const tr1::regex
pattern("(\\w+day)");
// the source text
string weekend = "Saturday and
Sunday";
smatch result;
bool match = regex_search(weekend,
result, pattern);
if(match)
{
for(size_t i = 1; i <
result.size(); ++i)
{
cout << result[i] <<
endl;
}
}
cout<<endl;
return 0;
}
运行结果:
上面这个例子只能返回第一个匹配的项,如果要返回所有匹配的子序列,可以使用下面的方式:
#include <regex>
#include <iostream>
#include <string>
int main()
{
// regular expression
const regex
pattern("\\w+day");
// the source text
string weekend = "Saturday and
Sunday, but some Fridays also.";
const
sregex_token_iterator end; //需要注意一下这里
for (sregex_token_iterator
i(weekend.begin(),weekend.end(), pattern); i != end ; ++i)
{
cout << *i << endl;
}
cout<<endl;
return 0;
}
运行结果:
下面的例子将元音字母打头的单词前面的a替换为an:
#include <regex>
#include <iostream>
#include <string>
int main()
{
// text to transform
string text = "This is a element
and this a unique ID.";
// regular expression with two
capture groups
const regex pattern("(\\ba
(a|e|i|u|o))+");
// the pattern for the
transformation, using the second
// capture group
string replace = "an $2";
string
newtext = regex_replace(text, pattern, replace);
cout << newtext << endl;
cout << endl;
return 0;
}
运行结果:
还是来说明一下,这里主要使用了regex_replace(text, pattern, replace),意思是将text的内容按照pattern进行匹配,匹配成功的使用replace串进行替换,并将替换后的结果作为函数值返回。需要注意的是string
replace = "an $2"; 这里‘$2’表示模式串的第二个子表达式,
也就是以a,e,i,o,u开头的单词。
C++11 正则表达式——实例3
下面一个例子将进行年月日格式的转换,将DD-MM-YYYY
–> YYYY-MM-DD,其中‘.’或者‘/’都能正确识别。
#include <regex>
#include <iostream>
#include <string>
using namespace std;
string format_date(const string&
date)
{
const
regex pattern("(\\d{1,2})(\\.|-|/)(\\d{1,2})(\\.|-|/)(\\d{4})");
string replacer =
"$5$4$3$2$1";
return regex_replace(date,
pattern, replacer);
}
int main()
{
string date1 = "1/2/2008";
string date2 =
"12.08.2008";
cout << date1 << "
-> " << format_date(date1) << endl;
cout << date2 << "
-> " << format_date(date2) << endl;
cout << endl;
return 0;
}
运行结果:
说明,这个例子也很有实用价值,这里用到的正则表达式的匹配模式前面都已经进行过说明就不在分析。
相信通过以上例子,对正则表达式的运用已经有了一个不错的了解,下面再来添加一个实例,加深一下理解。
下面一个例子用来查找给定文本中new的个数和delete的个数是否相等:
#include <iostream>
#include <string>
#include <regex>
using namespace std;
int main() {
// "new" and
"delete" 出现的次数是否一样?
regex
reg("(new)|(delete)");
smatch m;
string s= "Calls to new must be
followed by delete. \
Calling simply new results in a
leak!";
int new_counter=0;
int delete_counter=0;
string::const_iterator it=s.begin();
string::const_iterator end=s.end();
while (regex_search(it,end,m,reg))
{
// 是 new 还是 delete?
m[1].matched ? ++new_counter :
++delete_counter;
it=m[0].second;
}
if (new_counter!=delete_counter)
cout << "Leak
detected!\n";
else
cout << "Seems
ok...\n";
cout << endl;
}
运行结果:
运行结果表明,new和delete的数量不相等,也就是发生了“内存泄露”。
为了帮助理解,上面对于match_results类型的下标操作的意义,请看ISOIEC14882
C++11的说明:
#include <iostream>
#include <string>
#include <regex>
using namespace std;
class regex_callback {
int sum_;
public:
regex_callback() : sum_(0) {}
template <typename T> void operator()(const
T& what) {
sum_+=atoi(what[1].str().c_str());
}
int sum() const {
return sum_;
}
};
int main() {
regex reg("(\\d+),?");
string
s="1,1,2,3,5,8,13,21";
sregex_iterator
it(s.begin(),s.end(),reg);
sregex_iterator end;
regex_callback c;
int sum=for_each(it,end,c).sum();//for_each返回的是这个函数对象,因此可以调用sum
cout<<sum<<endl;
cout<<endl;
}
运行结果:
#include <iostream>
#include <string>
#include <regex>
using namespace std;
int main()
{
regex reg("/");
vector<string> vec;
string
s="Split/Vulue/Teather/Neusoft/Write/By/Lanwei";
sregex_token_iterator
it(s.begin(),s.end(),reg,-1);//// -1逆向匹配,就是匹配除了'/'之外的
sregex_token_iterator end ;
while(it!=end)
vec.push_back(*it++);
copy(vec.begin(),vec.end(),ostream_iterator<string>(
cout,"\n"));
}
运行结果:
C++11 多线程
注:vs2010对C++11支持不是很好,暂不支持C++11多线程,vs2011中已加入了对C++11的支持,增强了对C++11的支持。
从程序员的角度来看,C++11 最重要的特性就是并发了。C++11 提供了 thread 类,也提供了 promise 和 future 用以并发环境中的同步,用 async() 函数模板执行并发任务,和 thread_local
存储声明为特定线程独占的数据。
C++11开始支持多线程编程,之前多线程编程都需要系统的支持,在不同的系统下创建线程需要不同的API如pthread_create(),Createthread(),beginthread()等,使用起来都比较复杂,C++11提供了新头文件<thread>、<mutex>、<atomic>、<future>等用于支持多线程。
使用C++11开启一个线程是比较简单的,下面来看一个简单的例子:
#include <thread>
#include <iostream>
using namespace std;
void hello()
{
cout << "Hello from thread
" << endl;
}
int main()
{
thread t1(hello);
t1.join();
cout<<"Main
Thread"<<endl;
return 0;
}
运行结果:
说明,通过thread 类直接申明一个线程t1,参数是这个线程执行的回调函数的地址,通过jion()方法阻塞主线程,直到t1线程执行结束为止。
C++11支持Lambda表达式,因此一个新线程的回调函数也可以是有一个Lambda表达式的形式,但是注意如果使用Lambda表达式最好不要使用引用的方式,应该使用值传递的方式来访问数据,在多线程中使用引用容易造成混乱。下面这个例子稍微复杂,创建了多个子线程,并使用了get_id()方法来获取当前线程的id。
#include <thread>
#include <iostream>
#include <vector>
int main()
{
vector<thread> threads;
for(int i = 0; i < 5; ++i){
threads.push_back(thread([](){
cout << "Hello from lamda
thread " << this_thread::get_id() << endl;
}));
}
for(auto& thread : threads){
thread.join();
}
cout<<"Main
Thread"<<"\t"<<this_thread::get_id()<<endl;
return 0;
}
运行结果:
上述代码中,使用vector来存放每个线程,线程的回调函数通过Lambda表达式产生,注意后面join的使用方式。
可以通过sleep_for来使线程睡眠一定的时间:
#include <thread>
#include <iostream>
#include <mutex>
using namespace std;
int main()
{
mutex m;
thread t1([&m]()
{
this_thread::sleep_for
(chrono::seconds(10));
for(int i=0;i<10;i++)
{
m.lock();
cout << "In t1 ThreadID :
" << this_thread::get_id() << ":" << i
<< endl;
m.unlock ();
}
} );
thread t2([&m]()
{
this_thread::sleep_for
(chrono::seconds(1));
for(int i=0;i<10;i++)
{
m.lock ();
cout << "In t2 ThreadID :
" << this_thread::get_id() << ":" << i
<< endl;
m.unlock();
}
} );
t1.join();
t2.join();
cout<<"Main
Thread"<<endl;
return 0;
}
运行结果:
可以看出,由于线程t1睡眠的时间较长,t2先执行了。
延时有这几种类型:nanoseconds、microseconds、milliseconds、seconds、minutes、hours。
在使用多线程的程序中操作共享数据的时候一定要小心,由于线程的乱序执行,可能会得到意想不到的结果。通过下面的程序来看:
#include <thread>
#include <iostream>
#include <vector>
#include <mutex>
struct Counter {
mutex mutex;
int value;
Counter() : value(0) {}
void increment(){
// mutex.lock(); 【1】表示没有使用锁
++value;
// mutex.unlock(); 【1】
}
void decrement(){
mutex.lock();
--value;
mutex.unlock();
}
};
int main(){
Counter counter;
vector<thread> threads;
for(int i = 0; i < 5; ++i){
threads.push_back(thread([&](){
for(int i = 0; i < 10000; ++i){
counter.increment();
}
}));
}
for(auto& thread : threads){
thread.join();
}
cout << counter.value <<
endl;
return 0;
}
运行结果:
【1】
运行结果:(使用了锁)
说明:由于创建线程是使用lambda表达式,并使用引用的方式访问counter这个变量,当没有使用lock来保护的时候(情况【1】),执行的结果可能不像预期的5000(程序的意思是每个线程使counter中的value自加1000次,5个线程运行结束的时候应该是5000),当没有使用锁的时候自加的操作可能被其他线程打断,因此结果可能会小于5000。
C++11 lambda表达式
C++11引入了lambda表达式,使得程序员可以定义匿名函数,该函数是一次性执行的,既方便了编程,又能防止别人的访问。
Lambda表达式的语法通过下图来介绍:
各部分说明:
1Lambda表达式的引入标志,表示该lambda表达式“捕获”(lambda表达式在一定的scope可以访问的数据)数据时的方式。即:以何种方式将外部变量传入Lambda。
注意:区别于 Lambda表达式的参数列表中的参数
参数说明:
1、 空
没有使用任何函数对象参数。
2、 = 函数体内可以使用Lambda所在范围内的所有可见的局部变量(包括Lambda所在类的this),并以值传递的方式捕获,除非专门指出。
3、 &
函数体内可以使用Lambda所在范围内的所有可见的局部变量(包括Lambda所在类的this),并以引用的方式
4、 this
函数体内可以使用Lambda所在类中的成员变量
5、 a
将a按值进行传递,函数体内不能修改传递进来的a的拷贝,因为默认情况下函数式const的。要修改,可以添加mutaple修饰符。
6、 &a
将a按引用进行传递
7、 a, &b 将a按值传递,b按引用传递
8、 =,&a,&b 除a和b按引用传递外,其余参数都按值传递。注:=必须在前
9、 &,a,b
除a和b按值传递外,其余参数都按引用传递。注:&必须在前
2Lambda表达式的参数列表
-----------------可以省略
也就是一般函数的形参列表不过多加了一些限制:
不能有预设值
不能有可变长度的参数表 不能有没有名字的参数表
不需要传参数进Lambda时可以省略
3Mutable 标识 ---------- 可以省略
扩展Lambda的功能,其中mutable表示在函数体内Lambda将拷贝一份所用到的外部变量,他与外部变量值一样且可以改变,但并不能改变外部变量
4异常标识- ----------------可以省略
扩展Lambda的功能,表示在Lambda表达式内抛出异常。这其实是一般函数就可以加的功能,可以用来指定这个函数会不会丢出例外状况、丢出那种类型的例外状况
5返回值
----------------可以省略
当返回值为void,或者函数体中只有一处return的地方时,可以省略
6“函数”体,也就是lambda表达式需要进行的实际操作,不能省略,但可以为空
将上图的代码片段补充完整:
int x = 10;
int y = 3;
int z ;
z = [=]()mutable throw() -> int {
int n = x + y; x = y ; y = n; return n;}();
cout<<z<<endl;
cout<<"x:"<<x<<"\t"<<"y:"<<y<<endl;
运行结果为:
13
x: 10 y: 3
因为是以值传递的方式访问x,y所以x,y的值并没有发生改变
现在我们对lambda表达式的基本语法已经有一些了解,下面来举几个例子。
指定参数的传递
首先这个例子说明如何向lambda表达式里面传递参数:
#include <iostream>
using namespace std;
int main()
{
int n = [] (int x, int y) { return x
+ y; }(5, 4);
cout << n << endl;
}
运行结果为:9
通过这个例子我们可以看出,通过“函数体”后面的‘()’传入参数。
普通调用
接下来这个例子可以看出,可以像调用函数一样使用lambda表达式,但是感觉这种方式和普通函数的定义与调用就差不多了,这里只是学习使用方式而已。
#include <iostream>
using namespace std;
int main()
{
auto f = [] (int x, int y) { return x
+ y; };
cout << f(21, 12) <<
endl;
}
运行结果为:33
Lambda表达式与STL算法一起使用
自己写测试代码的时候经常用到排序、输出数组什么的,通过下面列举的几个算法也比较方便:
例1:
#include <iostream>
#include <algorithm>
#include <ctime>
using namespace std;
int main()
{
int a[10] = {0};
srand(time(NULL));
generate(a,a+10,[]()->int { return
rand() % 100; });
cout<<"before sort:
"<<endl;
for_each(a, a+10, [&](int i){
cout<< i <<" "; });
cout<<endl;
cout<<"After
sort"<<endl;
sort(a,a+10);
for_each(a, a+10, [&](int i){
cout<< i <<" "; });
return 0;
}
例2:计数字符串中有几个大写字母
#include <iostream>
#include<algorithm>
using namespace std;
int main()
{
char s[]="Hello World!";
int Uppercase = 0; //modified by the lambda
for_each(s, s+sizeof(s), [&Uppercase] (char c) { if (isupper(c)) Uppercase++; } );
cout<< Uppercase<<" uppercase letters in: "<< s<<endl;
}
Lambda表达式的嵌套
#include <iostream>
using namespace std;
int main()
{
int m = [](int x)
{ return [](int y) { return y * 2;
}(x) + 3; }(5);
cout << m << endl;
}
运行结果:13
以上代码在VC10和VC11上都能顺利编译通过。感觉lambda表达式还是比较有意思的语法,也是我接触的第一个VC11扩展。
C++11 tuple
tuple元组定义了一个有固定数目元素的容器,其中的每个元素类型都可以不相同,这与其他容器有着本质的区别.是对pair的泛化。
首先来介绍元组的创建和元组元素的访问。通过make_tuple()创建元组,通过get<>()来访问元组的元素。通过下面这段程序来认识这两个函数的用法:
#include <iostream>
#include <tuple>
#include <functional>
using namespace std;
int main()
{
auto t1 = make_tuple(10,
"Test", 3.14);
cout << "The value of t1
is "
<< "(" <<
get<0>(t1) << ", " << get<1>(t1)
<< ", " <<
get<2>(t1) << ")\n";
int n = 1;
auto t2 = make_tuple(ref(n), n);//ref表示引用
n = 7;
cout << "The value of t2
is "
<< "("
<<get<0>(t2) << ", " <<get<1>(t2)
<< ")\n";
}
运行结果为:
The value of t1 is (10, Test, 3.14)
The value of t2 is (7, 1)
接下来介绍tie()函数。 tie()函数可以将变量连接到一个给定的tuple上,生成一个元素类型全是引用的tuple,相当于make_tuple(ref(a),ref(b),…)。可以通过tie()函数的使用方便的对tuple进行“解包”操作。看下面的代码:
#include <iostream>
#include <tuple>
using namespace std;
int main ()
{
int myint;
char mychar;
float myfloat;
tuple<int,float,char> mytuple;
mytuple = make_tuple (10, 2.6, 'a');
// packing values into tuple
//tie (myint, ignore, mychar) =
mytuple; // unpacking tuple into variables 【1】
tie (myint,myfloat, mychar) =
mytuple;
cout << "myint contains:
" << myint << endl;
cout << "mychar contains:
" << mychar << endl;
cout << "myfloat contains:
"<< myfloat <<endl;
get<0>(mytuple) = 100;//修改tuple的值
cout <<"After assignment
myint contains: "<< get<0>(mytuple) << endl;
return 0;
}
运行结果:
myint contains: 10
mychar contains: a
myfloat contains: 2.6
After assignment myint contains: 100
注:正如【1】处我们可以使用ignore,从而不用关联tuple中的第二个元素.
最后介绍一个tuple_cat()函数,通过该函数可以将多个tuple连接起来形成一个tuple(注:在VC11中只能连接两个tuple并不是真正的多个tuple)。
#include <iostream>
#include <utility>
#include <string>
#include <tuple>
int main ()
{
tuple<float,string> mytuple
(3.14,"pi");
pair<int,char> mypair (10,'a');
auto myauto = tuple_cat ( mytuple,
mypair );
cout << "myauto contains:
" << endl;
cout << get<0>(myauto)
<< endl;
cout << get<1>(myauto)
<< endl;
cout << get<2>(myauto)
<< endl;
cout << get<3>(myauto)
<< endl;
return 0;
}
运行结果:
myauto contains:
3.14
pi
10
a
make it simple, make it happen
自动类型推导和
decltype
在 C++03 中,声明对象的同时必须指明其类型,其实大多数情况下,声明对象的同时也会包括一个初始值,C++11 在这种情况下就能够让你声明对象时不再指定类型了:
auto x=0; //0 是 int 类型,所以 x 也是 int 类型
auto c='a'; //char
auto d=0.5; //double
auto
national_debt=14400000000000LL;//long long
这个特性在对象的类型很大很长的时候很有用,如:
void func(const vector<int>
&vi)
{
vector<int>::const_iterator
ci=vi.begin();
}
那个迭代器可以声明为:
auto ci=vi.begin();
C++11 也提供了从对象或表达式中“俘获”类型的机制,新的操作符 decltype 可以从一个表达式中“俘获”其结果的类型并“返回”:
const vector<int> vi;
typedef decltype (vi.begin()) CIT;
CIT another_const_iterator;
统一的初始化语法
vs2010未实现
C++ 最少有 4 种不同的初始化形式,如括号内初始化:
std::string s("hello");
int m=int(); //default initialization
还有等号形式的:
std::string s="hello";
int x=5;
对于 POD 集合,又可以用大括号:
int arr[4]={0,1,2,3};
struct tm today={0};
最后还有构造函数的成员初始化:
struct S {
int x;
S(): x(0) {} };
这么多初始化形式,不仅菜鸟会搞得很头大,高手也吃不消。更惨的是 C++03 中居然不能初始化 POD 数组的类成员,也不能在使用 new[] 的时候初始 POD 数组。 C++11 就用大括号一统天下了:
class C
{
int a;
int b;
public:
C(int i, int j);
};
C c {0,0}; //C++11 only. 相当于 C c(0,0);
int* a = new int[3] { 1, 2, 0 };
/C++11 only
class X {
int a[4];
public:
X() : a{1,2,3,4} {} //C++11, 初始化数组成员
};
还有一大好事就是对于容器来说,终于可以摆脱 push_back() 调用了,C++11中可以直观地初始化容器了:
// C++11 container initializer
vector <string> vs={
"first", "second", "third"};
map singers =
{ {"Lady Gaga", "+1
(212) 555-7890"},
{"Beyonce Knowles",
"+1 (212) 555-0987"}};
而类中的数据成员初始化也得到了支持:
class C
{
int a=7; //C++11 only
public:
C();
};
deleted 函数和 defaulted 函数
vs2010未实现
像以下形式的函数:
struct A
{
A()=default; //C++11
virtual ~A()=default; //C++11
};
叫做 defaulted 函数,=default; 指示编译器生成该函数的默认实现。这有两个好处:一是让程序员轻松了,少敲键盘,二是有更好的性能。
与 defaulted 函数相对的就是 deleted 函数:
int func()=delete;
这货有一大用途就是实现 noncopyabe 防止对象拷贝,要想禁止拷贝,用 =deleted 声明一下两个关键的成员函数就可以了:
struct NoCopy
{
NoCopy & operator =( const NoCopy
& ) = delete;
NoCopy ( const NoCopy & ) =
delete;
};
NoCopy a;
NoCopy b(a); //编译错误,拷贝构造函数是 deleted 函数
nullptr
nullptr 是一个新的 C++ 关键字,它是空指针常量,它是用来替代高风险的 NULL 宏和 0 字面量的。nullptr 是强类型的:
void f(int); //#1
void f(char *);//#2
//C++03
f(0); //调用的是哪个 f?
//C++11
f(nullptr) //毫无疑问,调用的是 #2
所有跟指针有关的地方都可以用 nullptr,包括函数指针和成员指针:
const char *pc=str.c_str(); //data
pointers
if (pc!=nullptr)
cout<<pc<<endl;
int (A::*pmf)()=nullptr; //指向成员函数的指针
void (*pmf)()=nullptr; //指向函数的指针
委托构造函数
C++11 中构造函数可以调用同一个类的另一个构造函数:
class M //C++11 delegating
constructors
{
int x, y;
char *p;
public:
M(int v) : x(v), y(0), p(new char
[MAX]) {} //#1 target
M(): M(0)
{cout<<"delegating ctor"<<end;} //#2 delegating
#2 就是所谓的委托构造函数,调用了真正的构造函数 #1。
右值引用
在 C++03 中的引用类型是只绑定左值的,C++11 引用一个新的引用类型叫右值引用类型,它是绑定到右值的,如临时对象或字面量。
增加右值引用的主要原因是为了实现 move 语义。与传统的拷贝不同,move 的意思是目标对象“窃取”原对象的资源,并将源置于“空”状态。当拷贝一个对象时,其实代价昂贵且无必要,move 操作就可以替代它。如在 string 交换的时候,使用 move 意义就有巨大的性能提升,如原方案是这样的:
void naiveswap(string &a, string
& b)
{
string temp = a;
a=b;
b=temp;
}
这种方案很傻很天真,很慢,因为需要申请内存,然后拷贝字符,而 move 就只需要交换两个数据成员,无须申请、释放内存和拷贝字符数组:
void moveswapstr(string& empty,
string & filled)
{
//pseudo code, but you get the idea
size_t sz=empty.size();
const char *p= empty.data();
//move filled's resources to empty
empty.setsize(filled.size());
empty.setdata(filled.data());
//filled becomes empty
filled.setsize(sz);
1filled.setdata(p);
1}
要实现支持 move 的类,需要声明 move 构造函数和 move 赋值操作符,如下:
class Movable
{
Movable (Movable&&); //move
constructor
Movable&&
operator=(Movable&&); //move assignment operator
};
C++11 的标准库广泛使用 move 语义,很多算法和容器都已经使用 move 语义优化过了。
新的智能指针类
C++98 定义的唯一的智能指针类 auto_ptr 已经被弃用,C++11 引入了新的智能针对类 shared_ptr 和 unique_ptr。它们都是标准库的其它组件兼容,可以安全地把智能指针存入标准容器,也可以安全地用标准算法“倒腾”它们。
新算法
C++11标准库定义了新的算法,即模拟集合论领域的操作符all_of(),any_of(),none_of()。下列代码清单里,对[first,first+n]所限定的范围内,使用谓词方法ispositive()遍历检查每个元素,并且采用all_of(),any_of(),none_of()方法,来测定该范围整体的性质。
//are all of the elements positive?
all_of(first, first+n, ispositive());
//false
//is there at least one positive
element?
any_of(first, first+n,
ispositive());//true
// are none of the elements positive?
none_of(first, first+n,
ispositive()); //false
一类新型copy_n算法也出笼了。利用copy_n()复制含有5个成员的数组到另一数组,可说是小菜一碟。
#include
int source[5]={0,12,34,50,80};
int target[5];
//copy 5 elements from source to
target
copy_n(source,5,target);
算法iota负责创建有限的递增值序列,通过向*first表示的第一子项指定初始值,那么该值则被施加前缀++操作,以生成后序数据。
include
int a[5]={0};
char c[3]={0};
iota(a, a+5, 10); //changes a to
{10,11,12,13,14}
iota(c, c+3, 'a'); //{'a','b','c'}
C++11仍然缺少一些实用库,例如XML应用接口,sockets,图形用户界面,reflection——当然,还有合理的自动化废物回收器。新标准确实提供了大量新功能,使得C++更形可靠,高效,易于学习和使用。
相关文章推荐
- Android开发,MapBox地图详细介绍、使用、部分功能实现(初始化、标记、定位、styleurl)
- 专业PDF转换器(支持OCR)功能介绍及安装注册步骤和使用方法详细说明
- Win7搜索功能使用指南详细图文介绍
- Java字符串分割方法split()的功能以及使用方法的详细介绍!
- 经典vim插件功能说明、安装方法和使用方法介绍
- Elasticsearch简单使用系列--详细介绍ES的核心概念
- DM9000寄存器功能详细介绍
- mysql分区功能详细介绍,以及实例
- 详细介绍Linux重定向的使用
- mysql分区功能详细介绍,以及实例
- 使用Java实现简单的server/client回显功能的方法介绍
- Java 集合系列13之 WeakHashMap详细介绍(源码解析)和使用示例
- 使用Java实现简单的server/client回显功能的方法介绍
- Java 集合系列 05 Vector详细介绍(源码解析)和使用示例
- Java 集合框架--ArrayList详细介绍和使用示例
- vb mid函数的使用方法详细介绍
- JProfiler的详细使用介绍(JVM对象内存线程监测工具)
- 详细介绍在windows系统中配置 cocos2d-x 开发环境及简单使用(VS2012+Cocos2d-x 3.0rc1)
- Java 集合系列03之 ArrayList详细介绍(源码解析)和使用示例
- .NET压缩与解压控件Xceed Zip for .NET控件详细介绍使用方法及下载