This article describes the new features in the C++11 standards.

Table of Contents

Lambda Expression

Lambda Expression Syntax

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

Example:

int main() {
    char s[] = "Hello World!";
    int nuppercase = 0; // modified by the lambda
    for_each(s, s+sizeof(s), [&nuppercase] (char c) {
        if (isupper(c)) ++nuppercase;
    });
    cout << "number of uppercase char = " << nuppercase << endl;
}

[&nuppercase] means pass nuppercase by reference, otherwise pass by value.

自动类型推导和 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;

统一的初始化语法

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 vs<string> = { "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 函数

像以下形式的函数:

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&lt;&lt;pc&lt;&lt;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);
    filled.setdata(p);
}

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

class Movable {
    Movable (Movable&&); //move constructor
    Movable&& operator=(Movable&&); //move assignment operator
};

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++11 并发指南一(C++11 多线程初探)

C++11 新标准中引入了四个头文件来支持多线程编程,他们分别是<atomic> ,<thread>,<mutex>,<condition_variable>和<future>。

  • <atomic>:该头文主要声明了两个类, std::atomic 和 std::atomic_flag,另外还声明了一套 C 风格的原子类型和与 C 兼容的原子操作的函数。
  • <thread>:该头文件主要声明了 std::thread 类,另外 std::this_thread 命名空间也在该头文件中。
  • <mutex>:该头文件主要声明了与互斥量(mutex)相关的类,包括 std::mutex 系列类,std::lock_guard, std::unique_lock, 以及其他的类型和函数。
  • <condition_variable>:该头文件主要声明了与条件变量相关的类,包括 std::condition_variable 和 std::condition_variable_any。
  • <future>:该头文件主要声明了 std::promise, std::package_task 两个 Provider 类,以及 std::future 和 std::shared_future 两个 Future 类,另外还有一些与之相关的类型和函数,std::async() 函数就声明在此头文件中。

实例代码:

void taskfunc(int idx) {
    cout << "thread " << idx << " run" << endl;
}

#define TCNT 5U // thread count

int main(int , char **) {
    vector<thread *> tv;
    for (int i = 0; i < TCNT; ++i)
        tv.push_back(new thread(taskfunc, i));
    for (auto p : tv)
        p->join();

    for (auto p : tv)
        delete p;

    return 0;
}
// 运行结果:
% ./test
thread 0 run
thread 1 run
thread 2 run
tthhrreeaadd  43  rruunn

互斥量
多个线程同时访问共享资源的时候需要需要用到互斥量,当一个线程锁住了互斥量后,其他线程必须等待这个互斥量解锁后才能访问它。thread提供了四种不同的互斥量:
独占式互斥量non-recursive (std::mutex)
递归式互斥量recursive (std::recursive_mutex)
允许超时的独占式互斥量non-recursive that allows timeouts on the lock functions(std::timed_mutex)
允许超时的递归式互斥量recursive mutex that allows timeouts on the lock functions (std::recursive_timed_mutex)

独占式互斥量
独占式互斥量加解锁是成对的,同一个线程内独占式互斥量在没有解锁的情况下,再次对它进行加锁这是不对的,会得到一个未定义行为。

上面的程序输出结果看起来有点乱(std::cout不是线程安全的),所以我们需要在它们访问共享资源的时候使用互斥量加锁。在线程中std::mutex使用成员函数lock加锁unlock解锁,看起来工作的很好,但这样是不安全的,你得始终记住lock之后一定要unlock,但是如果在它们中间出现了异常或者线程直接退出了unlock就没有执行,因为这个互斥量是独占式的,所以在thread1没有解锁之前,其他使用这个互斥量加锁的线程会一直处于等待状态得不到执行。lock_guard模板类使用RAII手法封装互斥量,在实例化对象的时候帮你加锁,并且能保证在离开作用域的时候自动解锁,所以你应该用lock_guard来帮你加解锁。

mutex m;

void taskfunc(int idx) {
    m.lock();
    cout << "thread " << idx << " run" << endl;
    m.unlock();
}

新的智能指针类

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

新的算法

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

