c++11新特性

1. 字符串字面量

        c++11添加了字符串修饰符,通过定义用户定义的后缀,允许整数、浮点数、字符及字符串字面量产生用户定义类型的对象(以下列表内容参照c++网站介绍)。

"s字符序列 (可选)"
R"d字符序列 (可选)(r字符序列 (可选))d字符序列 (可选)"
L"s字符序列 (可选)"
LR"d字符序列 (可选)(r字符序列 (可选))d字符序列 (可选)"
u8"s字符序列 (可选)"
u8R"d字符序列 (可选)(r字符序列 (可选))d字符序列 (可选)"
u"s字符序列 (可选)"
uR"d字符序列 (可选)(r字符序列 (可选))d字符序列 (可选)"
U"s字符序列 (可选)"
UR"d字符序列 (可选)(r字符序列 (可选))d字符序列 (可选)"
s字符序列一个或多个s字符
s字符下列之一:
  • 一个基本s字符
  • 一个转义序列,定义见转义序列
  • 一个通用字符名,定义见转义序列转义序列转义序列
基本s字符翻译字符集中的字符,不包括双引号(")、反斜杠(\)和换行符
d字符序列一个或多个d字符,最多十六个
d字符基本字符集中的字符,不包括括号、反斜杠和空格
r字符序列一个或多个r字符,不得包含闭序列 )d字符序列"
r字符翻译字符集翻译字符集翻译字符集中的字符
十进制字面量 用户定义后缀
八进制字面量 用户定义后缀
十六进制字面量 用户定义后缀
二进制字面量 用户定义后缀
分数常量 指数部分 (可选) 用户定义后缀
数字序列 指数部分 用户定义后缀
字符字面量 用户定义后缀
字符串字面量 用户定义后缀

1-4) 用户定义整数字面量,例如 12_km

5-6) 用户定义浮点字面量,例如 0.5_Pa

7) 用户定义字符字面量,例如 'c'_X

8) 用户定义字符串字面量,例如 "abd"_L 或 u"xyz"_M

十进制字面量与整数字面量中相同,非零的十进制数位后随零或多个十进制数位
八进制字面量与整数字面量中相同,零后随零或多个八进制数位
十六进制字面量与整数字面量中相同,0x 或 0X 后随一个或多个十六进制数位
二进制字面量与整数字面量中相同,0b 或 0B 后随一或多个二进制数位
数字序列与浮点字面量中相同,一个十进制数字序列
分数常量与浮点字面量中相同,要么是一个后随小数点的 数字序列(123.),要么是一个可选的 数字序列 后随小数点和另一个 数字序列(1.0 或 .12)
指数部分与浮点字面量中相同,字母 e 或字母 E 后随可选的正负号,后随 数字序列
字符字面量与字符字面量中相同
字符串字面量与字符串字面量中相同,包括原始字符串字面量
用户定义后缀标识符,由字面量运算符字面量运算符模板声明引入

2. 属性说明符序列

        为类型、对象、代码等引入由实现定义的属性, 方便明确代码功能。

        1) 简单属性,例如 [[noreturn]]。

        2) 有命名空间的属性,例如 [[gnu::unused]]。

        3) 有实参的属性,例如 [[deprecated("原因")]]。

        4) 既有命名空间又有实参列表的属性。

3. lambda

        用于定义并创建匿名的函数对象,构造闭包,能够捕获作用域中的变量的无名函数对象,可创建为全局、局部函数形式。语法结构如下:

  • 没有显式模板形参的 lambda 表达式(可以不泛型)
[捕获 ] 前属性 (可选) (形参列表 ) 说明符 (可选) 异常 (可选)
后属性 (可选) 尾随类型 (可选) 约束 (可选) { 函数体 }
[捕获 ] { 函数体 }(C++23 前)
[捕获 ] 前属性 (可选) 尾随类型 (可选) { 函数体 }(C++23 起)
[捕获 ] 前属性 (可选) 异常
后属性 (可选) 尾随类型 (可选) { 函数体 }
(C++23 起)
[捕获 ] 前属性 (可选) 说明符 异常 (可选)
后属性 (可选) 尾随类型 (可选) { 函数体 }
(C++23 起)
  • 有显式模板形参的 lambda 表达式(必然泛型) (C++20 起)
[捕获 ] <模板形参 > 模板约束 (可选) 前属性 (可选) (形参列表 ) 说明符 (可选)
异常 (可选) 后属性 (可选) 尾随类型 (可选) 约束 (可选) { 函数体 }
[捕获 ] <模板形参 > 模板约束 (可选) { 函数体 }(C++23 前)
[捕获 ] <模板形参 > 模板约束 (可选)
前属性 (可选) 尾随类型 (可选) { 函数体 }
(C++23 起)
[捕获 ] <模板形参 > 模板约束 (可选) 前属性 (可选) 异常
后属性 (可选) 尾随类型 (可选) { 函数体 }
(C++23 起)
[捕获 ] <模板形参 > 模板约束 (可选) 前属性 (可选) 说明符 异常 (可选)
后属性 (可选) 尾随类型 (可选) { 函数体 }
(C++23 起)
  • 捕获 中单个捕获符的语法是
