C++11(下篇)

news2024/11/24 1:51:47

文章目录

  • C++11
    • 1. 模版的可变参数
      • 1.1 模版参数包的使用
    • 2. lambda表达式
      • 2.1 Lambda表达式语法
        • 捕获列表说明
      • 2.2 lambda的底层
    • 3. 包装器
      • 3.1 function包装器
      • 3.2 bind
    • 4. 线程库
      • 4.1 thread类
      • 4.2 mutex类
      • 4.3 atomic类
      • 4.4 condition_variable类


C++11

1. 模版的可变参数

C++11支持模版的可变参数,可变模版参数比较抽象晦涩,我们只探讨其中基础。

template <class ...Args> // 模版参数包
void ShowList(Args... args) // 函数参数包
{}

...表明是可变模版参数,称为参数包,可以有 [ 0 , N ] [0,N] [0,N] 个模版参数。可变参数的模版函数,同样是根据调用情况,实例化出多份。

// 展示参数包个数
cout << sizeof...(Args) << endl;
cout << sizeof...(args) << endl;

1.1 模版参数包的使用

void showlist()
{
    cout << endl;
}

template<class T, class... Args>
void show_list(const T& val, Args... args)
{
    cout << val << " "; // 使用第一个参数
    showlist(args...); // 向下递归传递参数包
}

int main()
{
    showlist();
    showlist('1');
    showlist('1', 2);
    showlist('1', 2, "string");

    return 0;
}

参数包可以递归解析。

  1. 首先无参调用可直接调用无参版本。
  2. 其次有参调用的第一个参数会被val获取,之后的参数会被参数包获取。
  3. 使用完第一个参数后,可以传参数包下去递归调用。

打印剩余的参数:

void showlist()
{
	cout << endl;
}

template<class T, class...Args>
void showlist (const T& val, Args... args)
{
	cout << __FUNCTION__ <<"-->" << sizeof...(args)<<endl;
	//cout << val << " ";
	 showlist(args...);
	//cout << sizeof...(args) << endl;//计算大小

	//如何解析出可变参数包呢?
	//不能这么玩,语法不支持
	//for (int i = 0; i < sizeof...(args); i++)
	//{
	//	cout << args[i] << " ";
	//}
}


int main()
{
	showlist('x', 1,2,"string");
	return 0;
}

在这里插入图片描述

线程库就是使用可变模版参数,支持传递任意个参数。 

2. lambda表达式

2.1 Lambda表达式语法

[capture-list](parameters) mutable -> return-type { statement }
语法组成解释是否省略
[capture_list]捕获列表,捕捉当前作用域中的变量。分为传值捕捉和引用捕捉不可省略
(param_list)参数列表,形参默认具有const属性,可加mutable去除常属性可省略
-> ret_type指明返回类型可省略自动推导
{}函数体内容不可省略

各部分说明:

  1. capture-list: 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据来。判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用。
  2. (parameters): 参数列表与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略。
  3. mutable: 默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。
  4. -> return-type: 返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回0值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导
  5. { statement }: 函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量。

注意:

在lambda函数定义中,参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为空。因此C++11中最简单的lambda函数为:0;该lambda函数不能做任何事情。

看个样例代码:

int main()
{
    // 最简单的lambda表达式, 该lambda表达式没有任何意义
    []{}; 
    
    // 省略参数列表和返回值类型,返回值类型由编译器推导为int
    int a = 3, b = 4;
    [=]{return a + 3; }; 
    
    // 省略了返回值类型,无返回值类型
    auto fun1 = [&](int c){b = a + c; }; 
    fun1(10);
    cout<<a<<" "<<b<<endl;
    
    // 各部分都很完善的lambda函数
    auto fun2 = [=, &b](int c)->int{return b += a+ c; }; 
    cout<<fun2(10)<<endl;
    
    // 复制捕捉x
    int x = 10;
    auto add_x = [x](int a) mutable { x *= 2; return a + x; }; 
    cout << add_x(10) << endl; 
    return 0;
}

通过上述例子可以看出,lambda表达式实际上可以理解为无名函数,该函数无法直接调 用,如果想要直接调用,可借助auto将其赋值给一个变量。

捕获列表说明

