[C++11] 新特性总结

C++11新增加了哪些新特性?一般而言,大概有以下四个方面:

  • “语法糖”:nullptrauto自动类型推导,范围for循环,初始化列表, lambda表达式等
  • 右值引用和移动语义
  • 智能指针
  • C++11多线程编程:thread库及其相配套的同步原语mutexlock_guardcondition_variable, 以及异步std::furture

1. “语法糖”

       c++11新增加了一些语法糖,其中常用的就是auto和lambda。

1.1 auto自动类型推导

       C语言也有auto关键字,但是其含义只是与static变量做一个区分,一个变量不指定的话默认就是auto。。因为很少有人去用这个东西,所以在C++11中就把原有的auto功能给废弃掉了,而变成了现在的自动类型推导关键字。用法很简单不多赘述,

       比如写一个auto a = 3, 编译器就会自动推导a的类型为int. 在遍历某些STL容器的时候,不用去声明那些迭代器的类型,也不用去使用typedef就能很简洁的实现遍历了。
auto的使用有以下两点必须注意:

  • auto声明的变量必须要初始化,否则编译器不能判断变量的类型。
  • auto不能被声明为返回值,auto不能作为形参,auto不能被修饰为模板参数

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

1.2 lambda表达式

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

[捕获区](参数区){代码区};

auto add = [](int a, int b) {return a + b};

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

  • [a,&b] 其中 a 以复制捕获而 b 以引用捕获。
  • [this] 以引用捕获当前对象( *this )
  • [&] 以引用捕获所有用于 lambda 体内的自动变量,并以引用捕获当前对象,若存在
  • [=] 以复制捕获所有用于 lambda 体内的自动变量,并以引用捕获当前对象,若存在
  • [] 不捕获,大部分情况下不捕获就可以了

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

2. 右值引用与移动语义

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

  • 消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率
  • 能够更简洁明确地定义泛型函数
    C++中的变量要么是左值、要么是右值。通俗的左值定义指的是非临时变量,而左值指的是临时对象。左值引用的符号是一个&,右值引用是两个&&

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

MyString(MyString&& str) {
    std::cout << "Move Ctor source from " << str._data << endl;
    _len = str._len;
    _data = str._data;
    str._len = 0;
    str._data = NULL;
}

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

  1. 参数(右值)的符号必须是&&
  2. 参数(右值)不可以是常量,因为我们需要修改右值
  3. 参数(右值)的资源链接和标记必须修改,否则,右值的析构函数就会释放资源。转移到新对象的资源也就无效了。

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

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

       详情请见:[C++11] 右值引用和移动语义

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()而不能通过赋值

详情请见:[C++11] 智能指针

4、C++11多线程编程

4.1 线程

std::thread

  • 创建std::thread,一般会绑定一个底层的线程。若该thread还绑定好函数对象,则即刻将该函数运行于thread的底层线程。
  • 线程相关的很多默认是move语义,因为在常识中线程复制是很奇怪的行为。
  • joinable():是否可以阻塞至该thread绑定的底层线程运行完毕(倘若该thread没有绑定底层线程等情况,则不可以join)
  • join():本线程阻塞直至该thread的底层线程运行完毕。
  • detach():该thread绑定的底层线程分离出来,任该底层线程继续运行(thread失去对该底层线程的控制)。

4.2 互斥量

    为了避免多线程对共享变量的一段操作会发生冲突,引入了互斥体和锁。

std::mutex

  • 互斥体,一般搭配锁使用,也可自己锁住自己(lock(),unlock())。
  • 若互斥体被第二个锁请求锁住,则第二个锁所在线程被阻塞直至第一个锁解锁。

std::lock_guard

  • 简单锁,构造时请求上锁,释放时解锁,性能耗费较低。适用区域的多线程互斥操作。

std::unique_lock

  • 更多功能也更灵活的锁,随时可解锁或重新锁上(减少锁的粒度),性能耗费比前者高一点点。适用灵活的区域的多线程互斥操作。

4.3 原子变量

      原子变量的意思就是单个最小的、不可分割的变量(例如一个int),原子操作则指单个极小的操作(例如一个自增操),C++的原子类封装了这种数据对象,使多线程对原子变量的访问不会造成竞争。(可以利用原子类可实现无锁设计)

std::atomic_flag

  • 它是一个原子的布尔类型,可支持两种原子操作。(实际上mutex可用atomic_flag实现)
  • test_and_set(): 如果atomic_flag对象被设置,则返回true; 如果atomic_flag对象未被设置,则设置之,返回false
  • clear():清除atomic_flag对象。

std::atomic<T>

  • 对int, char, bool等基本数据类型进行原子性封装(其实是特化模板)。
  • store():修改被封装的值。
  • load() 读取被封装的值。

4.4 条件变量

      条件变量一般是用来实现多个线程的等待队列,即主线程通知(notify)有活干了,则等待队列中的其它线程就会被唤醒,开始干活。

std::condition_variable

  • wait(std::unique_lock<std::mutex>& lock, Predicate pred = [](){return true;}):pred()为true时直接返回,pred()为false时,lock必须满足已被当前线程锁定的前提。执行原子地释放锁定,阻塞当前线程,并将其添加到等待*this的线程列表中。
  • notify_one()/notify_all():激活某个或者所有等待的线程,被激活的线程重新获得锁。

参考:

1、深入理解C++11:C++11新特性解析与应用

2、深入应用C++11:代码优化与工程级应用

3、Effective Modern C++

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值