标识符(1)
标识符 ...(2)
标识符 初始化器(3)(C++14 起)
& 标识符(4)
& 标识符 ...(5)
& 标识符 初始化器(6)(C++14 起)
this(7)
*this(8)(C++17 起)
... 标识符 初始化器(9)(C++20 起)
& ... 标识符 初始化器(10)(C++20 起)

1) 简单的按复制捕获

2) 作为包展开的简单的按复制捕获

3) 带初始化器的按复制捕获

4) 简单的按引用捕获

5) 作为包展开的简单的按引用捕获

6) 带初始化器的按引用捕获

7) 当前对象的简单的按引用捕获

8) 当前对象的简单的按复制捕获

9) 初始化器为包展开的按复制捕获

10) 初始化器为包展开的按引用捕获

当默认捕获符是 & 时,后继的简单捕获符不能以 & 开始。

当默认捕获符是 = 时,后继的简单捕获符必须以 &*this (C++17 起)、this (C++20 起) 之一开始。

#include <iostream>

struct A {
  int m_a = 10;
  void ATest(int x) {
    auto Add = [this, x] {  // 捕获 this 指针
      m_a += x;
      std::cout << "m_a value: " << m_a << std::endl;
    };
  }
};

int sa = 10;  // 全局变量一般以引用方式捕获

void Test() {
  int a = 10;

  auto LA = [] { std::cout << "[] {}" << std::endl; }; // 无捕获变量形式
  auto LB = [] {   
    sa = 20;   // 修改全局变量
    std::cout << "[=]{ sa = 20; }, sa = " << sa << std::endl;
  };
  auto LC = [a] { std::cout << "[a]{ a = 30; }, a = " << a << std::endl; };// 值捕获
  auto LD = [&] {  // 引用捕获所有作用域变量
    a = 40; 
    std::cout << "[&]{ a = 40; }, a = " << a << std::endl;
  };
  auto LE = [&a] {    // 引用捕获 a 变量
    a = 50;
    std::cout << "[&a]{ a = 50; }, a = " << a << std::endl;
  };
  auto LF = [a]() mutable {  // 值捕获,属性 mutable 表示此变量可被修改
    a = 60;
    std::cout << "[a]{ a = 60; }, a = " << a << std::endl;
  };

  const int aa = 70;  // 不可修改变量,引用捕获或加属性 mutable 均不可修改
  auto LG = [&] { std::cout << "[&]{ }, aa = " << a << std::endl; };

  LA();
  LB();
  LC();
  LD();
  LE();
  LF();
  LG();

  A obj;
  obj.ATest(15);

  auto LH = [](int a, int b) -> int { return a + b; };  // 返回值语法
  std::cout << "a + b = " << LH(20, 36) << std::endl;
}

int main() {
  Test();
  return 0;
}

执行结果

[] {}
[=]{ sa = 20; }, sa = 20
[a]{ a = 30; }, a = 10
[&]{ a = 40; }, a = 40
[&a]{ a = 50; }, a = 50
[a]{ a = 60; }, a = 60
[&]{ }, aa = 50
a + b = 56

4. noexcept

    指定函数是否抛出异常。

#include <iostream>

double T1() noexcept { // 函数不抛出异常
  int ret = 10;
  throw("T1 throw");   // 运行时调用 std::terminate
}
double T2() noexcept(true) {   // 函数不抛出异常
  int ret = 10;
  throw("T2 throw");   // 运行时调用 std::terminate
}
double T3() noexcept(false) {   // 函数可抛出异常
  int ret = 10;
  throw("T3 throw");   // 运行时调用 std::terminate
}
double T4() {    // 函数可抛出异常
  int ret = 10;
  throw("T4 throw");   // 运行时调用 std::terminate
}

void Test() {
  try {
    T1(); 
    T2(); 
    T3(); 
    T4(); 
  } catch (const std::exception &e) {
    std::cout << e.what() << std::endl;
  }
}

int main() {
  Test();
  return 0;
}

5. alignas

    指定类型或对象的对齐要求。

#include <iostream>
 
// 每个 struct_float 类型对象都将被对齐到 alignof(float) 边界
// (通常为 4):
struct alignas(float) struct_float
{
    // 定义在此
};
 
// sse_t 类型的每个对象将对齐到 32 字节边界
struct alignas(32) sse_t
{
    float sse_data[4];
};
 