[captrue_list] 捕获列表,用来捕捉当前作用域前和全局的变量。[]不可省略。

  • 分为传值捕捉和引用捕捉,引用捕捉[&a, &b]
  • [&]表示全引用捕捉,[=]表示全传值捕捉。捕捉所有能捕捉的变量。
  • [&a, =]表示混合捕捉,引用捕捉a变量,其他变量传值捕捉。但不可重复捕捉。
  • 捕捉列表和参数列表的变量默认用const修饰,可加mutable解除修饰
auto func1 = [a, b] () {};   // 传值捕捉
auto func2 = [&a, &b] () {}; // 引用捕捉
auto func3 = [=] () {}; // 全传值捕捉
auto func4 = [&] () {}; // 全引用捕捉

// 混合捕捉
[&a, &b, =](){}; // 引用捕捉a和b变量,其他变量传值捕捉
[=, a](){}; // 重复传值捕捉a,编译报错

注意

  • 父作用域指包含lambda函数的语句块

  • 语法上捕捉列表可由多个捕捉项组成,并以逗号分割。

​ 比如:[=, &a, &b]:以引用传递的方式捕捉变量a和b,值传递方式捕捉其他所有变量 [&,a, this]:值传递方式捕捉变量a和this,引用方式捕捉其他变量

//Lambda表达式捕捉列表的示例
auto lambda1 = [=, &b]() {
    std::cout << "Inside lambda1: a = " << a << ", b = " << b << std::endl;
    // 可以访问变量a的值,但只能以值传递的方式访问,而变量b可以以引用传递的方式访问
};
  • 捕捉列表不允许变量重复传递,否则就会导致编译错误。

​ 比如:[=, a]:=已经以值传递方式捕捉了所有变量,捕捉a重复

//Lambda表达式捕捉列表中不允许变量重复传递的示例
/// 以下代码将导致编译错误,因为变量a已经在捕捉列表中以值传递的方式捕捉了
// auto lambda2 = [=, a]() {}; // 编译错误:重复的捕捉变量'a'
// auto lambda3 = [=, &]() {}; //编译错误:传值和引用不可以同时存在


  • 在块作用域以外的lambda函数捕捉列表必须为空。

    // 在块作用域以外的lambda函数捕捉列表必须为空的示例
    int c = 20;
    auto lambda3 = [=]() {
    // 在此lambda函数中只能访问到变量a和b,无法访问外部的变量c
         std::cout << "Inside lambda3: a = " << a << ", b = " << b << std::endl;
    };
    
  • lambda表达式之间不能相互赋值,即使看起来类型相同

    // lambda表达式之间不能相互赋值的示例   
    // auto lambda4 = lambda3; 
    // 编译错误:无法从lambda函数'lambda3'初始化lambda函数'lambda4'
    

2.2 lambda的底层

lambda表达式不能相互赋值,即使看起来类型相同。

auto lamdba = []() {};
cout << sizeof(lamdba) << endl;        // 1
cout << typeid(lamdba).name() << endl; // class `int __cdecl main(void)'::`2'::<lambda_1>
									   // class <lambda_fcbffd5ae4b5ac20353abe92769a204f>

lambda表达式最后会被编译器处理成仿函数,所以lambda是个空类,大小为1。类名不同编译器实现不同,但能保证每个lambda表达式类名不同。

看看仿函数和lambda表达式的底层:

在这里插入图片描述

 

3. 包装器

包装器用来包装具有相同特征用途的多个可调用对象,便于以统一的形式调用它们。

3.1 function包装器

function包装器也叫做适配器,C++中的function本质是一个类模版。定义如下:

#include <functional>

template <class RetType, class... ArgsType> /* 声明返回类型和参数类型 */
	class function<Ret(Args...)>; 
// 普通函数
int func(int a, int b) { return a + b; }	
// 仿函数
struct functor {
    int operator()(int x, int y) { return x + y; }
};
// 非静态成员函数
struct Plus {
    int plus(int a, int b) { return a + b; }
};
// 静态成员函数
struct Sub {
    static int sub(int a, int b) { return a - b; }
};

std::function<int(int, int)>          f1 = f;
std::function<int(int, int)>          f2 = Functor();
std::function<int(Plus&, int, int)>   f3 = &Plus::plus;
std::function<int(int, int)>          f4 = Sub::sub;

