C++11 新特性汇总

文章目录

      • 1、指针、智能指针(nullptr、shared_ptr、std::weak_ptr)
        • (1)nullptr
        • (2)智能指针 shared_ptr、unique_ptr、weak_ptr
      • 2、类型推导(auto、decltype)
        • (1)auto
        • (2)decltype
        • (3)拖尾返回类型
      • 3、类特性修改
        • (1)默认函数行为(dafault、delete)
        • (2)构造函数(委托、继承构造函数using)
        • (3)显示控制虚函数重载(override、final)
      • 4、STL容器
        • (1)std::array
        • (2)std::forward_list
        • (3)无序容器unordered_map、unordered_set
        • (4)元组std::tuple
      • 5、多线程
        • (1)std::thread
        • (2)std::atomic
        • (3)std::condition_variable
      • 6、其他
        • (1)for循环(区间迭代)
        • (2)匿名函数 lamda表达式
        • (3)初始化列表std::initializer_list
        • (4)正则表达式

1、指针、智能指针(nullptr、shared_ptr、std::weak_ptr)

(1)nullptr

  • 作用: C++11 引入了 nullptr 关键字,专门用来区分空指针、0
  • 原有问题:传统 C++ 会把 NULL、0 视为同一种东西,这取决于编译器如何定义 NULL
  • 实现:nullptr 的类型为 nullptr_t,能够隐式的转换为任何指针或成员指针的类型,也能和他们进行相等或者不等的比较。当需要使用 NULL 时候,养成直接使用 nullptr的习惯

(2)智能指针 shared_ptr、unique_ptr、weak_ptr

shared_ptr
多个指针指向相同的对象。shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。每使用他一次,内部的引用计数加1,每析构一次,内部的引用计数减1,减为0时,自动删除所指向的堆内存。shared_ptr内部的引用计数是线程安全的,但是对象的读取需要加锁。

  • 初始化。智能指针是个模板类,可以指定类型,传入指针通过构造函数初始化。也可以使用make_shared函数初始化。不能将指针直接赋值给一个智能指针,一个是类,一个是指针。例如std::shared_ptr p4 = new int(1);的写法是错误的
  • 拷贝和赋值。拷贝使得对象的引用计数增加1,赋值使得原对象引用计数减1,当计数为0时,自动释放内存。后来指向的对象引用计数加1,指向后来的对象。
  • get函数获取原始指针
  • 注意不要用一个原始指针初始化多个shared_ptr,否则会造成二次释放同一内存
  • 注意避免循环引用,shared_ptr的一个最大的陷阱是循环引用,循环,循环引用会导致堆内存无法正确释放,导致内存泄漏。循环引用在weak_ptr中介绍。std::weak_ptr:与std::shared_ptr最大的差别是在赋值是,不会引起智能指针计数增加.shared_ptr会导致相互引用:导致的问题就是释放条件的冲突,最终也可能导致内存泄漏。用weak_ptr解决

unique_ptr
unique_ptr“唯一”拥有其所指对象,同一时刻只能有一个unique_ptr指向给定对象(通过禁止拷贝语义、只有移动语义来实现)。相比与原始指针unique_ptr用于其RAII的特性,使得在出现异常的情况下,动态资源能得到释放。unique_ptr指针本身的生命周期:从unique_ptr指针创建时开始,直到离开作用域。离开作用域时,若其指向对象,则将其所指对象销毁(默认使用delete操作符,用户可指定其他操作)。unique_ptr指针与其所指对象的关系:在智能指针生命周期内,可以改变智能指针所指对象,如创建智能指针时通过构造函数指定、通过reset方法重新指定、通过release方法释放所有权、通过移动语义转移所有权。

weak_ptr
weak_ptr是为了配合shared_ptr而引入的一种智能指针,因为它不具有普通指针的行为,没有重载operator*和->,它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况。weak_ptr可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。使用weak_ptr的成员函数use_count()可以观测资源的引用计数,另一个成员函数expired()的功能等价于use_count()==0,但更快,表示被观测的资源(也就是shared_ptr的管理的资源)已经不复存在。weak_ptr可以使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象, 从而操作资源。但当expired()==true的时候,lock()函数将返回一个存储空指针的shared_ptr

2、类型推导(auto、decltype)

(1)auto