int main()
{
    struct default_aligned
    {
        float data[4];
    } a, b, c;
    sse_t x, y, z;
 
    std::cout
        << "alignof(struct_float) = " << alignof(struct_float) << '\n'
        << "sizeof(sse_t) = " << sizeof(sse_t) << '\n'
        << "alignof(sse_t) = " << alignof(sse_t) << '\n'
        << std::hex << std::showbase
        << "&a: " << &a << "\n"
           "&b: " << &b << "\n"
           "&c: " << &c << "\n"
           "&x: " << &x << "\n"
           "&y: " << &y << "\n"
           "&z: " << &z << '\n';
}

执行结果

alignof(struct_float) = 4
sizeof(sse_t) = 32
alignof(sse_t) = 32
&a: 0x7fffcec89930
&b: 0x7fffcec89940
&c: 0x7fffcec89950
&x: 0x7fffcec89960
&y: 0x7fffcec89980
&z: 0x7fffcec899a0

6. alignof

 查询类型的对齐要求。

#include <iostream>
 
struct Foo
{
    int   i;
    float f;
    char  c;
};
 
// 注:下面的 `alignas(alignof(long double))`
// 如果需要可以简化为 `alignas(long double)`
struct alignas(alignof(long double)) Foo2
{
    // Foo2 成员的定义...
};
 
struct Empty {};
 
struct alignas(64) Empty64 {};
 
int main()
{
    std::cout << "对齐字节数"  "\n"
        "- char                :" << alignof(char)    << "\n"
        "- 指针                :" << alignof(int*)     << "\n"
        "- Foo 类              :" << alignof(Foo)      << "\n"
        "- Foo2 类             :" << alignof(Foo2)     << "\n"
        "- 空类                :" << alignof(Empty)    << "\n"
        "- 带 alignas(64) 的空类:" << alignof(Empty64) << "\n";
}

执行结果

对齐字节数
- char                :1
- 指针                :8
- Foo 类              :4
- Foo2 类             :16
- 空类                :1
- 带 alignas(64) 的空类:64

7. 范围 for 循环

        在一个范围上执行 for 循环。用作对范围中的各个值(如容器中的所有元素)进行操作的传统for循环的更加可读的等价版本。

#include <iostream>
#include <map>
#include <vector>

void Test() {
  for (const auto &v : {10, 20, 30, 40, 50})
    std::cout << v << ", ";
  std::cout << std::endl;

  std::string str = "HelloWorld";
  for (const auto &v : str)
    std::cout << v << ", ";
  std::cout << std::endl;

  std::vector<int> vec{10, 20, 30, 40, 50};
  for (const auto &v : vec)
    std::cout << v << ", ";
  std::cout << std::endl;

  std::map<int, int> mapTmp;
  mapTmp[0] = 10;
  mapTmp[1] = 20;
  mapTmp[2] = 30;
  mapTmp[3] = 40;
  mapTmp[4] = 50;

  for (const auto &pa : mapTmp)
    std::cout << pa.first << " | " << pa.second << ", ";
  std::cout << std::endl;
}

int main() {
  Test();
  return 0;
}

执行结果

10, 20, 30, 40, 50, 
H, e, l, l, o, W, o, r, l, d, 
10, 20, 30, 40, 50, 
0 | 10, 1 | 20, 2 | 30, 3 | 40, 4 | 50, 

8. 内存模型

为 C++ 抽象机器的目的定义了计算机内存存储的语义。可为 C++ 程序所用的内存是字节 的一或多个连续序列。内存中的每个字节拥有唯一的地址

说明符可以在...的声明说明符序列 中出现
变量声明函数声明 结构化绑定声明 
(C++17 起)
非成员成员非成员成员
非形参 函数形参  非静态  静态  非静态  静态 
auto 仅限块作用域 不适用
register仅限块作用域不适用
static声明为静态 仅限命名空间作用域 声明为静态
 thread_local 
extern

9. static_assert

    程序编译时断言检查。

10. shared_ptr

  std::shared_ptr 是一种通过指针保持对象共享所有权的智能指针。多个 shared_ptr 对象可持有同一对象。下列情况之一出现时销毁对象并解分配其内存:

  • 最后剩下的持有对象的 shared_ptr 被销毁;
  • 最后剩下的持有对象的 shared_ptr 被通过 operator= 或 reset() 赋值为另一指针。

用 delete 表达式或在构造期间提供给 shared_ptr 的定制删除器销毁对象。

#include <iostream>
#include <memory>
 
struct MyObj
{
    MyObj() { std::cout << "构造 MyObj\n"; }
 
    ~MyObj() { std::cout << "析构 MyObj\n"; }
};
 
struct Container : std::enable_shared_from_this<Container> // 注: 公开继承
{
    std::shared_ptr<MyObj> memberObj;
 
    void CreateMember() { memberObj = std::make_shared<MyObj>(); }
 
