2011年12月5日Tiobe发布了2011年12月的编程语言排行榜。新一期排行榜Objective-C持续给力跃居第五位,而上一期关注的
谷歌Dart语言也上升到第68名,但PHP由第四位跌到第6名。从2001年Tiobe编程语言排行榜开始发布至今,C++几乎一直占据着第三名。

        C++11中值得关注的几大变化       

        分类:           
C++2011-06-23 18:0554832人阅读评论(103)收藏;)举报

赖勇浩( 声明:本文源自 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++ 标准委员会成员。

我在这里仅对文中提到的这些变化“追问为什么要引入这些变化”的一个探讨,只有知道为了什么,用在什么地方,我们才能真正学到这个知识。而以此你可以更深入地了解这些变化。所以,本文不是翻译。因为写得有些仓促,所以难免有问题,还请大家指正。

虽然Perl, Visual Basic
和PHP曾经也排到第3名,但和C++相比,他们就显得弱爆了,因为他们最多只能保持几个月而已。然而相比C++,微软的编程语言C#却更被看好,甚至有
人认为C#势必会取代C++,C++是垃圾语言。因为微软的大力推广,使用C#的人越来越多,而老将C++却正在被人遗忘。不甘寂寞,2011年C++再
次亮剑,13年来第一个重大修订版,C++11横空出世!

Lambda 表达式

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

[cpp] view
plaincopyprint?

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

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

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

[cpp] view
plaincopyprint?

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

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; }

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

 

Lambda 表达式

Lambda表达式来源于函数式编程,说白就了就是在使用的地方定义函数,有的语言叫“闭包”,如果
lambda 函数没有传回值,其回返类型可被完全忽略。 定义在与 lambda
函数相同作用域的变量参考也可以被使用。这种的变量集合一般被称作
closure。我在这里就不再讲这个事了。表达式的简单语法如下,

[capture](parameters)->return_type {body}

原文的作者给出了下面的例子:

int main()

{

char s[]=”Hello World!”;

int Uppercase = 0; //modified by the lambda

for_each(s, s+sizeof, [&Uppercase] {

if (isupper

Uppercase++;

});

cout << Uppercase << ” uppercase letters in: ” << s
<<endl;

}

<span class="Apple-style-span" style="font-family:
Georgia, 'Times New Roman', 'Bitstream
Charter', Times, serif; font-size: 13px; line-height: 19px;
white-space: normal;">在传统的STL中for_each()
这个玩意最后那个参数需要一个“函数对象”,所谓函数对象,其实是一个class,这个class重载了operator(),于是这个对象可以像函数的式样的使用。实现一个函数对象并不容易,需要使用template,比如下面这个例子就是函数对象的简单例子(实际的实现远比这个复杂):</span>

template <class T>

class less

{

public:

bool operator()(const T&l, const T&r)const

{

return l < r;

}

};

<span class="Apple-style-span" style="font-family:
Georgia, 'Times New Roman', 'Bitstream
Charter', Times, serif; font-size: 13px; line-height: 19px;
white-space: normal;">所以,</span><strong
style="font-family: Georgia, 'Times New Roman',
'Bitstream Charter', Times, serif; font-size: 13px;
line-height: 19px; white-space:
normal;">C++引入Lambda的最主要原因就是1)可以定义匿名函数,2)编译器会把其转成函数对象</strong><span
class="Apple-style-span" style="font-family: Georgia,
'Times New Roman', 'Bitstream Charter', Times,
serif; font-size: 13px; line-height: 19px; white-space:
normal;">。相信你会和我一样,会疑问为什么以前STL中的ptr_fun()这个函数对象不能用?就是把一个自然函数转成函数对象的)。原因是,ptr_fun()
的局限是其接收的自然函数只能有1或2个参数。</span>

那么,除了方便外,为什么一定要使用Lambda呢?它比传统的函数或是函数对象有什么好处呢?我个人所理解的是,这种函数之年以叫“闭包”,就是因为其限制了别人的访问,更私有。也可以认为他是一次性的方法。Lambda表达式应该是简洁的,极私有的,为了更易的代码和更方便的编程。

下面是前20名的编程语言排行

自动类型推导和 decltype

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

[c-sharp] view
plaincopyprint?

  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 

auto x=0; //0 是 int 类型,所以 x 也是 int 类型 auto c=’a’; //char auto
d=0.5; //double auto national_debt=14400000000000LL;//long long

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

[cpp] view
plaincopyprint?

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

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

那个迭代器可以声明为:

[cpp] view
plaincopyprint?

  1. auto ci=vi.begin(); 

auto ci=vi.begin();

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

[c-sharp] view
plaincopyprint?

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

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

 

自动类型推导 auto

在这一节中,原文主要介绍了两个关键字 auto 和 deltype,示例如下:

auto x=0; //x has type int because 0 is int

auto c=’a’; //char

auto d=0.5; //double

auto national_debt=14400000000000LL;//long long

<span class="Apple-style-span" style="font-family:
Georgia, 'Times New Roman', 'Bitstream
Charter', Times, serif; font-size: 13px; line-height: 19px;
white-space: normal;">auto
最大的好处就是让代码简洁,尤其是那些模板类的声明,比如:STL中的容器的迭代子类型。</span>

vector<int>::const_iterator ci = vi.begin();

可以变成:

auto ci = vi.begin();

模板这个特性让C++的代码变得很难读,不信你可以看看STL的源码,那是一个乱啊。使用auto必需一个初始化值,编译器可以通过这个初始化值推导出类型。因为auto是来简化模板类引入的代码难读的问题,如上面的示例,iteration这种类型就最适合用auto的,但是,我们不应该把其滥用。

比如下面的代码的可读性就降低了。因为,我不知道ProcessData返回什么?int?
bool? 还是对象?或是别的什么?这让你后面的程序不知道怎么做。

auto obj = ProcessData(someVariables);

但是下面的程序就没有问题,因为pObject的型别在后面的new中有了。

auto pObject = new SomeType<othertype>::SomeOtherType();

图片 1 

统一的初始化语法

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

[cpp] view
plaincopyprint?

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

std::string s(“hello”); int m=int(); //default initialization

还有等号形式的:

[cpp] view
plaincopyprint?

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

std::string s=”hello”; int x=5;

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

[cpp] view
plaincopyprint?

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

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

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

[cpp] view
plaincopyprint?

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

struct S { int x; S(): x(0) {} };

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

[cpp] view
plaincopyprint?

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

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中可以直观地初始化容器了:

[cpp] view
plaincopyprint?

  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”}}; 

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

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