封装成员函数时需要注意的点有:指定类域、对象参数、加取地址符。

struct Plus {
    Plus(int i) {}
    int plus(int a, int b) { return a + b; }
};

int main()
{
    function<int(Plus, int, int)> f1 = &Plus::plus;
    f1(Plus(1), 1, 2);
    
    function<int(Plus&, int, int)> f2 = &Plus::plus;
    Plus p(1);
    f2(p, 1, 2);
    
    function<int(Plus*, int, int)> f3 = &Plus::plus;
    f3(&p, 1, 2);
    
    function<int(Plus&&, int, int)> f4 = &Plus::plus;
    f4(Plus(3), 1, 2);

    return 0;
}

3.2 bind

bind函数也是一个函数包装器,本质是一个函数模版。生成一个新的可调用对象,来调整一个可调用对象的参数列表。

// without return 
template <class Func, class... Args>
    bind(Func&& fn, Args&&... args);

// with return type
template <class Ret, class Func, class... Args>  
    bind(Func&& fn, Args&&... args);
class suber
{
public:
    suber(int rt) : _rt(rt)
    {}

    int sub(int a, int b) { return (a - b) * _rt; }
private:
    int _rt;
};

// 通过bind调整参数顺序
function<int(int, int)> f1 = bind(suber, placeholders::_1, placeholders::_2);
function<int(int, int)> f2 = bind(suber, placeholders::_2, placeholders::_1);
cout << f1(2, 1) << endl;
cout << f2(1, 2) << endl;

// 通过bind调整参数个数
function<int(suber, int, int)> f3 = &Sub::sub;
function<int(int, int)> f4 = bind(&Sub::sub, Sub(3), placeholders::_1, placeholders::_2);
cout << f3(Sub(1), 2, 1) << endl;
cout << f4(2, 1) << endl;

 

4. 线程库

C++11提供了跨平台的具有面向对象特性的线程库,线程相关的系统知识在此不作赘述,直接讨论线程库的使用。

4.1 thread类

构造函数解释
thread() noexcept创建thread对象,不执行任何操作
thread(Fn&& fn, Args&&... args)传入调用对象和参数列表
thread(const thread&) = delete线程对象不可拷贝
thread(thread&& th)线程对象支持移动
成员函数解释
void join()等待线程
void detach()分离线程

关于当前线程的一些操作被放到this_thread类中:

this_thread 成员函数解释
thread::id get_id () noexcept返回线程ID
void sleep_for (const chrono::duration<Rep,Period>& rel_time)设置休眠时间
vector<thread> thds(N); // 线程池
atomic<int> x = 0;

for (auto& td : thds) {
    td = thread([&x, M](int i = 0) { 
            while (i++ < M) {
                cout << this_thread::get_id() << "->" << x << endl; // get_id()
                this_thread::sleep_for(std::chrono::seconds(1));    // sleep_for()
                x++;
            }
    	}
    );
}

for (auto& td : thds) {
    td.join();
}

4.2 mutex类

mutex类封装系统中的互斥锁,具体接口如下:

mutex解释
mutex() noexcept创建互斥锁
mutex (const mutex&) = delete禁止拷贝锁
void lock()加锁
void unlock()解锁
lock_guard解释
explicit lock_guard (mutex_type& m)构造函数
lock_guard (const lock_guard&) = delete不支持拷贝
unique_lock解释
explicit unique_lock (mutex_type& m)构造函数
unique_lock (const unique_lock&) = delete不支持拷贝
void lock()加锁
void unlock()解锁

捕获异常并解锁释放资源是不够友好的,因此异常时资源的处理,交给RAII解决。RAII即资源获取就是初始化,是一种管理资源的用法。

本质是将资源封装成类,自动调用构造和析构。以达到资源获取自动初始化,出作用域自动释放的效果

利用 RAII 封装的成“智能锁”,我们称之为锁守卫lock_guard

4.3 atomic类

保证自增减的原子性,可以使用原子操作。atomic类封装系统原子操作,具体接口如下:

template <class T> struct atomic;