    std::shared_ptr<MyObj> GetAsMyObj()
    {
        // 为成员使用使用别名 shared_ptr
        return std::shared_ptr<MyObj>(shared_from_this(), memberObj.get());
    }
};
 
#define COUT(str) std::cout << '\n' << str << '\n'
 
#define DEMO(...) std::cout << #__VA_ARGS__ << " = " << __VA_ARGS__ << '\n'
 
int main()
{
    COUT("创建共享容器");
    std::shared_ptr<Container> cont = std::make_shared<Container>();
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
 
    COUT("创建成员");
    cont->CreateMember();
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
 
    COUT("创建另一个共享容器");
    std::shared_ptr<Container> cont2 = cont;
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
    DEMO(cont2.use_count());
    DEMO(cont2->memberObj.use_count());
 
    COUT("GetAsMyObj");
    std::shared_ptr<MyObj> myobj1 = cont->GetAsMyObj();
    DEMO(myobj1.use_count());
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
    DEMO(cont2.use_count());
    DEMO(cont2->memberObj.use_count());
 
    COUT("复制别名对象");
    std::shared_ptr<MyObj> myobj2 = myobj1;
    DEMO(myobj1.use_count());
    DEMO(myobj2.use_count());
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
    DEMO(cont2.use_count());
    DEMO(cont2->memberObj.use_count());
 
    COUT("重置 cont2");
    cont2.reset();
    DEMO(myobj1.use_count());
    DEMO(myobj2.use_count());
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
 
    COUT("重置 myobj2");
    myobj2.reset();
    DEMO(myobj1.use_count());
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
 
    COUT("重置 cont");
    cont.reset();
    DEMO(myobj1.use_count());
    DEMO(cont.use_count());
}

执行输出

创建共享容器
cont.use_count() = 1
cont->memberObj.use_count() = 0
 
创建成员
构造 MyObj
cont.use_count() = 1
cont->memberObj.use_count() = 1
 
创建另一个共享容器
cont.use_count() = 2
cont->memberObj.use_count() = 1
cont2.use_count() = 2
cont2->memberObj.use_count() = 1
 
GetAsMyObj
myobj1.use_count() = 3
cont.use_count() = 3
cont->memberObj.use_count() = 1
cont2.use_count() = 3
cont2->memberObj.use_count() = 1
 
复制别名对象
myobj1.use_count() = 4
myobj2.use_count() = 4
cont.use_count() = 4
cont->memberObj.use_count() = 1
cont2.use_count() = 4
cont2->memberObj.use_count() = 1
 
重置 cont2
myobj1.use_count() = 3
myobj2.use_count() = 3
cont.use_count() = 3
cont->memberObj.use_count() = 1
 
重置 myobj2
myobj1.use_count() = 2
cont.use_count() = 2
cont->memberObj.use_count() = 1
 
重置 cont
myobj1.use_count() = 1
cont.use_count() = 0
析构 MyObj

11. unique_ptr

        std::unique_ptr 是一种智能指针,它通过指针持有并管理另一对象(对其负责),并在 unique_ptr 离开作用域时释放该对象。

在发生下列两者之一时,用关联的删除器释放对象:

  • 管理它的 unique_ptr 对象被销毁。
  • 通过 operator= 或 reset() 赋值另一指针给管理它的 unique_ptr 对象。

对象的释放,是通过调用 get_deleter()(ptr),用可能由用户提供的删除器进行的。默认删除器(std::default_delete)使用 delete 运算符,它销毁对象并解分配内存。

unique_ptr 亦可以不占有对象,该情况下称它为空 (empty)

#include <cassert>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <locale>
#include <memory>
#include <stdexcept>
 
// 用于下面运行时多态演示的辅助类
struct B
{
    virtual ~B() = default;
 
    virtual void bar() { std::cout << "B::bar\n"; }
};
 
struct D : B
{
    D() { std::cout << "D::D\n"; }
    ~D() { std::cout << "D::~D\n"; }
 
    void bar() override { std::cout << "D::bar\n"; }
};
 
// 消费 unique_ptr 的函数能以值或以右值引用接收它
std::unique_ptr<D> pass_through(std::unique_ptr<D> p)
{
    p->bar();
    return p;
}
 
// 用于下面自定义删除器演示的辅助函数
void close_file(std::FILE* fp)
{
    std::fclose(fp);
}
 
// 基于 unique_ptr 的链表演示
struct List
{
    struct Node
    {
        int data;
        std::unique_ptr<Node> next;
    };
 
    std::unique_ptr<Node> head;
 
    ~List()
    {
        // 循环按顺序销毁各列表节点,默认析构函数将会递归调用其 `next` 指针的析构函数,
        // 这在足够大的链表上可能造成栈溢出。
        while (head)
        {
            auto next = std::move(head->next);
            head = std::move(next);
        }
    }
 
    void push(int data)
    {
        head = std::unique_ptr<Node>(new Node{data, std::move(head)});
    }
};
 