[cpp] view
plaincopyprint?

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

class C { int a=7; //C++11 only public: C(); };

 

自动化推导 decltype

关于 decltype 是一个操作符,其可以评估括号内表达式的类型,其规则如下:

如果表达式e是一个变量,那么就是这个变量的类型。

如果表达式e是一个函数,那么就是这个函数返回值的类型。

如果不符合1和2,如果e是左值,类型为T,那么decltype是T&;如果是右值,则是T。

原文给出的示例如下,我们可以看到,这个让的确我们的定义变量省了很多事。

const vector<int> vi;

typedef decltype (vi.begin CIT;

CIT another_const_iterator;

还有一个适合的用法是用来typedef函数指针,也会省很多事。比如:

decltype(&myfunc) pfunc = 0;

typedef decltype(&A::func1) type;

auto 和 decltype 的差别和关系

Wikipedia 上是这么说的(关于decltype的规则见上)

#include <vector>

int main()

{

const std::vector<int> v;

auto a = v[0]; // a 的类型是 int

decltype b = 1; // b 的类型是 const int&, 因为函数的返回类型是

// std::vector<int>::operator[](size_type) const

auto c = 0; // c 的类型是 int

auto d = c; // d 的类型是 int

decltype e; // e 的类型是 int, 因为 c 的类型是int

decltype f = c; // f 的类型是 int&, 因为 是左值

decltype g; // g 的类型是 int, 因为 0 是右值

}

<span class="Apple-style-span" style="font-family:
Georgia, 'Times New Roman', 'Bitstream
Charter', Times, serif; font-size: 13px; line-height: 19px;
white-space: normal;">如果auto 和 decltype
在一起使用会是什么样子?能看下面的示例,下面这个示例也是引入decltype的一个原因——让C++有能力写一个
“ </span><a style="font-family: Georgia, 'Times
New Roman', 'Bitstream Charter', Times, serif;
font-size: 13px; line-height: 19px; white-space: normal;"
href="
function</a><span class="Apple-style-span"
style="font-family: Georgia, 'Times New Roman',
'Bitstream Charter', Times, serif; font-size: 13px;
line-height: 19px; white-space: normal;"> 模板”,</span>

template< typename LHS, typename RHS>

auto AddingFunc(const LHS &lhs, const RHS &rhs) -> decltype

{return lhs + rhs;}

<span class="Apple-style-span" style="font-family:
Georgia, 'Times New Roman', 'Bitstream
Charter', Times, serif; font-size: 13px; line-height: 19px;
white-space: normal;">这个函数模板看起来相当费解,其用到了auto
和 decltype
来扩展了已有的模板技术的不足。怎么个不足呢?在上例中,我不知道AddingFunc会接收什么样类型的对象,这两个对象的
+
操作符返回的类型也不知道,老的模板函数无法定义AddingFunc返回值和这两个对象相加后的返回值匹配,所以,你可以使用上述的这种定义。</span>

掀起C++ 11的神秘面纱

deleted 函数和 defaulted 函数

像以下形式的函数:

[cpp] view
plaincopyprint?

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

struct A { A()=default; //C++11 virtual ~A()=default; //C++11 };

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

[cpp] view
plaincopyprint?

  1. int func()=delete; 

int func()=delete;

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

[cpp] view
plaincopyprint?

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

struct NoCopy { NoCopy & operator =( const NoCopy & ) = delete; NoCopy (
const NoCopy & ) = delete; }; NoCopy a; NoCopy b(a);
//编译错误,拷贝构造函数是 deleted 函数

 

统一的初始化语法

C/C++的初始化的方法比较,C++ 11 用大括号统一了这些初始化的方法。

比如:POD的类型。

int arr[4]={0,1,2,3};

struct tm today={0};

关于POD相说两句,所谓POD就是Plain Old
Data,当class/struct是极简的、属于标准布局(standard-layout),以及他的所有非静态(non-static)成员都是POD时,会被视为POD。如:

struct A { int m; }; // POD

struct B { ~B(); int m; }; // non-POD, compiler generated default ctor

struct C { C {}; ~C(); int m; }; // non-POD, default-initialising m

<span class="Apple-style-span" style="font-family:
Georgia, 'Times New Roman', 'Bitstream
Charter', Times, serif; font-size: 13px; line-height: 19px;
white-space: normal;">POD的初始化有点怪,比如上例,new A;
和new A();
是不一样的,对于其内部的m,前者没有被初始化,后者被初始化了(不同
的编译器行为不一样,VC++和GCC不一样)。而非POD的初始化,则都会被初始化。</span>

从这点可以看出,C/C++的初始化问题很奇怪,所以,在C++
2011版中就做了统一。原文作者给出了如下的示例:

C c {0,0}; //C++11 only. 相当于: C c;

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, member array initializer

};

<span class="Apple-style-span" style="font-family:
Georgia, 'Times New Roman', 'Bitstream
Charter', Times, serif; font-size: 13px; line-height: 19px;
white-space: normal;">容器的初始化:</span>

// C++11 container initializer

vector<string> vs={ “first”, “second”, “third”};

map singers =

{ {“Lady Gaga”, “+1 555-7890”},

{“Beyonce Knowles”, “+1 555-0987”}};

<span class="Apple-style-span" style="font-family:
Georgia, 'Times New Roman', 'Bitstream
Charter', Times, serif; font-size: 13px; line-height: 19px;
white-space:
normal;">还支持像Java一样的成员初始化:</span>

class C

{

int a=7; //C++11 only

public:

C();

};

<strong style="font-family: Georgia, 'Times New
Roman', 'Bitstream Charter', Times, serif;
font-size: 13px; line-height: 19px; white-space: normal;">
</strong>

<strong style="font-family: Georgia, 'Times New
Roman', 'Bitstream Charter', Times, serif;
font-size: 13px; line-height: 19px; white-space:
normal;">Delete 和 Default 函数</strong>

我们知道C++的编译器在你没有定义某些成员函数的时候会给你的类自动生成这些函数,比如,构造函数,拷贝构造,析构函数,赋值函数。有些时候,我们不想要这些函数,比如,构造函数,因为我们想做实现单例模式。传统的做法是将其声明成private类型。

在新的C++中引入了两个指示符,delete意为告诉编译器不自动产生这个函数,default告诉编译器产生一个默认的。原文给出了下面两个例子:

struct A

{

A()=default; //C++11

virtual ~A()=default; //C++11

};

再如delete

struct NoCopy

{

NoCopy & operator =( const NoCopy & ) = delete;

NoCopy ( const NoCopy & ) = delete;

};

NoCopy a;

NoCopy b; //compilation error, copy ctor is deleted

<span class="Apple-style-span" style="font-family:
Georgia, 'Times New Roman', 'Bitstream
Charter', Times, serif; font-size: 13px; line-height: 19px;
white-space:
normal;">这里,我想说一下,为什么我们需要default?我什么都不写不就是default吗?不全然是,比如构造函数,因为只要你定义了一个构造函数,编译器就不会给你生成一个默认的了。所以,为了要让默认的和自定义的共存,才引入这个参数,如下例所示:</span>

struct SomeType

{

SomeType() = default; // 使用编译器生成的默认构造函数

SomeType(OtherType value);

};

<span class="Apple-style-span" style="font-family:
Georgia, 'Times New Roman', 'Bitstream
Charter', Times, serif; font-size: 13px; line-height: 19px;
white-space:
normal;">关于delete还有两个有用的地方是</span>

1)让你的对象只能生成在栈内存上:

struct NonNewable {

void *operator new(std::size_t) = delete;

};

2)阻止函数的其形参的类型调用:(若尝试以 double 的形参调用
f(),将会引发编译期错误, 编译器不会自动将 double 形参转型为 int
再调用f(),如果传入的参数是double,则会出现编译错误)

