那c++11不但对c++的语法从核心上做了很多的改进 他还对c++的标准库也做了一些扩充,比方说他把st1 新加进了一些类模板,其中有一个特别好用大家呼声一直很高想要有的就是这个 哈希表,也就是无序容器 哈希表无序容器呢他是在头文件unordered map里面包含了 就这个哈希表它的用法和功能跟map是一模一样的 区别就在于哈希表它的时间效率要比map更高 哈希表是一种数据结构啦,具体 他是怎么实现的是属于数据结构的内容我在这里就不说了啊 但我们知道这个 unordered map这个东西跟map的用法是一模一样的,但是它的 效率比map更高,我们知道map在进行插入元素和查找的时候时间复杂度是什么呀,是logN的对吧 那这个unordered map哈希表它进行插入和查找的时候 时间复杂度都几乎就是常数的,这就是哈希表最大的好处 那我们来看一下哈希表的例子,在这里我们定义一个unordered map 叫做Turing winner ,我们用这个容器来记录 图灵奖的获奖者然后可以根据 获奖者的姓名去查找他获奖的年龄 这个每个元素就放着一个获奖者的姓名和他获奖的年龄 获奖的年份不是年龄啊 好,下面这个turning winner insert make pain dijkstra 1972就是dijkstra1972年获得的图灵奖 好那这个用法不是跟map一模一样的对吧 它里面ordered map里面也不能有这个重复元素的啊重复的 关键字相同的元素的,然后就是insert Scott wilkes hamming等等啊 还有这个Richie,我们看到,这个用法跟map也是一样的 就是,可以直接通过 关键字作为下标去对一个元素的值进行访问 那么在这里,如果turning winner里面还没有一个元素它的关键字 也就是first成员变量是richie的话 那么,有一个新的元素就会被插入到这个 turning winner里面去,这个新元素它的 first成员变量就是是richie,然后它的second被复制成了1983 接下来我们就可以输入一个姓名然后去 对这个turning winner这个容器进行查找 查找的方式跟map也是一样,定义一个迭代器,啊这里写auto的话就更简单了是吧 然后用turning winner的find成员函数去进行查找 如果 如果查找的结果不是这个 and的话那就说明找到,那我们就输出它的 获奖年份,找不到就输出not found,我看到这个用法跟map是 一模一样的,但是它内部的实现机制跟map是不一样 map是用平衡二叉树实现的,那这个 auto the map是用哈希表实现的,所以它的时间效率更高 但它时间效率更高也不是没有代价的,那就是 哈希表需要很多的内存空间,他所需要的内存空间会比map 多得多,但一般来说我们 计算机的内存总是够用的,就是时间往往是更优先于内存空间去考虑的 所以这个哈希表还是比map用起来可以说更爽一些吧 下面再看一个 c++11在这个库方面的改进,就是引入了正则表达式这个东西 要使用正则表达式include头文件regex,那什么叫正则表达式呢 你可以认为正则表达式实际上他就是一个 字符串,这个字符串描述了一种模式 它描述的是一种字符串的模式,那比方说 我们在这里定义了一个regex,这regex就是 正则表达式的类,那这里reg就是正则表达式对象 然后我们用一个字符串去对这个对象进行初始化,那reg就是一个正则表达式,它代表了一种模式 一种字符串的模式,这个字符串的模式呢,是跟这个 是被这个b点什么问号 是由这个b点问号p点*k来说明的 那这串东西说明了一种什么样的模式呢 它代表某一类的字符串,这类字符串有以下特点,什么特点呢,这个字符串必须是以b开头 然后呢这个点呢他是代表任意字符,也就是b后面跟任意字符 然后这个问号就表示刚才这个任意字符它应该出现这个 0次或者是1次,只能是0次或者是1次,刚才这个任意字符可以出现0次或者1次 然后下面就是一个P,那等于就是说在b和 p之间可以有0个或者1个字符 然后这里点就说明这里可以出现任意字符,这*就是说的是 刚才这个任意的字符可以出现0次或者是任意多次 然后呢紧接着就是一个k,那就是说p和k之间可以出现相同的 字符,任意多次,或者是没有字符 那么这个 字符串常量也就是这个正则表达式所对应的那个字符串的模式是什么呢 就是说以b开头,然后后面跟着 0个或一个字符,然后再是一个p,然后后面再跟着 0个或者是任意多个相同的字符,然后面再跟着一个k,就是这个意思 那好了,我们定义了一个正则表达式以后呢,我们就可以用reg match这个 函数,去判断某一个指定的字符串是不是 能跟这个正则表达式相匹配,相匹配的意思就是这个字符串符合这个正则表达式所规定的那一种模式 如果能够匹配的话这个 常数调用它的的返回值就是1,不匹配的返回值就是0,那我们看这个bopggk是不是符合 这种模式呢,它是以b打头的,然后后面跟着一个o字符,出现了一次对吧,然后接着就是一个p 然后有一个g,它出现了两次,这符合新的这个定义对吧,所以说 这条语句会输出1,表示这个bopggk它是符合reg所规定的这个模式的 那下面这条语句呢输出0,它的意思就是说boopgggk 它不符合reg所定义的模式,在什么地方不符合啊 这块说了,这里是一个字符,它出现,这个字符呢得出现 0次或者1次,而你这里出现了两个o,b和p之间有两个字符,这就不匹配了 那包括下面这个b空格pk 那它是不是匹配呢,它是匹配的,他为什么是匹配啊,因为空格嘛出现了一次没问题对吧 出现相同的字符若干次或者什么字符都不出现 也可以,那我这里p和k之间没有字符啊,就符合这个条件,所以就没有问题 那就是说这个正则表达式吧 它所代表的这个模式可以非常复杂,比方说下面我们定义的另外一个正则表达式reg2 他就规定了一种字符串的模式,这种模式呢我们用这一大串东西来代表 额 这一大串东西代表什么呢,那我们就一个个看啊,这里是两个斜杠 那我们知道在c++里面的字符串你要是一个斜杠的话他就被表示成两个斜杠对吧 所以这两个斜杠实际上是代表杠d的,那这个杠d是什么呢,杠d代表 数字,啊就是0到9,然后这个花括号3,就代表刚才那个东西应该出现3次 那也就是说,这个模式所对应的字符串它应该是以三个数字打头 好了接下来,有一个圆括号,这个圆括号就代表项 就代表里面的东西是一个项,这个项在后面可能还会引用到,反正圆括号代表一个项 括号本身并不要跟任何东西匹配,圆括号本身并不需要跟任何 东西匹配,它指明在这里要出现里面这一项,那里面这一项又是什么呢 里面这一项这有一个中括号 然后里面写着a到z,就说明整个中括号这一块代表 字母,就是这个项是由什么组成的啊,它是由 字母,出现 一次或者若干次组成的,这个加号代表什么啊,这个加号代表它前面的东西必须出现 一次或者是任意多次 那它前面东西是什么呢,用中括号表明,就说明这前面这东西是小写的a到z和大写的a到z,也就是 通常的英文字母,所以这个项 代表什么呀,就是一个英文单词,就是完全由大小写字母组成的英文单词 这个英文单词长度至少是1,这就是这个项 的含义,然后下面有一个点,点就代表这个地方可以出现任意一个字符 再接下来就是第二项了 第二项呢是什么样呢,第二项中间有一个竖线,这个竖线是或的意义,就是第二项它 可以是杠d花括号2,也可以是这个N杠A 啊,那这个“\\d{2}”,{2}代表什么呢?就代表两个数字。 啊,那这个“N/A”它就是N/A了,它没有别的意思,就是,就是N/A,这个字符串 反正这里的第二项,它要么是两个数字,要么是N /A。然后接下来这个“\\S”,“\\S就是空格,啊然后“\\1” “\\1”就代表第一项,啊就是在这个 地方出现的东西必须跟刚才这个第一项,在这里 出现的东西是一模一样。啊这个就是 整个这个,这个模式串它的这个含义。那这个正确表达式的这种 什么,呃什么方括号代表什么,“\\d”代表什么,它是很复杂的,有一系列的这个规定,写出来有好几页纸。 这个我没法在这里细说啦,大家真正要用的时候得去 查那个文档,因为这些东西它挺不好记的啊。那下面我们看 呃,有一个string correct,啊,这个string correct它是能够跟这个reg2 所代表的模式能配得上的,而这个incorrect是配不上,所以我们在,在 在输出regex_match(correct,reg2)的时候自然就会输出1 因为这个,这个correct是能够配上,而incorrect是配不上的。 那correct为什么能够配得上呢?我们逐个分析,这“\\d{3}”不是 说是3个数字对吧?然后这个,嗯这个第一项不是说是一个英文单词吗所以就Hello 它是一个英文单词没问题对吧?然后这里说是出现一个字符诶那我这个字符是个空格也没问题,然后然后这里的第二项 是两个数字或者是N/A,诶那我这里是个N/A能够匹配得上对吧 然后这里说来一个空格,啊我这里确实有一个空格啊,这也没有问题,然后呢 接下来这个 “\\1”说明这里应该出现第一项,那刚才第一项不是一个Hello吗 诶这里正好有一个Hello,所以就完全匹配得上。但这个incorrect它是 呃,匹配不上的,啊为什么呢? 诶我们看到,呃这个123Hello都没问题,然后这个空格也没问题,然后这一块,诶写的是12,也没有问题 因为第二项它可以是两个数字或者是N/A,那我写的是12也没问题,然后这里\\s是个空格也没问题,但是这一块的是小写的 hello,啊,而我们这里要求的是出现的第一项,而我们的第一项是这个Hello H是大写的,这一块是小写的,这个就匹配不上,所以incorrect这个字符串 是没法跟reg2匹配的。那正则 表达式它使用很灵活啦,我这里只仅仅列出了一点点 让同学们对这个正则表达式有那么一点印象,就是说它可以用来做 呃,字符串的匹配,只不过这个字符串 不是一模一样去匹配了,而是看这个字符串是不是符合某种模式 啊,实际上它还有,正则表达式还有其他的这个功能,那我们就,在这里就 时间所限呢就不做了。那最后我们 再介绍C++1里面一个特别酷的东西,叫做这个Lambda表达式。 啊这个Lambda表达式这个概念呢是从那个函数是程序设计语言,就是list那边过来的。那这个Lambda表达式 需要解决的问题是什么呢?啊就是我们在 使用这个换,换新程序设计使用S1的时候啊我们经常会用到 函数对象,啊我们用到一个函数对象呢,我们要专门编写一个类,对吧?那可能有的 函数对象我只在这个地方用一次,我还得到别处去把这个类也定义一下,然后后来别人看我的程序的时候看到了这个函数对象 他也到前面去翻,啊这个函数对象所指的类到底是怎么样,是不是挺讨厌对吧? 然后你用了一大堆的函数对象,很可能你的程序里面就出现了一大堆的函数对象类,这个看上去也 挺烦的,啊,所以,能不能我们减少 定义函数对象类?啊,不要定义那么多函数对象类 这样使我们的程序看得精神一点,尤其是只用一次的函数对象,我们跑别处 专门分一个类,这确实太浪费了;那同理,就是说有的简单的函数我可能只调用一次我也得在 呃,所有的呃函数外面,专门定义一下,这个反正呢 也挺烦的,啊,就是说如果有的函数只用一次,而且又很简单,我是不是能够在调用的时候 就直接把它函数体写出来啦?啊不需要拿到外面再去写这个函数啦。 那Lambda表达式就能解决这种问题,啊它能够使得程序的可读性更好,更容易 理解。实际上java里面就有类似的这种语法啦,啊 但它不叫Lambda表达式,java里面也是可以,呃 拿一整个函数的函数体作为参数去,去调用某个函数的iii 那这个Lambda表达式啊它本质上就是一个函数,所以它写起来也像是一个函数,啊它是以所谓的这个 这个外部变量访问方式说明符开头,然后后面跟着一个函数的参数表,然后箭号后面还可以 跟返回值类型,那这一部分呢 呃也可以不写,呃没有的话编译器可以自动根据return语句后面东西来判断这个函数的返回值类型。然后里面就是语句组啊 就跟函数里面的语句是一样的。 那么这一块这个外部变量访问说明符,它可以是下面这几种形式啊,呃比如说 写个“=”就意味着以传值的形式使用所有的外部变量,那什么叫外部变量啊?呃就是这个Lambda 表达式吧它一般是出现在一个函数内部的,那么 只要不是在Lambda表达式里面定义的变量 都称为外部变量,那外部变量就可以是全局变量,也可以是调用了这个Lambda 表达式的那个函数里面定义的局部变量,反正只要有定义就可以啦,啊就可以在这里面 使用外部变量。那使用外部变量可以有两种方式,一种是传值,一种是传引用,如果是传值方式使用的话 Lambda表达式内部就不允许去修改这个外部变量的值,而且以传值方式 使用的话,如果那个外部变量是个对象,那就会 有可能引发,复制过多函数的调用,啊因为要传值嘛,呃那 Lambda表达式也可以传引用的方式使用外部变量,那以传引用的方式使用的话,呃这块儿就写个 “&”就行啦,以传引用方式使用外部变量,外部变量就可以在 Lambda表达式执行的过程中被修改,当然你也可以 这一块写个空的“[ ]”,这就意味着我这个Lambda表达式啊,它不使用任何外部变量,那 这个外部变量访问方式说明符还可以比较复杂,比如说[x,&y] 这就是说,呃我这个Lambda表达式要以x 要以传值的方式使用x这个外部变量,但是呢以引用的话呢 使用外部的变量,那其他变量不用。 你可以写[=,&x,&y]就意味着x,y是以引用方式使用,然后其他的变量是以传值方式使用,你可以写[&,x,y] 那就说明x,y是以传值使用,然后其余变量以引用函数使用 反正这个是,这里面的项可以有很多项啊 那这个Lambda表达式怎么定义我们知道,那到底它 如何使用呢?我们看这个例子,啊这里定义了,面里面 定义了3个变量:x,y,z,接下来我们就调用了一个Lambda表达式,在这,啊这个Lambda表达式呢它当然是没有名字的 它有两个参数:double a,double b 然后它的函数的代码是return a+b,那 再下面呢不是Lambda表达式的一部分啊,它是调用这个Lambda表达式的时候所给的这个参数,就是1,2,2,5 那这个Lambda表达式它的返回值当然就是3.7,所以这条语句就会输出3.7,啊这儿,我们看到 使用到了这个Lambda表达式,这个Lambda表达式内部不使用任何外部变量,所以可以[]。下面,这又是 一个Lambda表达式,在这一块,啊,这是一个Lambda表达式,它有一个整形参数 然后呢它,呃它用到了外部变量,就是 y和z是以引用的方式来使用的,其他的外部变量是用 传值的方式来使用的,而在这里面呢输出了x,那这个x就是这一块的x啦,然后呢 呃又修改了y和z,嗯然后返回n的平方,啊那我们 可以把这个Lambda表达式整个赋值给一个变量ff,那这个ff到底是什么类型的呢?搞不清楚,我们就偷懒一点 我们写个auto吧,那么编译器就自动会给ff一个适当的类型 那ff在这里看上去有点像函数指针对吧,因为 Lambda表达式本质上就是一个函数,好了我们把这个Lambda表达式啊 赋值给ff,那这条赋值语句并不导致这个Lambda表达式被调用,那我们要调动它怎么办呢? 我就可以写ff(15)这就调用了这个Lambda表达式啦,有点像函数指针的用法;那调用这个表达式以后 执行这些语句,然后就会输出x,x是100,然后呢把y和z的值 修改了,然后返回n平方,就是15的平方,然后输出,那当然就是225 然后y和z的值都被加1了嘛,你再把它输出就是201,301 那刚才那两个例子只是说明了Lambda表达式的用法,并没有体现出 很明显的优势。啊下面我们就要看看Lambda表达式到底真正能够起到什么好的 作用。啊,这里有一个数组a,呃然后我们 现在要做的事情呢是用sort这个算法 把这个数组a呀,按照个位数从小到大进行排序,呃那前两个 参数当然就是a和a+4啦,那如果按照前面的做法,我们sort的第3个参数 这一块就应该写一个函数的名字或者写一个函数对象对吧? 如果写函数对象的话,你就在这个函数对象类的那个中括号那个地方 啊圆括号那个地方指明这个比较大小的规则。 那反正你要写一个函数或者函数对象类 写到外面比较啰嗦,如果我这个,这种排序方式只做一次,你还要专门去写就很麻烦。 那要用Lambda表达式的话呢就可以把整个比较大小的规则就全部写在这儿了,在这里我们写了一个Lambda表达式 这个Lambda表达式呢它是由两个参数x和y,那返回值是bool类型的。 然后呢,它返回的就是,第一个参数的个位数,是不是小于第二个参数的个位数, 也就是说,它规定了一种比大小的规则。那这个 sort 在 执行期间,需要比较两个元素 x ,y 的大小的时候呢, 就会调用这个任务的表达式,并且以 x ,y 作为参数, 然后看这个返回值,如果返回值是 true ,那就认为 x 小于 y。 那这样的规则实际上就是按个位数从小到大排序。 呃,那这条语句总之就对 a 进行排序了。下面呢,for each , 对这个 a 进行一下处理,遍历这个 a 啊, 呃,在这里呢,对这个区间里面的每一个元素,都要, 以其为参数,去调用这个 lembda 表达式。这个 lembda 表达式呢, 有一个参数 x ,然后它输出了 x 的值。那 for each 在执行的过程中,对每一个元素, 都调用了这个 lembda 表达式,并且以 x ,以这个元素作为参数,x 就是元素嘛,那当然会把每个元素都输出啦。 所以这个程序的输出结果就是 11,2,33,4。 再看一个例子。在这个例子里面呢, 我们看有一个 vector,啊, 它里面是 1,2,3,4。 然后有个 total ,一开始初始值0。 然后 for each 对这个 vector a 里面的元素进行遍历,啊,对每一个元素都做某种操作。 这个,这个操作呢,就是通过这个 lembda 表达式来进行体现的。 这个 lembda 表达式,这里写了一个 &,就意味着我这里面需要以引用的方式去使用外部变量。 然后这个 lembda 表达式有一个参数 x , 而且这个参数是引用的形式,呃, 这意味着什么呢,就是 for each, 对于 a 里面的每一个元素,都会以这个元素作为参数去调用这个 lembda 表达式。也就是说,这个时候 x 是, a 里面的元素啦,可是这时候,可是这时候,我们这个 x 是传引用的,也就是说我们在这个 lembd 表达式里面,如果修改了 x 的值, 这个 vector 里面的元素,就会被修改了。那我们看这个 lembda 表达式做什么。 它把 x 的值先加到 total 上面,然后把 x 乘以 2, 那么,呃,这个 for each 循环执行完的话呢,呃,遍历执行完了以后呢,肯定就会把这个,呃,数组里面, 所有被修改之前的元素的值,都加到了这个 total 上面,对吧?等于就是对这个数组进行求和。 然后每一个元素呢还被 double 了,乘以2了,所有我们输出 total 呢就是1 一直加到 4 ,就是 10。 然后下面这个 for each ,就是对这个 a 里面, 的每一个元素 x 都调用,呃,这个 lembda 表达式,啊,并且把这个 x 的,给它,值给它输出了, 那当然,这个程序的输出结果,后面就是2,4,6,8。 呃,那 lembda 表达式还可以实现递归的这个功能,啊,这个, 写法看上去比较奇葩,我们来看一下啊。呃, 在这里,我们写个 f ,function,int,int,fib, 啊。呃,这个 function int int fib 是个什么意思呢? 它实际上表示一个,一种类型,这种类型类似于这个函数,或者函数指针啊。 它表示一个返回值为 int,有一个参,int 参数的函数,返回值 int 是在这个地方体现的。 有一个 int 参数是这个圆括号里面只有一个 int ,所以这就是,它要有一个 int 参数。 好,现在我们定义的这种, 类似于函数或者是什么,lembda 表达式这样一种类型的东西,叫作 fib,fib 是个变量。 然后我对 fib 这个变量进行初始化,我怎么初始化呢? 我用一个 lembda 表达式对这个 fib 变量进行初始化。 那对于这个 lembda 表达式来说,这个 fib 就是外部变量,对吧? 然后我们在这个 lembda 表达式的开头,我说了,唉,我这个 lembda 里面要使用 fib 这个外部变量, 而且是以引用的方式来使用的哦。然后这个 lembda 表达式它的参数就是 n 。然后这个 lembda 表达式里面干什么呢? 它不是要求斐波那契数列的第 n 项,对吧?那就首先判断 n 是不是小于等于2 ,如果是第1项和第2项的话, 那就返回1就完了。那如果这个 n 是大于等于3呢?呃,那么第 n 项就是, 前两项之和,那怎么表示前两项之和啊,呃,我们就递归调用 fib , 并且以 n-1 作为参数, fib 以 n-2 作为参数了,这就是递归了,对不对啊?因为 fib 是什么? fib 就是 整个这个 lembda 表达式,对吧?所以我们在这里写了 fib, 就等于又调用了这整个 lembda 表达式, 这就实现了这个递归。呃,那我们要真正调用 fib 的时候,就, 这时候就这么做了, 就 fib 5,这就求出了第5 项,然后输出就是5 啦。 那这里面有一个问题,就是,啊,这个东西写起来好麻烦啊,我能不能这个不写,我也写个 all too? 唉,让编译器自动去判断这个 fib 的类型,我们岂不是就省事儿了对吧?这么做行不行呢? 啊,实际上是不可以的,为什么呢? 你想啊,编译器要判断 fib 类型的时候,它根据什么呀? 它根据你这里 return 的这个返回值,对吧? 那你 return 的返回值它是有可能是 fib n-1,再加上 fib n -2 的。 那在这种情况下,你要求这个 fib 的类型,还得 依赖于这个 fib 算出来的这个表达式的类型,才行。 这不就循环了,对吧?要求 fib 的类型,就得先知道 fib 的类型,这算什么啊? 所以,编译器没有办法完成这个任务,你得自己把这个 fib 的类型给它写好啦。 那这个功能强大的 lembda 表达式就说完了,实际上我们整个C++ 11的 这个新特性就讲这么多了, 那么现在的 C++ 的编译器,比如说 GCC4.8,什么 ES2013 什么的啊, 对 C++11的特性都支持得非常好啦,大家可以放心大胆地使用这些很强的功能,enjoy it ,bye bye!