int main()
{
    std::cout << "1) 独占所有权语义演示\n";
    {
        // 创建一个(独占)资源
        std::unique_ptr<D> p = std::make_unique<D>();
 
        // 转移所有权给 `pass_through`,而它再通过返回值将所有权转移回来
        std::unique_ptr<D> q = pass_through(std::move(p));
 
        // p 现在是已被移动的“空”状态,等于 nullptr
        assert(!p);
    }
 
    std::cout << "\n" "2) 运行时多态演示\n";
    {
        // 创建派生类资源并通过基类指向它
        std::unique_ptr<B> p = std::make_unique<D>();
 
        // 动态派发如期工作
        p->bar();
    }
 
    std::cout << "\n" "3) 自定义删除器演示\n";
    std::ofstream("demo.txt") << 'x'; // 准备要读取的文件
    {
        using unique_file_t = std::unique_ptr<std::FILE, decltype(&close_file)>;
        unique_file_t fp(std::fopen("demo.txt", "r"), &close_file);
        if (fp)
            std::cout << char(std::fgetc(fp.get())) << '\n';
    } // `close_file()` 于此调用(若 `fp` 为空)
 
    std::cout << "\n" "4) 自定义 lambda 表达式删除器和异常安全性演示\n";
    try
    {
        std::unique_ptr<D, void(*)(D*)> p(new D, [](D* ptr)
        {
            std::cout << "由自定义删除器销毁...\n";
            delete ptr;
        });
 
        throw std::runtime_error(""); // `p` 若为普通指针则此处将泄漏
    }
    catch (const std::exception&)
    {
        std::cout << "捕获到异常\n";
    }
 
    std::cout << "\n" "5) 数组形式的 unique_ptr 演示\n";
    {
        std::unique_ptr<D[]> p(new D[3]);
    } // `D::~D()` 被调用 3 次
 
    std::cout << "\n" "6) 链表演示\n";
    {
        List wall;
        const int enough{1'000'000};
        for (int beer = 0; beer != enough; ++beer)
            wall.push(beer);
 
        std::cout.imbue(std::locale("en_US.UTF-8"));
        std::cout << "墙上有 " << enough << " 瓶啤酒...\n";
    } // 销毁所有啤酒
}

执行输出

1) 独占所有权语义演示
D::D
D::bar
D::~D
 
2) 运行时多态演示
D::D
D::bar
D::~D
 
3) 自定义删除器演示
x
 
4) 自定义 lambda 表达式删除器和异常安全性演示
D::D
由自定义删除器销毁...
D::~D
捕获到异常
 
5) 数组形式的 unique_ptr 演示
D::D
D::D
D::D
D::~D
D::~D
D::~D
 
6) 链表演示
墙上有 1,000,000 瓶啤酒...

12. weak_ptr

    std::weak_ptr 是一种智能指针,它持有被 std::shared_ptr 管理的对象的非拥有性“弱”引用。在访问引用的对象前必须先转换为 std::shared_ptr。
    std::weak_ptr 实现临时所有权:当某个对象只有存在时才需要被访问,且随时可能被他人删除时,可以使用 std::weak_ptr 来跟踪该对象,需要获得临时所有权时,将其转换为 std::shared_ptr。如果此时销毁了原始 std::shared_ptr,则对象的生命周期将被延长,直到临时 std::shared_ptr 也被销毁为止。
    std::weak_ptr 的另一用法是打断被 std::shared_ptr 管理的对象组成的环状引用。若这种环被孤立(例如无指向环中的外部共享指针),则 shared_ptr 引用计数无法抵达零,而内存被泄露。可通过令环中的指针之一为弱指针来避免这种情况。

#include <iostream>
#include <memory>
 
std::weak_ptr<int> gw;
 
void observe()
{
    std::cout << "gw.use_count() == " << gw.use_count() << "; ";
    // 使用之前必须制作一个 shared_ptr 副本
    if (std::shared_ptr<int> spt = gw.lock())
        std::cout << "*spt == " << *spt << '\n';
    else
        std::cout << "gw 已过期\n";
}
 
int main()
{
    {
        auto sp = std::make_shared<int>(42);
	gw = sp;
 
	observe();
    }
 
    observe();
}

执行结果

gw.use_count() == 1: *spt == 42
gw.use_count() == 0: gw 已过期

13. thread

         类 thread 表示单个执行线程。线程允许多个函数同时执行。
        线程在构造关联的线程对象时立即开始执行(等待任何OS调度延迟),从提供给作为构造函数参数的顶层函数开始。顶层函数的返回值将被忽略,而且若它以抛异常终止,则调用 std::terminate。顶层函数可以通过 std::promise 或通过修改共享变量(可能需要同步,见 std::mutex 与 std::atomic )将其返回值或异常传递给调用方。
         std::thread 对象也可能处于不表示任何线程的状态(默认构造、被移动、detach 或 join 后),并且执行线程可能与任何 thread 对象无关(detach 后)。