#include <algorithm>
// C++11 code
//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:

vector<int> vi;
// read 5 integers from stdin
copy_n(istream_iterator<int>(cin), 5, back_inserter(vi));

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

#include <algorithm>
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 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++。

参考

  1. http://blog.csdn.net/candy060403/article/details/7414456
  2. http://www.cnblogs.com/hujian/archive/2012/12/10/2810813.html
  3. 维基百科上关于 C++11 新特性的介绍,中文C++11介绍英文C++11介绍
  4. C++之父 Bjarne Stroustrup 的关于 C++11 的 FAQ
  5. http://www.open-std.org/jtc1/sc22/wg21/
  6. C++0x/C++11 Support in GCC:http://gcc.gnu.org/projects/cxx0x.html
  7. What is C++0x:https://www2.research.att.com/~bs/what-is-2009.pdf
  8. Overview of the New C++http://www.artima.com/shop/overview_of_the_new_cpp
  9. Overview of the New C++ (C++0x).pdf:http://ishare.iask.sina.com.cn/f/20120005.html?from=like
  10. A Brief Look at C++0xhttp://www.artima.com/cppsource/cpp0x.html
  11. Summary of C++11 Feature Availability in gcc and MSVC:http://www.aristeia.com/C++11/C++11FeatureAvailability.htm
  12. C++ 11: Come Closer:http://www.codeproject.com/Articles/344282/Cplusplus-11-Come-Closer
  13. C++11 threads, locks and condition variables: http://www.codeproject.com/Articles/598695/Cplusplus11-threads-locks-and-condition-variables
  14. Move Semantics and Perfect Forwarding in C++11:http://www.codeproject.com/Articles/397492/Move-Semantics-and-Perfect-Forwarding-in-Cplusplus
  15. http://solarianprogrammer.com/categories/C++11/
  16. C++11 Concurrency:http://www.baptiste-wicht.com/2012/03/cpp11-concurrency-part1-start-threads/
  17. http://www.hpl.hp.com/personal/Hans_Boehm/misc_slides/sfacm-cleaned.pdf
  18. http://en.cppreference.com/w/cpp/thread
  19. http://isocpp.org/blog/2012/12/c11-a-cheat-sheet-alex-sinyakov
  20. The Biggest Changes in C++11:http://blog.smartbear.com/c-plus-plus/the-biggest-changes-in-c11-and-why-you-should-care/
  21. Ten C++11 Features Every C++ Developer Should Use:http://www.codeproject.com/Articles/570638/Ten-Cplusplus11-Features-Every-Cplusplus-Developer
  22.  C++11 – A Glance [part 1 of n]:http://www.codeproject.com/Articles/312029/Cplusplus11-A-Glance-part-1-of-n
  23.  C++11 – A Glance [part 2 of n]:http://www.codeproject.com/Articles/314415/Cplusplus11-A-Glance-part-2-of-n
  24. C++11(及现代C++风格)和快速迭代式开发:http://mindhacks.cn/2012/08/27/modern-cpp-practices/
  25. Lambda Functions in C++11 - the Definitive Guide:http://www.cprogramming.com/c++11/c++11-lambda-closures.html
  26. Better types in C++11 - nullptr, enum classes (strongly typed enumerations) and cstdint:http://www.cprogramming.com/c++11/c++11-nullptr-strongly-typed-enum-class.html
  27. Rvalue-references-and-move-semantics-in-c++11:http://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html
  28. http://www.gotw.ca/publications/index.htm
  29. http://www.devx.com/SpecialReports/Door/38865
  30. Multi-threading in C++0x:http://accu.org/index.php/journals/1584
  31. C++ 0X feature summary cheat sheat:http://www.iesensor.com/blog/2011/05/31/c-0x-feature-summary-cheat-sheat/
  32. Multithreading in C++0x part 1: Starting Threads:http://www.justsoftwaresolutions.co.uk/threading/multithreading-in-c++0x-part-1-starting-threads.html
  33. http://en.cppreference.com/w/cpp/thread
  34. http://www.cplusplus.com/reference/multithreading/