C++11新特性

C+11的标准规范于2011年2月正式落稿,而此前10余年间,C++正式标准一直是C++98/03。相比C++98/03,C++11有了非常多的变化。

包括大量的新特性:主要特征像lambda表达式和移动语义,实用的类型推导关键字auto,更简单的容器遍历方法,和大量使模板更容易使用的改进。

C++11将修复大量缺陷和降低代码拖沓,比如lambda表达式的支持将使代码更简洁。像移动语义这种特性会提高语言内核的基础效率,使你可以写出更快的代码。对模板系统的优化可以使你更容易写出泛型的代码。

新的标准库同时也会包含新的特性,包括对模板”>>“写法的支持,对多线程的支持,优化智能指针,将给那些还没用类似于boost::shared_ptr的人提供更简单的内存管理方法。

你可以通过阅读维基百科里的C++11页面学习。

 

以下是

C++11常用特性的使用经验总结

1. nullptr

nullptr 出现的目的是为了替代 NULL。

在某种意义上来说,传统 C++ 会把 NULL、0 视为同一种东西,这取决于编译器如何定义 NULL,有些编译器会将 NULL 定义为 ((void*)0),有些则会直接将其定义为 0。

C++ 不允许直接将 void * 隐式转换到其他类型,但如果 NULL 被定义为 ((void*)0),那么当编译char *ch = NULL;时,NULL 只好被定义为 0。

而这依然会产生问题,将导致了 C++ 中重载特性会发生混乱,例如:

void TestWork(int index){}
void TestWork(int * index){}

为了解决这个问题,C++11 引入了 nullptr 关键字,专门用来区分空指针、0。

当需要使用 NULL 时候,养成直接使用 nullptr的习惯。

 

2. 类型推导

C++11 引入了 auto 和 decltype 这两个关键字实现了类型推导,让编译器来操心变量的类型。

auto并没有让C++成为弱类型语言,也没有弱化变量什么,只是使用auto的时候,编译器根据上下文情况,确定auto变量的真正类型。

auto index = 10;              //int
auto str = "abc";            //char*
auto arr = new auto(10)     //int *
auto ret = AddTest(1,2);    //int

auto不能做什么?

auto 不能用于函数传参,因此下面的做法是无法通过编译的(考虑重载的问题,我们应该使用模板)。

auto 还不能用于推导数组类型。

 

decltype(Declared Type)“声明类型”

decltype 关键字是为了解决 auto 关键字只能对变量进行类型推导的缺陷而出现的。它的用法和 sizeof 很相似:

auto x = 1;
auto y = 2;
decltype(x+y) z;

在此过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值。 
有时候,我们可能需要计算某个表达式的类型。

 

3. 基于范围的 for 循环

int numbers[] = { 1,2,3,4,5 };
for (auto number : numbers)
{
    std::cout << number << std::endl;
}

 

4. 初始化列表

C++11 提供了统一的语法来初始化任意的对象

A a {1, 1.1};    // 统一的初始化语法
B b {2, 2.2};

C++11 还把初始化列表的概念绑定到了类型上,并将其称之为 std::initializer_list,允许构造函数或其他函数像参数一样使用初始化列表,这就为类对象的初始化与普通数组和 POD 的初始化方法提供了统一的桥梁,例如:

#include <initializer_list>

class Magic {
public:
    Magic(std::initializer_list<int> list) {}
};

Magic magic = {1,2,3,4,5};
std::vector<int> v = {1, 2, 3, 4};

 

5. 模板增强

在传统 C++ 的编译器中,>>一律被当做右移运算符来进行处理。但实际上我们很容易就写出了嵌套模板的代码:

std::vector<std::vector<int>> wow;

这在传统C++编译器下是不能够被编译的,而 C++11 开始,连续的右尖括号将变得合法,并且能够顺利通过编译。

 

6. Lambda 表达式

lambda 表达式能够方便地构造匿名函数,假设你的代码里面存在大量的小函数,而这些函数一般仅仅被调用一次。那么最好还是将他们重构成 lambda 表达式.

C++11 的 lambda 表达式规范例如以下:

[ capture ] ( params ) mutableexceptionattribute -> ret {body } (1)  
[ capture ] ( params ) -> ret { body } (2)  
[ capture ] ( params ) { body } (3)  
[ capture ] { body } (4)  

当中

  • (1) 是完整的 lambda 表达式形式。
  • (2) const 类型的 lambda 表达式,该类型的表达式不能改捕获("capture")列表中的值。
  • (3)省略了返回值类型的 lambda 表达式。可是该 lambda 表达式的返回类型能够依照下列规则推演出来:
    • 假设 lambda 代码块中包括了 return 语句,则该 lambda 表达式的返回类型由 return 语句的返回类型确定。
    • 假设没有 return 语句。则类似 void f(...) 函数。
  • (4)省略了參数列表,类似于无參函数 f()。

mutable 修饰符说明 lambda 表达式体内的代码能够改动被捕获的变量。而且能够訪问被捕获对象的 non-const 方法。

exception 说明 lambda 表达式是否抛出异常(noexcept)。以及抛出何种异常,类似于void f()throw(X, Y)。

attribute 用来声明属性。

另外,capture 指定了在可见域范围内 lambda 表达式的代码内可见得外部变量的列表。详细解释例如以下:

  • [a,&b] a变量以值的方式呗捕获,b以引用的方式被捕获。
  • [this] 以值的方式捕获 this 指针。
  • [&] 以引用的方式捕获全部的外部自己主动变量。
  • [=] 以值的方式捕获全部的外部自己主动变量。
  • [] 不捕获外部的不论什么变量。

此外,params 指定 lambda 表达式的參数。

int main()
{
    Test test;
    test.Add(add, 1, 2);

    TestAdd testAdd;
    test.Add(std::bind(&TestAdd::Add, testAdd, std::placeholders::_1, std::placeholders::_2), 1, 2);

    test.Add([](int a, int b)->int {
        std::cout << "lamda add fun" << std::endl;
        return a + b;
    },1,2);
    return 0;
}

联想仿函数 

 

7. 新增容器

std::array

std::forward_list

C++11 引入了两组无序容器:

std::unordered_map/std::unordered_multimap 和 std::unordered_set/std::unordered_multiset。

 

8. 语言级线程支持

std::thread 

std::atomic

std::mutex/std::unique_lock 

std::future/std::packaged_task 

std::condition_variable

 

9. 智能指针内存管理

std::shared_ptr

std::weak_ptr

 

10. 右值引用和move语义

c++11:左值、右值

 

11.正则表达式

 C++11 提供的正则表达式库操作 std::string 对象,对模式 std::regex (本质是 std::basic_regex)进行初始化,通过 std::regex_match 进行匹配,从而产生 std::smatch (本质是 std::match_results 对象)。

 

 

参考:

什么是C++11

C++11常用特性的使用经验总结

C++11常用新特性快速一览

C++11 Lambda表达式简单解析

posted @ 2018-08-22 13:17  _raindrop  阅读(218)  评论(0编辑  收藏  举报