#include <chrono>
#include <iostream>
#include <thread>

void Test() {
  std::thread t1([] {  // 创建线程
    int count = 0;
    while (count++ < 10) {
      std::cout << "thread t1 id: " << std::this_thread::get_id() << std::endl; // 打印ID
      std::this_thread::sleep_for(std::chrono::seconds(1)); // 此线程休眠 1 秒
    }
  });

  std::thread t2([] {  // 创建线程
    int count = 0;
    while (count++ < 5) {
      std::cout << "thread t2 id: " << std::this_thread::get_id() << std::endl;
      std::this_thread::sleep_for(std::chrono::seconds(1));
    }
  });
  t2.detach();  // 线程与当前进程分离,最终线程资源由系统回收
  t1.join();    // 等待线程结束,回收线程资源
}

int main() {
  Test();
  return 0;
}

执行结果

thread t1 id: thread t2 id: 129131230524992129131222132288

thread t2 id: 129131222132288
thread t1 id: 129131230524992
thread t2 id: 129131222132288
thread t1 id: 129131230524992
thread t2 id: 129131222132288
thread t1 id: 129131230524992
thread t2 id: 129131222132288
thread t1 id: 129131230524992
thread t1 id: 129131230524992
thread t1 id: 129131230524992
thread t1 id: 129131230524992
thread t1 id: 129131230524992
thread t1 id: 129131230524992

14. mutex

        互斥算法避免多个线程同时访问共享资源。这会避免数据竞争,并提供线程间的同步支持。

mutex

(C++11)

提供基本互斥设施
(类)

timed_mutex

(C++11)

提供互斥设施,实现有时限锁定
(类)

recursive_mutex

(C++11)

提供能被同一线程递归锁定的互斥设施
(类)

recursive_timed_mutex

(C++11)

提供能被同一线程递归锁定的互斥设施,并实现有时限锁定
(类)

在标头 <shared_mutex> 定义

shared_mutex

(C++17)

提供共享互斥设施
(类)

shared_timed_mutex

(C++14)

提供共享互斥设施并实现有时限锁定
(类)

通用互斥体管理

lock_guard

(C++11)

实现严格基于作用域的互斥体所有权包装器
(类模板)

scoped_lock

(C++17)

用于多个互斥体的免死锁 RAII 封装器
(类模板)

unique_lock

(C++11)

实现可移动的互斥体所有权包装器
(类模板)

shared_lock

(C++14)

实现可移动的共享互斥体所有权封装器
(类模板)

defer_lock  try_to_lock  adopt_lock  defer_lock_t  try_to_lock_t  adopt_lock_t

(C++11)

用于指定锁定策略的标签
(标签)

单次调用

once_flag

(C++11)

确保 call_once 只调用函数一次的帮助对象
(类)

call_once

(C++11)

仅调用函数一次,即使从多个线程调用
(函数模板)
#include <iostream>
#include <mutex>
#include <thread>

int calcCount = 0;
std::mutex mtx;

void Test() {
  std::thread t1([] {
    int count = 0;
    std::lock_guard<std::mutex> locker(mtx);
    while (count++ < 10) {
      calcCount++;
    }
  });

  std::thread t2([] {
    int count = 0;
    std::lock_guard<std::mutex> locker(mtx);
    while (count++ < 5) {
      calcCount++;
    }
  });
  t2.join();
  t1.join();

  std::cout << "calcCount = " << calcCount << std::endl;
}

int main() {
  Test();
  return 0;
}

15. 条件变量

        条件变量是允许多个线程相互交流的同步原语。它允许一定量的线程等待(可以定时)另一线程的提醒,然后再继续。条件变量始终关联到一个互斥体。

#include <chrono>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <queue>
#include <thread>

int calcCount = 0;
std::mutex mtx;
std::condition_variable cond;
std::vector<int> vecData;

void Test() {
  std::thread consumer([] {
    while (true) {
      std::unique_lock<std::mutex> locker(mtx);
      cond.wait(locker);

      if (vecData.empty())
        continue;

      std::cout << vecData.back() << std::endl;
      vecData.pop_back();
    }
  });

  std::thread producer([] {
    while (true) {
      std::this_thread::sleep_for(std::chrono::seconds(1));
      std::lock_guard<std::mutex> locker(mtx);
      vecData.push_back(calcCount++);
      cond.notify_one();
    }
  });
  producer.join();
  consumer.join();
}

int main() {
  Test();
  return 0;
}

16. future

        标准库提供了一些工具来获取异步任务(即在单独的线程中启动的函数)的返回值,并捕捉其所抛出的异常。这些值在共享状态中传递,其中异步任务可以写入其返回值或存储异常,而且可以由持有该引用该共享态的 std::future 或 std::shared_future 实例的线程检验、等待或是操作这个状态。