auto新的语意实现自动类型推导,其推导阶段在编译期实现,而且由于编译期间需要判断左右值是否匹配,所以不会对编译和运行速度带来影响.使用auto的时候,编译器根据上下文情况,确定auto变量的真正类型
ps:auto作为函数返回值时,只能用于定义函数,不能用于声明函数。

(2)decltype

decltype()可以在编译期间获取变量的类型.auto能够让你声明一个变量。而decltype则能够从一个变量或表达式中得到类型

(3)拖尾返回类型

利用 auto 关键字将返回类型后置

decltype(x+y) add(T x, U y);

template
auto add(T x, U y) -> decltype(x+y) {
    return x+y;
}

3、类特性修改

(1)默认函数行为(dafault、delete)

在没有指定的情况下,c++会对类设置默认的构造函数、拷贝构造函数、赋值函数以及析构函数,但是有时候我们并不需要这些默认函数,因此在C++11中引入了对这些特性进行精确控制的特性:
default指定生成默认函数,
delete指定禁用默认函数。如果禁用了默认的构造函数和析构函数,必须指定一个自定义的函数。

class Test {
public:
    Test() = default;       //指定为Test类生成默认构造函数,如果设置为delete,就是禁用默认构造函数,如果禁用了
    ~Test() = default;      //默认析构函数
    Test(const Test&) = delete;    //禁用拷贝构造函数
    Test& operator=(const Test&) = delete;  //禁用类赋值函数
};

Test a;
Test b(a);      //error,因为已经被禁用
Test c = a;     //error,因为已经被禁用

(2)构造函数(委托、继承构造函数using)

C++11提供了两种新的构造函数特性,用于提升类构造的效率,分别是委托构造和继承构造,前者主要用于多构造函数的情况,而后者用在类继承方面.
(1)委托构造函数
委托构造的本质为了简化函数代码,做到复用其他构造函数代码的目的。用于多构造函数情况

(2)继承构造函数(using关键字)
c++在继承的时候,需要将构造函数的参数逐个传递到积父类的构造函数中完成父类的构造,这种效率是很低下的,因此c++11引入了继承构造的特性,使用using关键字.用于类继承方面

(3)显示控制虚函数重载(override、final)

由于虚函数的特性,可能会被意外进行重写,为了做到精确对虚函数重载的控制,c++11使用了override和final关键字完成对这一特性的实现.
override关键字 : 显式声明对虚函数进行重载
final关键字 : 显式终结类的继承和虚函数的重载使用

4、STL容器

(1)std::array

std::array 保存在栈内存中,相比堆内存中的 std::vector,我们能够灵活的访问这里面的元素,从而获得更高的性能。
std::array 会在编译时创建一个固定大小的数组,std::array 不能够被隐式的转换成指针,使用 std::array只需指定其类型和大小即可. std::array arr= {1,2,3,4};

(2)std::forward_list

std::forward_list 是一个列表容器,使用方法和 std::list 基本类似。
和 std::list 的双向链表的实现不同,std::forward_list 使用单向链表进行实现,提供了 O(1) 复杂度的元素插入,不支持快速随机访问(这也是链表的特点),也是标准库容器中唯一一个不提供 size() 方法的容器。当不需要双向迭代时,具有比 std::list 更高的空间利用率

(3)无序容器unordered_map、unordered_set

C++11 引入了两组无序容器,无序容器中的元素是不进行排序的,内部通过 Hash 表实现,插入和搜索元素的平均复杂度为 O(constant)。

std::unordered_map/std::unordered_multimap
td::map使用的数据结构为二叉树,而std::unordered_map内部是哈希表的实现方式,哈希map理论上查找效率为O(1)。但在存储效率上,哈希map需要增加哈希表的内存开销。

std::unordered_set/std::unordered_multiset
std::unordered_set的数据存储结构也是哈希表的方式结构,除此之外,std::unordered_set在插入时不会自动排序,这都是std::set表现不同的地方

(4)元组std::tuple

tuple是一个固定大小的不同类型值的集合,是泛化的std::pair,其中的元素个数不再限于两个,而且功能更加丰富.
元组的使用有三个核心的函数:
std::make_tuple: 构造元组
std::get: 获得元组某个位置的值
std::tie: 元组拆包
std::tuple_cat:合并两个元组,可以通过 std::tuple_cat 来实现