void f;

void f = delete;

C++标准在1998年获得通过后,有两位委员会委员预言,下一代C++标准将“肯定”包括内置的垃圾回收器(GC),但可能不会支持多线程,因为定义一
个可移植的线程模型涉及到的技术太复杂了,13年后,新的C++标准C++11也接近完成,你猜怎么着?让那两位委员没想到的是,本次更新还是没有包括
GC,但却包括了一个先进的线程库。C++之父Bjame
Stroustrup说C++11就像一个新语言,
的确,C++11核心已经发生了巨大的变化,它现在支持Lambda表达式,对象类型自动推断,统一的初始化语法,委托构造函数,deleted和defaulted函数声明nullptr,以及最重要的右值引用。

nullptr

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

[cpp] view
plaincopyprint?

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

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

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

[cpp] view
plaincopyprint?

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

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

 

nullptr

C/C++的NULL宏是个被有很多潜在BUG的宏。因为有的库把其定义成整数0,有的定义成
0。在C的时代还好。但是在C++的时代,这就会引发很多问题。你可以上网看看。这是为什么需要
nullptr 的原因。 nullptr 是强类型的。

void f; //#1

void f;//#2

//C++03

f; //二义性

//C++11

f //无二义性,调用f

所以在新版中请以 nullptr 初始化指针。

