C++11 新特性

来源:互联网 发布:js获取html标签属性 编辑:程序博客网 时间:2024/06/04 01:36

原文地址:http://blog.csdn.net/jofranks/article/details/17375061

C++11的设计目标是:

1.使得c++成为更好的适用于系统开发及库开发的语言。

2.使得c++成为更易于教学的语言。

3.保证语言的稳定性,以及和C++03及c语言的兼容性。

c++11 的增强点,主要包括:

1.通过内存模型,线程,原子操作等来支持本地并行编程。

2.通过统一初始化表达式、auto、declytype、移动语义等来统一对泛型编程的支持。

3.通过constexpr,POD等更好地支持系统编程。

4.通过内联命名空间,集成构造函数和右值引用等,以更好地支持库的构建。


下面我们把特性里面涉及的名词来解释一下:

1、统一初始化表达式:

c++11用大括号统一了初始化。在c++11中,这种初始化方法叫做:初始化列表 initializer list

[cpp] view plain copy
  1. int a[] {12, 22};  
  2. vector<int> a{ 1, 2, 3 };  
  3. map<intfloat> a = { { 1, 1.0f }, { 2, 2.0f } };  

在c++11中,我们可以通过以下几种形式来完成初始化的工作:

[cpp] view plain copy
  1. int a = 1 + 1;  
  2. int a = { 1 + 1 };  
  3. int a{ 1 + 1 };  
  4. int a(1 + 1);  


2、就地声明:

在类中,我们直接使用等号来初始化静态成员常量的方式,就是就地初始化。如:

[cpp] view plain copy
  1. class a{  
  2. public:  
  3.     a() :b(0) {}  
  4. private:  
  5.     int b;  
  6.     const static int b = 0;  
  7. };  

要注意的是,需要静态的常量成员。    还有一点就是需要整形或者枚举类型。


但是在我们c++11中,允许对非静态成员变量进行就地初始化:

[cpp] view plain copy
  1. class a{  
  2.     int b = 1;  
  3.     int c{ 2 };  
  4. };  
要注意的是,这里不能使用圆括号进行就地初始化。

在类中会涉及到构造函数初始化列表的问题,他跟就地初始化并不冲突,但是构造函数初始化列表的效果总是优先于就地初始化。

3、自定义类初始化列表:

可以通过#include<initializer_list>头文件中的initializer_list类模板支持初始化列表。
下面来看一下对类、函数和操作符进行初始化的例子:

[cpp] view plain copy
  1. #include <iostream>  
  2. #include <vector>  
  3. #include <string>  
  4. using namespace std;  
  5.   
  6. class C{  
  7. public:  
  8.     C(initializer_list<pair<string, int>> l, initializer_list<int> m, initializer_list<int> n)  
  9.     {  
  10.         auto i = l.begin();  
  11.         for (; i != l.end(); ++i)  
  12.         {  
  13.             data.push_back(*i);  
  14.         }  
  15.         auto j = m.begin();  
  16.         for (; j != m.end(); ++j)  
  17.             idx.push_back(*j);  
  18.   
  19.         auto s = n.begin();  
  20.         for (; s != n.end(); ++s)  
  21.             d.push_back(*s);  
  22.     }  
  23.   
  24.     C & operator[](initializer_list<int> l)  
  25.     {  
  26.         for (auto i = l.begin(); i != l.end(); ++i)  
  27.         {  
  28.             idx.push_back(*i);  
  29.         }  
  30.         return *this;  
  31.     }  
  32.   
  33.     C & operator = (int v)  
  34.     {  
  35.         if (idx.empty() != true)  
  36.         {  
  37.             for (auto i = idx.begin(); i != idx.end(); ++i)  
  38.             {  
  39.                 d.resize((*i > d.size()) ? *i : d.size());  
  40.                 d[*i - 1] = v;  
  41.             }  
  42.             idx.clear();  
  43.         }  
  44.         return *this;  
  45.     }  
  46.   
  47.     void Fun(initializer_list<int> l)  
  48.     {  
  49.         for (auto i = l.begin(); i != l.end(); ++i)  
  50.         {  
  51.             cout << *i << " ";  
  52.         }  
  53.         cout << endl;  
  54.     }  
  55. private:  
  56.     vector<pair<string, int>> data;  
  57.     vector<int> idx;  
  58.     vector<int> d;  
  59. };  
  60.   
  61. int main(void)  
  62. {  
  63.     C ctr{ { { "s", 1 }, { "e", 2 } }, { 3, 4 }, { 5, 6 } };  
  64.     ctr.Fun({ 1, 2 });  
  65.     ctr.Fun({});  
  66.   
  67.     ctr[{2, 3, 4}] = 7;  
  68.     ctr[{1, 3, 4, 5}] = 2;  
  69. };  


4、auto类型

先来看一下代码:

[cpp] view plain copy
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. int main(void)  
  5. {  
  6.     auto name = "hello world \n";  
  7.     cout << name;  
  8.   
  9.     char* name1 = "hello world \n";  
  10.     cout << name1;  
  11.   
  12.     return 0;  
  13. }  

根据上面的代码可以知道,auto和string的效果是一样的,是的,auto关键字要求编译器对变量name的类型进行自动推导。

在之前版本的C++中,auto的意思是具有自动存储期的局部变量,然而,一般情况下在函数里没有声明为static的变量总是具有自动储存期的局部变量。

