C++ 11 新特性梳理

转载 https://mp.weixin.qq.com/s?__biz=MzAxNDI5NzEzNg==&mid=2651158293&idx=1&sn=a14b1e19e3739afe2cc88dc00c3203c3&chksm=8064a64ab7132f5c5d33c6655ea9b849fd969a2a4c2ab9ee6b0187d39962b17a470927ff011c&mpshare=1&scene=1&srcid=&pass_ticket=414ppzhCG6A5LGDbeLtQ8N3OA2drWpCqbxO%2BjOiVQy03x3ofOu4yStah02EhPk10#rd

 

在面试中,经常被问的一个问题就是:你了解C++11哪些新特性?一般而言,回答以下四个方面就够了:

 

  • “语法糖”:nullptr, auto自动类型推导,范围for循环,初始化列表, lambda表达式等

  • 右值引用和移动语义

  • 智能指针

  • C++11多线程编程:thread库及其相配套的同步原语mutex, lock_guard, condition_variable, 以及异步std::furture

  • 1、“语法糖”

     

    这部分内容一般是一句话带过的,但是有时候也需要说一些,比较重重要的就是auto和lambda。

     

    auto自动类型推导

     

    C语言也有auto关键字,但是其含义只是与static变量做一个区分,一个变量不指定的话默认就是auto。。因为很少有人去用这个东西,所以在C++11中就把原有的auto功能给废弃掉了,而变成了现在的自动类型推导关键字。用法很简单不多赘述,比如写一个auto a = 3, 编译器就会自动推导a的类型为int. 在遍历某些STL容器的时候,不用去声明那些迭代器的类型,也不用去使用typedef就能很简洁的实现遍历了。


    auto的使用有以下两点必须注意:

     

  • auto声明的变量必须要初始化,否则编译器不能判断变量的类型。

  • auto不能被声明为返回值,auto不能作为形参,auto不能被修饰为模板参数

  •  

    关于效率: auto实际上实在编译时对变量进行了类型推导,所以不会对程序的运行效率造成不良影响。另外,auto并不会影响编译速度,因为编译时本来也要右侧推导然后判断与左侧是否匹配。


    关于具体的推导规则,可以参考这里http://www.cnblogs.com/boydfd/p/4950334.html

     

    lambda表达式

     

    lambda表达式是匿名函数,可以认为是一个可执行体functor,语法规则如下:

     

     

     

     

    就我的理解而言,捕获的意思即为将一些变量展开使得为lambda内部可见,具体方式有如下几种

     

  • [a,&b] 其中 a 以复制捕获而 b 以引用捕获。

  • [this] 以引用捕获当前对象( *this )

  • [&] 以引用捕获所有用于 lambda 体内的自动变量,并以引用捕获当前对象,若存在

  • [=] 以复制捕获所有用于 lambda 体内的自动变量,并以引用捕获当前对象,若存在

  • [] 不捕获,大部分情况下不捕获就可以了

  •  

    一般使用场景:sort等自定义比较函数、用thread起简单的线程。

     

    2. 右值引用与移动语义

     

    右值引用是C++11新特性,它实现了转移语义和完美转发,主要目的有两个方面

     

  • 消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率

  • 能够更简洁明确地定义泛型函数
    C++中的变量要么是左值、要么是右值。通俗的左值定义指的是非临时变量,而左值指的是临时对象。左值引用的符号是一个&,右值引用是两个&&

  •  

    移动语义


    转移语义可以将资源(堆、系统对象等)从一个对象转移到另一个对象,这样可以减少不必要的临时对象的创建、拷贝及销毁。移动语义与拷贝语义是相对的,可以类比文件的剪切和拷贝。在现有的C++机制中,自定义的类要实现转移语义,需要定义移动构造函数,还可以定义转移赋值操作符。


    以string类的移动构造函数为例

     

     

     

    和拷贝构造函数类似,有几点需要注意:

     

    1、参数(右值)的符号必须是&&

    2、参数(右值)不可以是常量,因为我们需要修改右值

    3、参数(右值)的资源链接和标记必须修改,否则,右值的析构函数就会释放资源。转移到新对象的资源也就无效了。

     

    标准库函数std::move --- 将左值变成一个右值

     

    编译器只对右值引用才能调用移动构造函数,那么如果已知一个命名对象不再被使用,此时仍然想调用它的移动构造函数,也就是把一个左值引用当做右值引用来使用,该怎么做呢?用std::move,这个函数以非常简单的方式将左值引用转换为右值引用。

     

    完美转发 Perfect Forwarding

     

    完美转发使用这样的场景:需要将一组参数原封不动地传递给另一个函数。原封不动不仅仅是参数的值不变,在C++中还有以下的两组属性:

     

  • 左值/右值

  • const / non-const
    完美转发就是在参数传递过程中,所有这些属性和参数值都不能改变。在泛型函数中,这样的需求十分普遍。
    为了保证这些属性,泛型函数需要重载各种版本,左值右值不同版本,还要分别对应不同的const关系,但是如果只定义一个右值引用参数的函数版本,这个问题就迎刃而解了,原因在于:
    C++11对T&&的类型推导: 右值实参为右值引用,左值实参仍然为左值

  •  

    3、智能指针

     

    核心思想:为防止内存泄露等问题,用一个对象来管理野指针,使得在该对象构造时获得该指针管理权,析构时自动释放(delete).


    基于此思想C++98提供了第一个智能指针:auto_ptr


    auto_ptr基于所有权转移的语义,即将一个就的auto_ptr赋值给另外一个新的auto_ptr时,旧的那一个就不再拥有该指针的控制权(内部指针被赋值为null),那么这就会带来一些根本性的破绽:

     

  • 函数参数传递时,会有隐式的赋值,那么原来的auto_ptr自动失去了控制权

  • 自我赋值时,会将自己内部指针赋值为null,造成bug

  •  

    因为auto_ptr的各种bug,C++11标准基本废弃了这种类型的智能指针,转而带来了三种全新的智能指针:

     

  • shared_ptr,基于引用计数的智能指针,会统计当前有多少个对象同时拥有该内部指针;当引用计数降为0时,自动释放

  • weak_ptr,基于引用计数的智能指针在面对循环引用的问题将无能为力,因此C++11还引入weak_ptr与之配套使用,weak_ptr只引用,不计数

  • unique_ptr:遵循独占语义的智能指针,在任何时间点,资源智能唯一地被一个unique_ptr所占有,当其离开作用域时自动析构。资源所有权的转移只能通过std::move()而不能通过赋值

  • joinable(): 判断线程对象是否可以join,当线程对象被析构的时候如果对象``joinable()==true会导致std::terminate`被调用。

  • join(): 阻塞当前进程(通常是主线程),等待创建的新线程执行完毕被操作系统回收。

  • detach(): 将线程分离,从此线程对象受操作系统管辖。

  •  

     

    线程管理函数

     

    除了std::thread的成员函数外,在std::this_thread命名空间也定义了一系列函数用于管理当前线程。

     

    函数名作用
    get_id返回当前线程的id
    yield告知调度器运行其他线程,可用于当前处于繁忙的等待状态。相当于主动让出剩下的执行时间,具体的调度算法取决于实现
    sleep_for指定的一段时间内停止当前线程的执行
    sleep_until停止当前线程的执行直到指定的时间点

     

    至于mutex, condition_variable等同步原语以及future关键字的使用这里不做详细介绍,如果用过自然可以说出,没有用过的话这部分内容也不应该和面试官讨论。


     

    这里给出一个shared_ptr的简单实现:

     

     

     

    需要记住的事,在以下三种情况下会引起引用计数的变更:

     

    1、调用构造函数时: SmartPointer p(new Object());

    2、赋值构造函数时: SmartPointer p(const SmartPointer &p);

    3、赋值时:SmartPointer p1(new Object()); SmartPointer p2 = p1;

     

    C++11多线程编程

     

    线程 #include <thread>

     

    std::thread可以和普通函数和lambda表达式搭配使用。它还允许向线程执行函数传递任意多参数。

     

     

     

    上面就是一个最简单的使用std::thread的例子,函数func()在新起的线程中执行。调用join()函数是为了阻塞主线程,直到这个新起的线程执行完毕。线程函数的返回值都会被忽略,但线程函数可以接受任意数目的输入参数。

     

    std::thread的其他成员函数

     

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值