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字符 | 翻译字符集中的字符,不包括双引号(" )、反斜杠(\ )和换行符 |
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 |
14. mutex
互斥算法避免多个线程同时访问共享资源。这会避免数据竞争,并提供线程间的同步支持。
(C++11) | 提供基本互斥设施 (类) |
(C++11) | 提供互斥设施,实现有时限锁定 (类) |
(C++11) | 提供能被同一线程递归锁定的互斥设施 (类) |
(C++11) | 提供能被同一线程递归锁定的互斥设施,并实现有时限锁定 (类) |
在标头 | |
(C++17) | 提供共享互斥设施 (类) |
(C++14) | 提供共享互斥设施并实现有时限锁定 (类) |
通用互斥体管理
(C++11) | 实现严格基于作用域的互斥体所有权包装器 (类模板) |
(C++17) | 用于多个互斥体的免死锁 RAII 封装器 (类模板) |
(C++11) | 实现可移动的互斥体所有权包装器 (类模板) |
(C++14) | 实现可移动的共享互斥体所有权封装器 (类模板) |
defer_lock try_to_lock adopt_lock defer_lock_t try_to_lock_t adopt_lock_t (C++11) | 用于指定锁定策略的标签 (标签) |
单次调用
(C++11) | 确保 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 实例的线程检验、等待或是操作这个状态。
(C++11) | 存储一个值以进行异步获取 (类模板) |
(C++11) | 打包一个函数,存储其返回值以进行异步获取 (类模板) |
(C++11) | 等待被异步设置的值 (类模板) |
(C++11) | 等待一个被异步设置的值(可能被其他未来体引用) (类模板) |
(C++11) | 异步运行一个函数(有可能在新线程中执行),并返回将保有它的结果的 std::future (函数模板) |
(C++11) | 指定 std::async 所用的启动策略 (枚举) |
(C++11) | 指定在 std::future 和 std::shared_future 上的定时等待的结果 (枚举) |
future错误
(C++11) | 报告与未来体或承诺体有关的错误 (类) |
(C++11) | 鉴别未来体错误类别 (函数) |
(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;
}
原文地址:https://blog.csdn.net/TimerSea/article/details/142697775
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!