在c++11中,auto声明变量的类型必须由编译器在编译时期推导得到。


5、declytype

declytype与auto类似,也可以进行类型推导,但是使用方式有一定区别。

declytype以一个普通的表达式为参数,返回该表达式的类型。他也是作为一个类型指示符,在编译时进行推导。看代码:

[cpp] view plain copy
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. int main(void)  
  5. {  
  6.     int i;  
  7.     decltype(i) j = 0;  
  8.     cout << typeid(j).name() << endl;  
  9.   
  10.     int a;  
  11.     double b;  
  12.     decltype(a + b) c;  
  13.     cout << typeid(c).name() << endl;  
  14.     return 0;  
  15. }  

6、继承和委托构造函数

c++中,继承类无法使用基类的非虚函数,除非显式使用。

而在c++11中,允许使用using声明来声明继承类的构造函数。如:

[cpp] view plain copy
  1. class A{  
  2. public:  
  3.     A() {}  
  4.     A(int i) {}  
  5. };  
  6.   
  7. class B :public A{  
  8. public:  
  9.     using A::A;     //继承构造函数  
  10.     virtual void s(int i)  
  11.     {  
  12.         cout << "B:" << i << endl;  
  13.     }  
  14. };  

在c++11中,标准继承构造函数被设计为跟派生类中的各种类默认函数(默认构造,析构,copy构造等)一样,都是隐式声明的。


c++11中构造函数可以调用同一个类的另一个构造函数,通过委派其他构造函数,过构造函数的类会更加容易编写:

[cpp] view plain copy
  1. class A{  
  2. public:  
  3.     A(int a)  
  4.     {  
  5.         this->a = a;  
  6.     }  
  7.     A() :A(0) {}  
  8.     int geta()  
  9.     {  
  10.         return a;  
  11.     }  
  12. private:  
  13.     int a;  
  14. };  
  15. int main(void)  
  16. {  
  17.     A b;  
  18.     cout << b.geta() << endl;  
  19.     return 0;  
  20. }  
委派构造函数:委派函数将构造的任务委派给了目标构造函数来完成这样一种类构造的方式。


7、右值引用

在c++11中,右值是由两个概念构成的,一个是将亡值,另一个则是纯右值。

在c++11中,右值引用就是对一个右值进行引用的类型。它能够以non-const值的方式传入,允许对象去改动他。

如:T&& a = returna();

这里a是右值引用,他的值等于returna返回的临时变量的值。


8、移动语义

来看一张图:


看到上图,应该可以看得出上半部分是copy构造函数咯。而下面的部分就是c++11中的新特性,叫做移动构造函数。

移动移动,移为己用,他偷走了临时变量中的资源。

[cpp] view plain copy
  1. class string  
  2. {  
  3.     string (string&&); //move constructor  
  4.     string&& operator=(string&&); //move assignment operator  
  5. };  


9、pod

c++11中,学习pod的概念是非常有用的。

plain old data。

plain表示了pod是一个普通的类型。

old体现了他与c的兼容性。

在pod中,有两个基本概念:平凡的+标准布局的。


10、内联命名空间

c++中,名字空间的目的是分割全局共享的名字空间。

在c++11中,引入了“内联命名空间”   inline namespace 就可以声明一个内联命名空间。

在内联空间中,允许程序员在父命名空间定义或者特化名字空间的模板。

当你需要长期维护,发布不同的版本的时候,可以用一下内联名字空间。 后续还会介绍。


11、nullptr

先来看一下NULL宏的定义:

[cpp] view plain copy
  1. #undef NULL  
  2. #if defined(_cplusplus)  
  3. #define NULL 0  
  4. #else  
  5. #define NULL ((void*)0)  
  6. #endif  
从代码中可以看到NULL可以是0或者是((void*)0),无论哪种都会出现这样那样的麻烦,所以在c++11中就出现了nullptr这个强类型。

[cpp] view plain copy
  1. void f(int); //#1  
  2. void f(char *);//#2  
  3. //C++03  
  4. f(0); //二义性  
  5. //C++11  
  6. f(nullptr) //无二义性,调用f(char*)  
在c++11用,我们要用nullptr来初始化指针。

12、override,final

final关键字的作用是使派生类不可覆盖它所修饰的虚函数。

override关键字的作用是保证编译器辅助检查。


13、lambda

他来源于函数式编程,也就是你在使用的地方定义,也就是“闭包”。

来看一下C++11中lambda的定义:

[cpp] view plain copy
  1. [capture](parameters) mutable->return-type {statement}   
[capture]:捕捉列表。

(parameters):参数列表。

mutable:mutable修饰符。

->return-type:返回类型。

{statement}:函数体。

看一下实例:

[cpp] view plain copy
  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. }  

c++11中,引入lambda有两个原因:

1)、可以定义匿名函数。

2)、编译器会把其转换成函数对象。

14、static_assert

c++中assert宏只有在程序运行时才能起作用,#error只有在编译器预处理时才能起作用。

在c++11中加入了static_assert宏来实现编译时期的断言:静态断言。

他的声明方式:

static_assert(编译时可推断出结果的表达式,一个简单的多字节的字符串)。

第一个参数:断言表达式,必须是编译时可知的。

第二个参数:警告信息。

比如:

[cpp] view plain copy
  1. static_assert(sizeof(int) == 4, " 32-bit");  




c++11中还有很多新变化,非常好用,c++11是一门新语言。