T fetch_add (T val, memory_order sync = memory_order_seq_cst) volatile noexcept; // +=
T fetch_sub (T val, memory_order sync = memory_order_seq_cst) volatile noexcept; // -=
T fetch_and (T val, memory_order sync = memory_order_seq_cst) volatile noexcept; // &=
T fetch_or  (T val, memory_order sync = memory_order_seq_cst) volatile noexcept; // |=
T fetch_xor (T val, memory_order sync = memory_order_seq_cst) volatile noexcept; // ^=
T operator++() volatile noexcept; // ++
T operator--() volatile noexcept; // --

无锁算法CAS

Linux原子操作系统调用

4.4 condition_variable类

条件变量是线程同步的一种机制,主要包括两个动作:等待条件变量挂起,条件变量成立运行。

condition_variable解释
condition_variable()构造条件变量
condition_variable (const condition_variable&) = delete禁止拷贝条件变量
void wait (unique_lock<mutex>& lck)直接等待
void wait (unique_lock<mutex>& lck, Predicate pred)指定条件下等待
void notify_one() noexcept唤醒单个线程
void notify_all() noexcept唤醒多个线程
// wait的实现
template <class Predicate>  
void wait (unique_lock<mutex>& lck, Predicate pred)
{
    while (!pred()) /* pred()为假,进入等待 */
        wait(lck);
}