promise

(C++11)

存储一个值以进行异步获取
(类模板)

packaged_task

(C++11)

打包一个函数,存储其返回值以进行异步获取
(类模板)

future

(C++11)

等待被异步设置的值
(类模板)

shared_future

(C++11)

等待一个被异步设置的值(可能被其他未来体引用)
(类模板)

async

(C++11)

异步运行一个函数(有可能在新线程中执行),并返回将保有它的结果的 std::future
(函数模板)

launch

(C++11)

指定 std::async 所用的启动策略
(枚举)

future_status

(C++11)

指定在 std::future 和 std::shared_future 上的定时等待的结果
(枚举)

future错误

future_error

(C++11)

报告与未来体或承诺体有关的错误
(类)

future_category

(C++11)

鉴别未来体错误类别
(函数)

future_errc

(C++11)

鉴别未来体错误码
(枚举)
#include <chrono>
#include <future>
#include <iostream>
#include <thread>

void Test() {
  auto Promise = [] {    // 测试 promise
    std::promise<int> promise;
    std::future<int> future = promise.get_future();
    std::thread t1([&promise] {
      std::this_thread::sleep_for(std::chrono::seconds(1));
      promise.set_value(16);
    });

    t1.join();

    if (future.valid())
      std::cout << "get promise value: " << future.get() << std::endl;
  };
  Promise();

  auto Async = [] {   // 测试 async
    std::future<int> future =
        std::async(std::launch::async, []() -> int { return 26; });

    int result = future.get();
    std::cout << "result: " << result << std::endl;
  };
  Async();

  auto SharedFuture = [] {   // 测试 shared_future
    std::promise<int> promise;
    std::shared_future<int> future = promise.get_future().share();
    std::thread t1([&future] {
      int ret = future.get();
      std::cout << "thread id: " << std::this_thread::get_id() << ", " << ret
                << std::endl;
    });

    std::thread t2([&future] {
      int ret = future.get();
      std::cout << "thread id: " << std::this_thread::get_id() << ", " << ret
                << std::endl;
    });

    std::this_thread::sleep_for(std::chrono::seconds(1));
    promise.set_value(36);

    t1.join();
    t2.join();
  };
  SharedFuture();

  auto TimeoutFuture = [] {   // 测试 future 等待超时
    std::future<int> future =
        std::async(std::launch::async, []() -> int { return 46; });

    std::future_status st = future.wait_for(std::chrono::seconds(3));
    if (std::future_status::ready == st)
      std::cout << "std::future_status::ready " << std::endl;
    else
      std::cout << "!std::future_status::ready " << std::endl;
  };
  TimeoutFuture();

  auto PackagedTask = [] {   // 测试 packaged_task
    std::packaged_task<int(int, int)> task1(
        [](int a, int b) -> int { return a + b; });
    std::thread t1(std::ref(task1), 10, 26);
    t1.join();

    std::packaged_task<int(int, int)> task2(
        [](int a, int b) -> int { return a + b; });

    std::thread t2([&task2] { task2(36, 46); });
    std::future<int> ft = task2.get_future();
    if (ft.valid())
      std::cout << "t2 thread: " << ft.get() << std::endl;

    t2.join();
  };
  PackagedTask();
}

int main() {
  Test();
  return 0;
}

执行结果

get promise value: 16
result: 26
thread id: 132924221421120, 36
thread id: 132924137535040, 36
std::future_status::ready 
t2 thread: 82

17. function    

        类模板 std::function 是一种通用多态函数包装器。std::function 的实例能存储、复制及调用任何可复制构造 (CopyConstructible) 的可调用 (Callable) 目标——函数(通过其指针)、lambda 表达式、bind 表达式或其他函数对象,以及成员函数指针和数据成员指针。
存储的可调用对象被称为 std::function 的目标。若 std::function 不含目标,则称它为空。调用空 std::function 的目标导致抛出 std::bad_function_call 异常。

#include <functional>
#include <iostream>
 
struct Foo
{
    Foo(int num) : num_(num) {}
    void print_add(int i) const { std::cout << num_+i << '\n'; }
    int num_;
};
 
void print_num(int i)
{
    std::cout << i << '\n';
}
 
struct PrintNum
{
    void operator()(int i) const
    {
        std::cout << i << '\n';
    }
};
 