C++11中值得关注的几大变化

委托构造函数

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

[cpp] view
plaincopyprint?

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

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++中,构造函数之间不能互相调用,所以,我们在写这些相似的构造函数里,我们会把相同的代码放到一个私有的成员函数中。

class SomeType {

private:

int number;

string name;

SomeType( int i, string& s ) : number, name{}

public:

SomeType : SomeType( 0, “invalid” ){}

SomeType : SomeType( i, “guest” ){}

SomeType( string& s ) : SomeType{ PostInit(); }

};

<span class="Apple-style-span" style="font-family:
Georgia, 'Times New Roman', 'Bitstream
Charter', Times, serif; font-size: 13px; line-height: 19px;
white-space:
normal;">但是,为了方便并不足让“委托构造”这个事出现,最主要的问题是,基类的构造不能直接成为派生类的构造,就算是基类的构造函数够了,派生类还要自己写自己的构造函数:</span>

class BaseClass

{

public:

BaseClass(int iValue);

};

class DerivedClass : public BaseClass

{

public:

using BaseClass::BaseClass;

};

上例中,派生类手动继承基类的构造函数,
编译器可以使用基类的构造函数完成派生类的构造。
而将基类的构造函数带入派生类的动作 无法选择性地部分带入,
所以,要不就是继承基类全部的构造函数,要不就是一个都不继承。
此外,若牵涉到多重继承,从多个基类继承而来的构造函数不可以有相同的函数签名(signature)。
而派生类的新加入的构造函数也不可以和继承而来的基类构造函数有相同的函数签名,因为这相当于重复声明。(所谓函数签名就是函数的参数类型和顺序不)

◆ Lambda表达式

右值引用

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

[cpp] view
plaincopyprint?

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

void naiveswap(string &a, string & b) { string temp = a; a=b; b=temp; }

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

[cpp] view
plaincopyprint?

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

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 赋值操作符,如下:

[cpp] view
plaincopyprint?

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

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

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

 

右值引用和move语义

在老版的C++中,临时性变量(称为右值”R-values”,位于赋值操作符之右)经常用作交换两个变量。比如下面的示例中的tmp变量。示例中的那个函数需要传递两个string的引用,但是在交换的过程中产生了对象的构造,内存的分配还有对象的拷贝构造等等动作,成本比较高。

void naiveswap(string &a, string &b)

{

string temp = a;

a=b;

b=temp;

}

C++ 11增加一个新的引用(reference)类型称作右值引用(R-value
reference),标记为typename
&&。他们能够以non-const值的方式传入,允许对象去改动他们。这项修正允许特定对象创造出move语义。

举例而言,上面那个例子中,string类中保存了一个动态内存分存的char*指针,如果一个string对象发生拷贝构造,string类里的char*内存只能通过创建一个新的临时对象,并把函数内的对象的内存copy到这个新的对象中,然后销毁临时对象及其内存。这是原来C++性能上重点被批评的事

能过右值引用,string的构造函数需要改成“move构造函数”,如下所示。这样一来,使得对某个stirng的右值引用可以单纯地从右值复制其内部C-style的指针到新的string,然后留下空的右值。这个操作不需要内存数组的复制,而且空的暂时对象的析构也不会释放内存。其更有效率。

class string

{

string (string&&); //move constructor

string&& operator=(string&&); //move assignment operator

};

<span class="Apple-style-span" style="font-family:
Georgia, 'Times New Roman', 'Bitstream
Charter', Times, serif; font-size: 13px; line-height: 19px;
white-space: normal;">The C++11
STL中广泛地使用了右值引用和move语议。因此,很多算法和容器的性能都被优化了。</span>

◆自动类型推断和decltype

C++11 的标准库

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

C++ 11 STL 标准库

C++ STL库在2003年经历了很大的整容手术 Library Technical Report 1 。 TR1
中出现了很多新的容器类 (unordered_set, unordered_map,
unordered_multiset, 和 unordered_multimap)
以及一些新的库支持诸如:正则表达式, tuples,函数对象包装,等等。 C++11
批准了 TR1 成为正式的C++标准,还有一些TR1
后新加的一些库,从而成为了新的C++ 11
STL标准库。这个库主要包含下面的功能:

◆统一初始化语法

线程库

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

线程库

这们就不多说了,以前的STL饱受线程安全的批评。现在好 了。C++ 11
支持线程类了。这将涉及两个部分:第一、设计一个可以使多个线程在一个进程中共存的内存模型;第二、为线程之间的交互提供支持。第二部分将由程序库提供支持。大家可以看看promises
and futures,其用于对象的同步。 async() 函数模板用于发起并发任务,而
thread_local 为线程内的数据指定存储类型。更多的东西,可以查看 Anthony
Williams的 Simpler Multithreading in C++0x.


Deleted和Defaulted函数

新的智能指针类

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

新型智能指针

C++98 的知能指针是 auto_ptr, 在C++ 11中被废弃了。C++11
引入了两个指针类: shared_ptr 和 unique_ptr。
shared_ptr只是单纯的引用计数指针,unique_ptr 是用来取代auto_ptr。
unique_ptr 提供 auto_ptr 大部份特性,唯一的例外是 auto_ptr
的不安全、隐性的左值搬移。不像 auto_ptr,unique_ptr 可以存放在 C++0x
提出的那些能察觉搬移动作的容器之中。

为什么要这么干?大家可以看看《More Effective C++》中对 auto_ptr的讨论。

◆ nullptr

新的算法

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

[cpp] view
plaincopyprint?

  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 

#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:

[cpp] view
plaincopyprint?

  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); 

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

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

[cpp] view
plaincopyprint?

  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’} 

#include <numeric> 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 的
基准测试结果
如果觉得 C++
变化太大了,不必惊恐,花点时间来学习就好了。可能在你融会贯通新特性以后,你会同意
Stroustrup 的观点:C++11 是一门新的语言——一个更好的 C++。

新的算法

定义了一些新的算法: 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

<span class="Apple-style-span" style="font-family:
Georgia, 'Times New Roman', 'Bitstream
Charter', Times, serif; font-size: 13px; line-height: 19px;
white-space:
normal;">使用新的copy_n()算法,你可以很方便地拷贝数组。</span>

#include <algorithm>