5、多线程

(1)std::thread

std::thread为C++11的线程类,使用方法和boost接口一样,非常方便,同时,C++11的std::thread解决了boost::thread中构成参数限制的问题,我想着都是得益于C++11的可变参数的设计风格。

(2)std::atomic

std::atomic为C++11分装的原子数据类型.
从功能上看,简单地说,原子数据类型不会发生数据竞争,能直接用在多线程中而不必我们用户对其进行添加互斥资源锁的类型。从实现上,大家可以理解为这些原子类型内部自己加了锁。

(3)std::condition_variable

std::condition_variable就像Linux下使用pthread_cond_wait和pthread_cond_signal一样,可以让线程休眠,直到被唤醒,现在在从新执行。线程等待在多线程编程中使用非常频繁,经常需要等待一些异步执行的条件的返回结果。

std::condition_variable cv;
while (!ready) cv.wait(lck); //调用cv.wait(lck)的时候,线程将进入休眠
cv.notify_all(); //休眠结束

6、其他

(1)for循环(区间迭代)

  • 作用: for循环语句更为简洁
  • 原有问题:循环语句麻烦
for(std::vector::iterator i = arr.begin(); i != arr.end(); ++i){
   xxx
}
  • 优化后:
for(auto &i : arr) {    
    std::cout << i << std::endl;
}

(2)匿名函数 lamda表达式

lambda 表达式:实际上就是提供了一个类似匿名函数的特性,而匿名函数则是在需要一个函数,但是又不想费力去命名一个函数的情况下去使用的. [ caputrue ] ( params ) opt -> ret { body; }; lambda表达式的大致原理:每当你定义一个lambda表达式后,编译器会自动生成一个匿名类(这个类重载了()运算符),我们称为闭包类型(closure type)。那么在运行时,这个lambda表达式就会返回一个匿名的闭包实例,是一个右值。所以lambda表达式的结果就是一个个闭包。对于复制传值捕捉方式,类中会相应添加对应类型的非静态数据成员。在运行时,会用复制的值初始化这些成员变量,从而生成闭包。对于引用捕获方式,无论是否标记mutable,都可以在lambda表达式中修改捕获的值。至于闭包类中是否有对应成员,C++标准中给出的答案是:不清楚的,与具体实现有关。
捕获列表:lambda表达式的捕获列表精细控制了lambda表达式能够访问的外部变量,以及如何访问这些变量。

  1. [] 不捕获任何变量。
  2. [&] 捕获外部作用域中所有变量,并作为引用在函数体中使用(按引用捕获)。
  3. [=] 捕获外部作用域中所有变量,并作为副本在函数体中使用(按值捕获)。注意值捕获的前提是变量可以拷贝,且被捕获的变量在 lambda 表达式被创建时拷贝,而非调用时才拷贝。如果希望lambda表达式在调用时能即时访问外部变量,我们应当使用引用方式捕获。
  4. [=,&foo]按值捕获外部作用域中所有变量,并按引用捕获foo变量。
  5. [bar]按值捕获bar变量,同时不捕获其他变量。
  6. [this]捕获当前类中的this指针,让lambda表达式拥有和当前类成员函数同样的访问权限。如果已经使用了&或者=,就默认添加此选项。捕获this的目的是可以在lamda中使用当前类的成员函数和成员变量。

(3)初始化列表std::initializer_list

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

C++11新特性参考:https://blog.csdn.net/jiange_zh/article/details/79356417

(4)正则表达式

正则表达式描述了一种字符串匹配的模式。一般使用正则表达式主要是实现下面三个需求:

  1. 检查一个串是否包含某种形式的子串;
  2. 将匹配的子串替换;
  3. 从某个串中取出符合条件的子串。
    C++11 提供的正则表达式库操作 std::string 对象,对模式 std::regex (本质是 std::basic_regex)进行初始化,通过 std::regex_match 进行匹配,从而产生 std::smatch (本质是 std::match_results 对象)。
    std::regex_match 用于匹配字符串和正则表达式,有很多不同的重载形式
    std::smatch 被定义为了 std::match_results,也就是一个子串迭代器类型的 match_results。使用 std::smatch 可以方便的对匹配的结果进行获取

你可能感兴趣的:(C++)