声 明:本文源自 Danny Kalev 在 2011 年 6 月 21 日发表的《The Biggest Changes in C++11(and Why You Should Care)》一文,几乎所有内容都搬了过来,但不是全文照译,有困惑之处,请参详原文(http://www.softwarequalityconnection.com/2011/06/the-biggest-changes-in-c11-and-why-you-should-care/ )。
注:作者 Danny Kalev 曾是 C++ 标准委员会成员。

Lambda 表达式

Lambda 表达式的形式是这样的:

  1. [capture](parameters)->return-type {body}

来看个计数某个字符序列中有几个大写字母的例子:

  1. int main()
  2. {
  3. char s[]="Hello World!";
  4. int Uppercase = 0; //modified by the lambda
  5. for_each(s, s+sizeof(s), [&Uppercase] (char c) {
  6. if (isupper(c))
  7. Uppercase++;
  8. });
  9. cout<< Uppercase<<" uppercase letters in: "<< s<<endl;
  10. }

其中 [&Uppercase] 中的 & 的意义是 lambda 函数体要获取一个 Uppercase 引用,以便能够改变它的值,如果没有 &,那就 Uppercase 将以传值的形式传递过去。

自动类型推导和 decltype

在 C++03 中,声明对象的同时必须指明其类型,其实大多数情况下,声明对象的同时也会包括一个初始值,C++11 在这种情况下就能够让你声明对象时不再指定类型了:

  1. auto x=0; //0 是 int 类型,所以 x 也是 int 类型
  2. auto c='a'; //char
  3. auto d=0.5; //double
  4. auto national_debt=14400000000000LL;//long long

这个特性在对象的类型很大很长的时候很有用,如:

  1. void func(const vector<int> &vi)
  2. {
  3. vector<int>::const_iterator ci=vi.begin();
  4. }

那个迭代器可以声明为:

  1. auto ci=vi.begin();

C++11 也提供了从对象或表达式中“俘获”类型的机制,新的操作符 decltype 可以从一个表达式中“俘获”其结果的类型并“返回”:

  1. const vector<int> vi;
  2. typedef decltype (vi.begin()) CIT;
  3. CIT another_const_iterator;

统一的初始化语法

C++ 最少有 4 种不同的初始化形式,如括号内初始化,见:

  1. std::string s("hello");
  2. int m=int(); //default initialization

还有等号形式的:

  1. std::string s="hello";
  2. int x=5;

对于 POD 集合,又可以用大括号:

  1. int arr[4]={0,1,2,3};
  2. struct tm today={0};

最后还有构造函数的成员初始化:

  1. struct S {
  2. int x;
  3. S(): x(0) {} };

这么多初始化形式,不仅菜鸟会搞得很头大,高手也吃不消。更惨的是 C++03 中居然不能初始化 POD 数组的类成员,也不能在使用 new[] 的时候初始 POD 数组,操蛋啊!C++11 就用大括号一统天下了:

  1. class C
  2. {
  3. int a;
  4. int b;
  5. public:
  6. C(int i, int j);
  7. };
  8. C c {0,0}; //C++11 only. 相当于 C c(0,0);
  9. int* a = new int[3] { 1, 2, 0 }; /C++11 only
  10. class X {
  11. int a[4];
  12. public:
  13. X() : a{1,2,3,4} {} //C++11, 初始化数组成员
  14. };

还有一大好事就是对于容器来说,终于可以摆脱 push_back() 调用了,C++11中可以直观地初始化容器了:

  1. // C++11 container initializer
  2. vector vs<string>={ "first", "second", "third"};
  3. map singers =
  4. { {"Lady Gaga", "+1 (212) 555-7890"},
  5. {"Beyonce Knowles", "+1 (212) 555-0987"}};

而类中的数据成员初始化也得到了支持:

  1. class C
  2. {
  3. int a=7; //C++11 only
  4. public:
  5. C();
  6. };

deleted 函数和 defaulted 函数

像以下形式的函数:

  1. struct A
  2. {
  3. A()=default; //C++11
  4. virtual ~A()=default; //C++11
  5. };

叫做 defaulted 函数,=default; 指示编译器生成该函数的默认实现。这有两个好处:一是让程序员轻松了,少敲键盘,二是有更好的性能。
与 defaulted 函数相对的就是 deleted 函数:

  1. int func()=delete;

这货有一大用途就是实现 noncopyabe 防止对象拷贝,要想禁止拷贝,用 =deleted 声明一下两个关键的成员函数就可以了:

  1. struct NoCopy
  2. {
  3. NoCopy & operator =( const NoCopy & ) = delete;
  4. NoCopy ( const NoCopy & ) = delete;
  5. };
  6. NoCopy a;
  7. NoCopy b(a); //编译错误,拷贝构造函数是 deleted 函数

nullptr

nullptr 是一个新的 C++ 关键字,它是空指针常量,它是用来替代高风险的 NULL 宏和 0 字面量的。nullptr 是强类型的:

  1. void f(int); //#1
  2. void f(char *);//#2
  3. //C++03
  4. f(0); //调用的是哪个 f?
  5. //C++11
  6. f(nullptr) //毫无疑问,调用的是 #2

所有跟指针有关的地方都可以用 nullptr,包括函数指针和成员指针:

  1. const char *pc=str.c_str(); //data pointers
  2. if (pc!=nullptr)
  3. cout<<pc<<endl;
  4. int (A::*pmf)()=nullptr; //指向成员函数的指针
  5. void (*pmf)()=nullptr; //指向函数的指针

委托构造函数

C++11 中构造函数可以调用同一个类的另一个构造函数:

  1. class M //C++11 delegating constructors
  2. {
  3. int x, y;
  4. char *p;
  5. public:
  6. M(int v) : x(v), y(0),  p(new char [MAX])  {} //#1 target
  7. M(): M(0) {cout<<"delegating ctor"<<end;} //#2 delegating

#2 就是所谓的委托构造函数,调用了真正的构造函数 #1。

右值引用

在 C++03 中的引用类型是只绑定左值的,C++11 引用一个新的引用类型叫右值引用类型,它是绑定到右值的,如临时对象或字面量。
增加右值引用的主要原因是为了实现 move 语义。与传统的拷贝不同,move
的意思是目标对象“窃取”原对象的资源,并将源置于“空”状态。当拷贝一个对象时,其实代价昂贵且无必要,move 操作就可以替代它。如在
string 交换的时候,使用 move 意义就有巨大的性能提升,如原方案是这样的:

  1. void naiveswap(string &a, string & b)
  2. {
  3. string temp = a;
  4. a=b;
  5. b=temp;
  6. }

这种方案很傻很天真,很慢,因为需要申请内存,然后拷贝字符,而 move 就只需要交换两个数据成员,无须申请、释放内存和拷贝字符数组:

  1. void moveswapstr(string& empty, string & filled)
  2. {
  3. //pseudo code, but you get the idea
  4. size_t sz=empty.size();
  5. const char *p= empty.data();
  6. //move filled's resources to empty
  7. empty.setsize(filled.size());
  8. empty.setdata(filled.data());
  9. //filled becomes empty
  10. filled.setsize(sz);
  11. filled.setdata(p);
  12. }

要实现支持 move 的类,需要声明 move 构造函数和 move 赋值操作符,如下:

  1. class Movable
  2. {
  3. Movable (Movable&&); //move constructor
  4. Movable&& operator=(Movable&&); //move assignment operator
  5. };

C++11 的标准库广泛使用 move 语义,很多算法和容器都已经使用 move 语义优化过了。

C++11 的标准库

除 TR1 包含的新容器(unordered_set, unordered_map, unordered_multiset,
和unordered_multimap),还有一些新的库,如正则表达式,tuple,函数对象封装器等。下面介绍一些 C++11 的标准库新特性:

线程库

从程序员的角度来看,C++11 最重要的特性就是并发了。C++11 提供了 thread 类,也提供了 promise 和 future
用以并发环境中的同步,用 async() 函数模板执行并发任务,和 thread_local
存储声明为特定线程独占的数据,这里(http://www.devx.com/SpecialReports/Article/38883)有一个简单
的 C++11 线程库教程(英文)。

新的智能指针类

C++98 定义的唯一的智能指针类 auto_ptr 已经被弃用,C++11 引入了新的智能针对类 shared_ptr 和 unique_ptr。它们都是标准库的其它组件兼容,可以安全地把智能指针存入标准容器,也可以安全地用标准算法“倒腾”它们。

新的算法

主要是 all_of()、any_of() 和 none_of(),下面是例子:

  1. #include <algorithm>
  2. //C++11 code
  3. //are all of the elements positive?
  4. all_of(first, first+n, ispositive()); //false
  5. //is there at least one positive element?
  6. any_of(first, first+n, ispositive());//true
  7. // are none of the elements positive?
  8. none_of(first, first+n, ispositive()); //false

还有一个新的 copy_n:

  1. #include <algorithm>
  2. int source[5]={0,12,34,50,80};
  3. int target[5];
  4. //从 source 拷贝 5 个元素到 target
  5. copy_n(source,5,target);

iota() 算法可以用来创建递增序列,它先把初值赋值给 *first,然后用前置 ++ 操作符增长初值并赋值到给下一个迭代器指向的元素,如下:

  1. #include <numeric>
  2. int a[5]={0};
  3. char c[3]={0};
  4. iota(a, a+5, 10); //changes a to {10,11,12,13,14}
  5. iota(c, c+3, 'a'); //{'a','b','c'}

是的,C++11 仍然缺少一些很有用的库如 XML API,socket,GUI、反射——以及自动垃圾收集。然而现有特性已经让 C++
更安全、高效(是的,效率更高了,可以参见 Google 的
基准测试结果http://www.itproportal.com/2011/06/07/googles-rates-c-most-
complex-highest-performing-language/)以及更加易于学习和使用。

如果觉得 C++ 变化太大了,不必惊恐,花点时间来学习就好了。可能在你融会贯通新特性以后,你会同意 Stroustrup 的观点:C++11 是一门新的语言——一个更好的 C++。

转:c++ 11 新特性的更多相关文章

  1. C++ 11学习和掌握 ——《深入理解C++ 11:C++11新特性解析和应用》读书笔记(一)

    因为偶然的机会,在图书馆看到<深入理解C++ 11:C++11新特性解析和应用>这本书,大致扫下,受益匪浅,就果断借出来,对于其中的部分内容进行详读并亲自编程测试相关代码,也就有了整理写出 ...

  2. C++11新特性总结 (二)

    1. 范围for语句 C++11 引入了一种更为简单的for语句,这种for语句可以很方便的遍历容器或其他序列的所有元素 vector<int> vec = {1,2,3,4,5,6}; ...

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

    1. 概述 最近在看C++ Primer5 刚好看到一半,总结一下C++11里面确实加了很多新东西,如果没有任何了解,别说自己写了,看别人写的代码估计都会有些吃力.C++ Primer5是学习C++1 ...

  4. C++ 11 新特性

    C++11新特性:          1.auto          2.nullptr          3.for          4.lambda表达式          5.override ...

  5. [转载] C++11新特性

    C++11标准发布已有一段时间了, 维基百科上有对C++11新标准的变化和C++11新特性介绍的文章. 我是一名C++程序员,非常想了解一下C++11. 英文版的维基百科看起来非常费劲,而中文版维基百 ...

  6. 在C++98基础上学习C++11新特性

    自己一直用的是C++98规范来编程,对于C++11只闻其名却没用过其特性.近期因为工作的需要,需要掌握C++11的一些特性,所以查阅了一些C++11资料.因为自己有C++98的基础,所以从C++98过 ...

  7. C++11新特性——range for

    很多编程语言都有range for语法功能,自C++11起,终于将这个重要功能加入C++标准中.range for语句,可以方便的遍历给定序列中的每个元素并对其执行某种操作. 1.基本语法 for(d ...

  8. C++11新特性——大括号初始化

    C++11之前,C++主要有以下几种初始化方式: //小括号初始化 string str("hello"); //等号初始化 string str="hello" ...

  9. C++11新特性之六——元编程

    C++11新特性之六——元编程

  10. C++11新特性之一——Lambda表达式

    C++11新特性总结可以参考:http://www.cnblogs.com/pzhfei/archive/2013/03/02/CPP_new_feature.html#section_6.8 C++ ...

随机推荐

  1. sstable, bigtable,leveldb,cassandra,hbase的lsm基础

    先看懂文献1和2 1. 先了解sstable.SSTable: Sorted String Table [2] [10] WiscKey:  类似myisam, key value分离, 根据ssd优 ...

  2. CF-1072-C. Cram Time(贪心,数学)

    CF-1072-C. Cram Time http://codeforces.com/contest/1072/problem/C 题意: 第一天有 a 小时,第二天有 b 小时.第 k 个任务需要 ...

  3. 【OS_Linux】Linux下软件的安装与卸载

    1.Linux中软件安装包的分类 1) 一类是可执行的软件包,无需编译直接安装.在Windows中所有的软件包都是这种类型.安装完这个程序后,你就可以使用,但你看不到源程序.而且下载时要注意这个软件是 ...

  4. 浮动的label

    在web项目中,有一个很重的模块就是登陆/注册模块,这个模块的主体部分就是一个form表单,这个form表单包含两个重要input组(用户名/密码),每个input组都包含label和input,而关 ...

  5. Qtopia移植

    Qtopia 是Trolltech 公司为采用嵌入式Linux操作系统的消费电子设备而开发的综合应用平台, Qtopia包含完整的应用层.灵活的用户界面.窗口操作系统.应用程序启动程序以及开发框架.下 ...

  6. 【php】关于尾部去除和分号问题

    One thing to remember is, if you decide to omit the closing PHP tag, then the last line of the file ...

  7. [转]ARM平台下独占访问指令LDREX和STREX

    参考:ARM平台下独占访问指令LDREX和STREX的原理与使用详解 全文转载如下: 为了实现线程间同步,一般都要在执行关键代码段之前加互斥(Mutex)锁,且在执行完关键代码段之后解锁.为了实现所谓 ...

  8. Java中HashMap底层原理源码分析

    在介绍HashMap的同时,我会把它和HashTable以及ConcurrentHashMap的区别也说一下,不过本文主要是介绍HashMap,其实它们的原理差不多,都是数组加链表的形式存储数据,另外 ...

  9. python基础——12(包的概念)

    一.模块 1.模块的加载顺序 加载顺序:内存-->内置-->sys.path(一系列自定义模块) import sys sys.path  #环境变量:存放文件路径的列表 重点:默认列表的 ...

  10. 【14】javascript有哪几种数据类型

    javascript有哪几种数据类型 六种基本数据类型 undefined null string boolean number symbol(ES6) 一种引用类型 Object **