int source[5]={0,12,34,50,80};

int target[5];

//copy 5 elements from source to target

copy_n(source,5,target);

<span class="Apple-style-span" style="font-family:
Georgia, 'Times New Roman', 'Bitstream
Charter', Times, serif; font-size: 13px; line-height: 19px;
white-space: normal;">使用 </span><code
style="font-size: 13px; line-height: 19px; white-space:
normal;">iota()</code><span
class="Apple-style-span" style="font-family: Georgia,
'Times New Roman', 'Bitstream Charter', Times,
serif; font-size: 13px; line-height: 19px; white-space:
normal;"> 可以用来创建递增的数列。如下例所示:</span>

include <numeric>

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++
20了。呵呵。不过C++ 11在性能上还是很快。原文还引用Stroustrup
的观点:C++11 是一门新的语言——一个更好的 C++。

如果把所有的改变都列出来,你会发现真多啊。我估计C++
Primer那本书的厚度要增加至少30%以上。C++的门槛会不会越来越高了呢?我不知道,但我个人觉得这门语言的确是变得越来越令人望而却步了,但是其重要性还是不言而喻的!(想起了某人和我说的一句话——学技术真的是太累了,还是搞方法论好混些?)

◆ 委托构造函数

◆ 右值引用

C++11标准库

C++于2003年以库技术报告1(TR1)的形式经历了重大改版,TR1包括新的容器类
(unordered_set,unordered_map,unordered_multiset和unordered_multimap)和多个支撑
正则表达式、元组和函数对象封装器等的新库。随着C++11标准获得通过,TR1和自它首次发布以来新增的库被正式纳入标准的C++标准,下面是
C++11标准库的一些特性:

线程库

站在程序员的角度来看,C++11最重要的新功能毫无疑问是并行操作,C++11拥有一个代表执行线程的线程类,在并行环境中用于同步,async()函
数模板启动并行任务,为线程独特的数据声明thread_local存储类型。如果你想找C++11线程库的快速教程,请阅读Anthony
William的“C++0x中更简单的多线程”。

新的智能指针类

C++98只定义了一个智能指针类auto_ptr,它现在已经被废弃了,C++11引入了新的智能指针类shared_ptr和最近添加的unique_ptr,两者都兼容其它标准库组件,因此你可以在标准容器内安全保存这些智能指针,并使用标准算法操作它们。

新的算法

C++11标准库定义了新的算法模仿all_of(),any_of()和none_of()操作,下面列出适用于ispositive()到
(first, first+n)范围,且使用all_of(), any_of() and 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算法也可用了,使用copy_n()函数,复制一个包含5个元素的数组到另一个数组的代码如下:

  1. #include  
  2. int source[5]={0,12,34,50,80};  
  3. int target[5];  
  4. //copy 5 elements from source to target  
  5. copy_n(source,5,target);  
  6.   

算法iota()创建了一个值顺序递增的范围,好像分配一个初始值给*first,然后使用前缀++使值递增,在下面的代码中,iota()分配连续值{10,11,12,13,14}给数组arr,并将{‘a’,’b’,’c’}分配给char数组c。

  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,套接字,GUI,反射以及前面提到的一个合适的自动垃圾回收器,但C++11的确也带来了许多新特性,让C++变得更加安全,高效,易学易用。

如果C++11的变化对你来说太大的话,也不要惊慌,多花些时间逐渐消化这一切,当你完全吸收了C++11的变化后,你可能就会同意Stroustrup的说法:C++11感觉就像一个新语言,一个更好的新语言。

下面是本期编程语言排行榜的其他排名数据和趋势走向。

前10名编程语言走势图
图片 2 

20到50名语言排行

图片 3

下面是第50到100的编程语言排名

(Visual) FoxPro, ABC, ActionScript, Algol, Alice, Bash, bc, BETA,
BlitzMax, Boo, Bourne shell, CFML, cg, CL (OS/400), Clean, Clojure, cT,
Dart, Dylan, Eiffel, Euphoria, Factor, Icon, IDL, Io, J, JavaFX Script,
JScript.NET, Korn shell, Lingo, MAD, MUMPS, NATURAL, Oberon, Occam,
OpenCL, OpenEdge ABL, Oz, PowerShell, REXX, S, Scala, SPSS, Standard ML,
SuperCollider, VBScript, Verilog, X10, xBase, Z shell

下面给出了编程语言类别的一年变化趋势
图片 4

(文/51CTO)