int main()
{
    // 存储自由函数
    std::function<void(int)> f_display = print_num;
    f_display(-9);
 
    // 存储 lambda
    std::function<void()> f_display_42 = []() { print_num(42); };
    f_display_42();
 
    // 存储到 std::bind 调用的结果
    std::function<void()> f_display_31337 = std::bind(print_num, 31337);
    f_display_31337();
 
    // 存储到成员函数的调用
    std::function<void(const Foo&, int)> f_add_display = &Foo::print_add;
    const Foo foo(314159);
    f_add_display(foo, 1);
    f_add_display(314159, 1);
 
    // 存储到数据成员访问器的调用
    std::function<int(Foo const&)> f_num = &Foo::num_;
    std::cout << "num_: " << f_num(foo) << '\n';
 
    // 存储到成员函数及对象的调用
    using std::placeholders::_1;
    std::function<void(int)> f_add_display2 = std::bind( &Foo::print_add, foo, _1 );
    f_add_display2(2);
 
    // 存储到成员函数和对象指针的调用
    std::function<void(int)> f_add_display3 = std::bind( &Foo::print_add, &foo, _1 );
    f_add_display3(3);
 
    // 存储到函数对象的调用
    std::function<void(int)> f_display_obj = PrintNum();
    f_display_obj(18);
 
    auto factorial = [](int n)
    {
        // 存储 lambda 对象以模拟“递归 lambda ”,注意额外开销
        std::function<int(int)> fac = [&](int n){ return (n < 2) ? 1 : n * fac(n - 1); };
        // 请注意 "auto fac = [&](int n){...};" 无法递归调用
        return fac(n);
    };
    for (int i{5}; i != 8; ++i)
        std::cout << i << "! = " << factorial(i) << ";  ";
    std::cout << '\n';
}

执行结果

-9
42
31337
314160
314160
num_: 314159
314161
314162
18
5! = 120;  6! = 720;  7! = 5040;

function转换为函数指针

#include <functional>
#include <iostream>

void Test1(int a) { std::cout << "value = " << a << std::endl; }

void Test() {
  std::function<void(int)> f1 = Test1;
  void (**ptr)(int) = f1.target<void (*)(int)>();
  (*ptr)(56);
}

int main() {
  Test();    // 输出 "value = 56"
  return 0;
}

18. atomic

        每个 std::atomic 模板的实例化和全特化均定义一个原子类型。如果一个线程写入原子对象,同时另一线程从它读取,那么行为有良好定义(数据竞争的细节见内存模型)。
另外,对原子对象的访问可以建立线程间同步,并按 std::memory_order 对非原子内存访问定序。std::atomic 既不可复制也不可移动。

19. 容器

        添加了 array(数组) forward_list(单向链表) unordered_map(无序一对一映射) unordered_multimap(无序一对多映射) unordered_set(无序不重复集合) unordered_multiset(无序可重复集合)。

20. 正则表达式

        正则表达式由一些普通字符和一些元字符(metacharacters)组成。普通字符包括大小写的字母和数字,而元字符则具有特殊的含义。

  • 元字符

.:匹配除换行符外的任意单个字符。
^:匹配行的开始。
$:匹配行的结束。
[]:匹配方括号内的任意单个字符。
[^]:匹配不在方括号内的任意单个字符。
():定义子表达式,用于分组和捕获匹配的部分。
*:匹配前导表达式零次或多次。
+:匹配前导表达式一次或多次。
?:匹配前导表达式零次或一次。
{}:指定前导表达式的确切重复次数或范围。
|:表示逻辑“或”操作。
\:转义字符,用于匹配字面量中的特殊字符。
\d:匹配任意数字。
\D:匹配任意非数字字符。
\w:匹配任意字母、数字或下划线。
\W:匹配任意非字母、数字或下划线字符。
\s:匹配任意空白字符。
\S:匹配任意非空白字符。

  • 预定义字符类

[[:alpha:]]:匹配任何字母。
[[:digit:]]:匹配任何数字。
[[:alnum:]]:匹配任何字母和数字。
[[:xdigit:]]:匹配任何16进制字符。
[[:space:]]:匹配任何空白字符。
[[:upper:]]:匹配任何大写字母。
[[:lower:]]:匹配任何小写字母。
[[:punct:]]:匹配任何标点符号。

  • 零宽断言(匹配宽度为零,满足一定的条件/断言)

(?=exp):正向预查,匹配位置后面能匹配表达式exp的部分。
(?<=exp):正向回顾后发断言,匹配位置前面能匹配表达式exp的部分。
(?!exp):负向预查,匹配位置后面不能匹配表达式exp的部分。
(?<!exp):负向回顾后发断言,匹配位置前面不能匹配表达式exp的部分。

  • 其他特性

std::regex_match:检查整个字符串是否与正则表达式匹配。
std::regex_search:在字符串中搜索与正则表达式匹配的子串。
std::regex_replace:替换字符串中与正则表达式匹配的部分。

#include <iostream>
#include <regex>

// 匹配电子邮件地址
int main() {
    std::string email = "dsdsdfsf@10086.com";
    std::regex pattern("\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}\\b");

    if (std::regex_match(email, pattern)) {
        std::cout << "The email address is valid." << std::endl;
    }
    else {
        std::cout << "The email address is invalid." << std::endl;
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值