tex>& lck) | 直接等待 | |void wait (unique_lock& lck, Predicate pred) | 指定条件下等待 | |void notify_one() noexcept | 唤醒单个线程 | |void notify_all() noexcept` | 唤醒多个线程 |

// wait的实现
template <class Predicate>  
void wait (unique_lock<mutex>& lck, Predicate pred)
{
    while (!pred()) /* pred()为假,进入等待 */
        wait(lck);
}

模版参数pred是个可调用对象,其返回值代表线程是否进入临界区的条件。条件为真停止等待,条件为假进入等待。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1600926.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

初学若依笔记

初学若依 下载ruoyi(以前后端分离板为例) https://ruoyi.vip/ 部署 安装mysql安装redis将数据库和redis配置到若依 配置文件为 ruoyi-admin\src\main\resource\application-druid.yml 运行 略 开发自己的功能 创建模块 为了不影响原有功能&#xff0c;创建一个模块写自…

[AI]-(第0期):认知深度学习

深度学习是一种人工智能&#xff08;AI&#xff09;方法&#xff0c;用于教计算机以受人脑启发的方式处理数据。 深度学习模型可以识别图片、文本、声音和其他数据中的复杂模式&#xff0c;从而生成准确的见解和预测。 您可以使用深度学习方法自动执行通常需要人工智能完成的…

深入理解大语言模型微调技术

一、概念解析 1、什么是微调&#xff08;Fine-tuning&#xff09;&#xff1f; 大模型微调&#xff0c;也称为Fine-tuning&#xff0c;是指在已经预训练好的大型语言模型基础上&#xff08;一般称为“基座模型”&#xff09;&#xff0c;使用特定的数据集进行进一步的训练&am…

pandas基本用法

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、pandas的数据结构1、一维数组pd.Series1.1 pd.Series&#xff08;data,index,dtype&#xff09;示例1&#xff1a;不定义index示例2&#xff1a;自定义inde…

【C++学习】C++IO流

这里写目录标题 &#x1f680;C语言的输入与输出&#x1f680;什么是流&#x1f680;CIO流&#x1f680;C标准IO流&#x1f680;C文件IO流 &#x1f680;C语言的输入与输出 C语言中我们用到的最频繁的输入输出方式就是scanf ()与printf()。 scanf(): 从标准输入设备(键盘)读取…

14.C++常用的算法_排序算法

文章目录 遍历算法1. sort()代码工程运行结果 2. random_shuffle()代码工程运行结果第一次运行结果第二次运行结果第三次运行结果 3. merge()代码工程运行结果 4. reverse()代码工程运行结果 遍历算法 1. sort() 代码工程 sort()函数默认是升序排列&#xff0c;如果想要降序…

Jenkins配置windows/linux从节点

背景&#xff1a; 环境&#xff1a;jenkins环境&#xff08;Ubuntu&#xff09; 节点机器&#xff1a;Linux、Windows 前置条件&#xff1a; 节点机器&#xff1a;安装java、allure、python 1 Linux节点管理机器添加 1.1 系统管理->节点列表->New Node 1.2 节点配置…

基于微信小程序投票评选系统的设计与实现(论文+源码)_kaic

摘 要 社会发展日新月异&#xff0c;用计算机应用实现数据管理功能已经算是很完善的了&#xff0c;但是随着移动互联网的到来&#xff0c;处理信息不再受制于地理位置的限制&#xff0c;处理信息及时高效&#xff0c;备受人们的喜爱。所以各大互联网厂商都瞄准移动互联网这个潮…

Weblogic 数据源无法解析错误的解决方法

问题现象 javax.naming.NameNotFoundException: Unable to resolve datasource1. Resolved ; remaining name datasource1at weblogic.jndi.internal.BasicNamingNode.newNameNotFoundException(BasicNamingNode.java:1292)at weblogic.jndi.internal.BasicNamingNode.lookupH…

leetcode-合并两个有序链表

目录 题目 图解 方法一 方法二 代码(解析在注释中) 方法一 ​编辑方法二 题目 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1…

电机控制器电路板布局布线参考指导(五)

电机控制器电路板布局布线参考指导&#xff08;五&#xff09;大容量电容和旁路电容的放置 1.大容量电容的放置2.电荷泵电容器3.旁路电容/去耦电容的放置3.1 靠近电源3.2 靠近功率器件3.3 靠近开关电流源3.4 靠近电流感测放大器3.5 靠近稳压器 tips&#xff1a;资料主要来自网络…

Spring Boot 多环境配置:YML 文件的三种高效方法

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

【Redis 神秘大陆】006 灾备方案

六、Redis 灾备方案 6.1 存储方案 6.1.1 基础对比 RDB持久化AOF持久化原理周期性fork子进程生成持久化文件每次写入记录命令日志文件类型二进制dump快照文件文本appendonly日志文件触发条件默认超过300s间隔且有1s内超过1kb数据变更永久性每秒fsync一次文件位置配置文件中指…

基于Qt的二维码生成与识别

基于Qt的二维码生成与识别 一、获取QZxing开源库 1.通过封装的QZxing开源库生成和识别二维码&#xff0c;下载地址&#xff1a;GitCode - 开发者的代码家园https://gitcode.com/mirrors/ftylitak/qzxing/tree/master。 2.下载解压后&#xff0c;使用Qt Creator xx&#xff0…

如何采集opc服务器数据上传云端

为了进一步提高生产效率&#xff0c;生产制造的不断朝着智能化发展和升级&#xff0c;传统的自动化生产系统已经不能满足需求。传统的SCADA系统一般是用于现场的数据采集与控制&#xff0c;但是本地控制已经无法满足整个工厂系统智能化数字化的需求&#xff0c;智能化数字化是需…

NTC热敏电阻采集温度-单片机通用模板

NTC热敏电阻采集温度-单片机通用模板 一、NTC热敏电阻转换温度的原理二、AT104Tem.c的实现三、AT104Tem.h的实现 一、NTC热敏电阻转换温度的原理 ①NTC热敏电阻会随着温度的升高&#xff0c;电阻值R逐渐降低&#xff1b;②硬件搭建电阻分压电路采集ADC逆推热敏电阻当前的阻值&…

线上频繁fullgc问题-SpringActuator的坑

整体复盘 一个不算普通的周五中午&#xff0c;同事收到了大量了cpu异常的报警。根据报警表现和通过arthas查看&#xff0c;很明显的问题就是内存不足&#xff0c;疯狂无效gc。而且结合arthas和gc日志查看&#xff0c;老年代打满了&#xff0c;gc不了一点。既然问题是内存问题&…

[html]一个动态js倒计时小组件

先看效果 代码 <style>.alert-sec-circle {stroke-dasharray: 735;transition: stroke-dashoffset 1s linear;} </style><div style"width: 110px; height: 110px; float: left;"><svg style"width:110px;height:110px;"><cir…

新零售门店、商品、会员管理指标体系总览

新零售&#xff0c;旨在打破传统零售业的边界&#xff0c;引入先进科技和数字化手段&#xff0c;通过整合线上线下渠道&#xff0c;全面提升用户体验&#xff0c;并实现更智能、高效、个性化的零售运营模式。这一模式不仅仅关注销售产品&#xff0c;更注重构建全方位的购物生态…