《深入理解C++11:C++11新特性解析与应用》笔记六
第六章 提高性能及操作硬件的能力
6.1 常量表达式
6.1.1 运行时常量性与编译时常量性
大多数情况下,const描述的是运行时常量性,也即是运行时数据的不可更改性。但有时候我们需要的却是编译时的常量性,这是const关键字无法保证的。例如:
c++11使用constexpr关键字声明常量表达式,不仅限于函数,也可以作用于数据声明,以及类的构造函数。
6.1.2 常量表达式函数
在函数返回类型前加入关键字constexpr来使其称为常量表达式函数,另外还有几点要求:
1.函数体只有单一的return返回语句。
2.函数必须返回值。
3.在使用前必须已有定义。
4.return返回语句表达式中不能使用非常量表达式的函数、全局数据,且必须是一个常量表达式。
多条语句的写法一般无法通过编译,但是一些不会产生实际代码的语句在常量表达式函数中使用,不会引起编译错误,例如static_assert、using、typedef等。
6.1.3 常量表达式的值
由constexpr关键字修饰的变量就是所谓的常量表达式值。常量表达式值必须被一个常量表达式赋值,必须在使用前被初始化。
编译器可以不为常量表达式的值生成数据,仅将其当做编译器的值。
c++11允许编译时的浮点数常量表达式的值,标准要求编译时浮点常量表达式值的精度至少等于或高于运行时的浮点数常量的精度。
c++11标准中,constexpr关键字不能用于修饰自定义类型的定义。正确的做法是定义自定义常量构造函数。
常量表达式的构造函数也有两点约束:
1.函数体必须为空。
2.初始化列表只能由常量表达式来赋值。
虽然声明的是常量表达式构造函数,但编译时的常量性体现在类型上。
PRCfound的成员变量也具有编译时的常量性。
c++11不允许常量表达式作用于virtual的成员函数。
6.1.4 常量表达式的其他应用
常量表达式可以用于模板函数。c++11规定,当声明为常量表达式的模板函数后,当某个该模板函数的实例化结果不满足常量表达式的需求的话,constexpr会被自动忽略。实例化后的函数将成为一个普通函数。
常量表达式可以用于递归函数。例如:
基于模板的编译时期运算的编程方式,通常被称为模板元编程。例如:
并不是使用了constexpr,编译器就一定会在编译时期对常量表达式函数进行值计算。
6.2 变长模板
6.2.1 变长函数和变长的模板参数
c++11支持c99的变长宏。printf则使用了c语言的函数变长参数特征。例子:
变长函数的第一个参数表示的是变长参数的个数。而被调用者中,需要通过一个类型为va_list的数据结构ap来辅助地获得参数。首先使用va_start函数对ap进行初始化,使得ap称为被传递的变长参数的一个句柄。而后代码再使用va_arg函数从ap中将参数一一取出用于运算。
一方面,变长函数本身完全无法知道参数数量或者参数类型。另一方面,c++需要更为现代化的变长参数的实现方式,类型和变量同时能传递给变长参数的函数。一个好的方式就是使用c++的函数模板。c++98中标准要求函数模板始终具有数目确定的模板参数及函数参数。
类也需要不定长度的模板参数。c++98中没有变长模板,tuple能够支持的模板参数数量实际上是由标准库定义了多少个不同参数版本的tuple模板而决定的。c++11中tuple可以接受任意多个不同类型的元素的集合。
6.2.2 变长模板:模板参数包和函数参数包
c++11中的一个tuple变长类模板:template<typename ...Elements> class tuple;其中Elements被称为模板参数包(template parameter pack)。对于实例化的tuple模板类:tuple<int, char, double>,编译器可以将多个模板参数打包为单个的模板参数包Elements,即Elements在进行模板推导的时候,就是一个包含int、char和double三种类型的类型集合。
与普通的模板参数类似,模板参数包也可以是非类型的,比如:
一个模板参数包在模板推导时会被认为是模板的单个参数。为了使用模板参数包,总是需要将其解包,通常通过包扩展(pack expansion)的表达式来完成。
template<typename ...A> class Template:private B<A...>{};
这里的表达式A...就是一个包扩展,参数包会在包扩展的位置上展开为多个参数。比如:
这里类模板B总是接受两个参数的前提下,如果我们声明了一个Template<X,Y,Z>就会发生模板推导的错误。c++11中,实现tuple模板使用了递归的手段,使得模板参数包在实例化时能够层层展开,直到参数包中的参数逐渐耗尽或者到达某个数量的边界为止。
除了变长的模板类,在c++11中还可以声明变长模板的函数。除了声明变长个模板参数的模板参数包,也可以声明成函数参数包,比如:template<typename ...T> void f(T ...args);由于T是个变长模板参数,因此args是对应于这些变长类型数据,即函数参数包。c++11中标准要求函数参数包必须唯一,且是函数的最后一个参数,模板参数包没有这样的要求。
6.2.3 变长模板:进阶
标准定义了以下7种参数包可以展开的位置:
1.表达式
2.初始化列表
3.基类描述列表
4.类成员初始化列表
5.模板参数列表
6.通用属性列表
7.lambda函数的捕捉列表
语言的其他地方则无法展开参数包。在不同位置展开参数包会有不同的效果,例如:
类似情况也发生在函数声明上:
除了扩展外,c++11标准还引入了新的操作sizeof...,作用是计算参数包中的参数个数。
变长模板的参数也可以是模板。变长模板参数也可以用于完美转发。
6.3 原子类型与原子操作
6.3.1 并行变成、多线程与c++11
c++11之前,c/c++程序使用线程主要使用POSIX线程(pthread)和OpenMP编译器指令两种标称模型来完成程序的线程化。
c++11标准引入了多线程的支持。一个最为重要的部分是在原子操作中引入了原子类型的概念。
6.3.2 原子操作与c++11原子类型
原子操作就是多线程程序中最小的且不可并行化的操作。通常情况下原子操作都是通过互斥的访问来保证的。c++11标准之前,需要在c/c++代码中嵌入内联的汇编代码。如果想实现粗粒度的互斥,借助POSIX标准的pthread库中的互斥锁也可以做到:
c++11对并行编程更为良好的抽象,实现相同功能就简单多了:
c++11不需要为原子数据类型显式地声明互斥锁或调用加锁、解锁的API,线程就能够对变量互斥地进行访问。c++11标准定义的是原子类型,而传统面向过程编程语言里使用的是原子操作。
c++11与c11标准都支持原子类型。可以通过#include<cstdatomic>头文件来使用对应于内置类型的原子类型定义。
更为普遍地,可以使用atomic类模板定义任意的原子类型,例如:std::atomic<T> t;而c11则需要使用新关键字_Atomic来完成。
多个线程通常只能访问单个原子类型的拷贝,因此原子类型只能从其模板参数类型中进行构造,不允许原子类型进行拷贝构造、移动构造,以及使用operator=等,以防发生意外。
不过从atomic<T>类型的变量来构造其模板参数类型T的变量则是可以的。比如:
因为atomic类模板总是定义了从atomic<T>到T的类型转换函数。
原子类型能够在线程间保持原子性,主要还是因为编译器能够保证针对原子类型的操作都是原子操作。c++11标准将对原子操作定义为atomic模板类的成员函数,对于内置类型而言,主要是重载一些全局操作符来完成的。
对于大多数原子类型而言,都可以执行读(load),写(store)、交换(exchange)、比较并交换(compare_exchange_weak/compare_exchange_strong)等操作。
atomic_flag是无锁的,
c++11原子操作还可以包含一个参数:memory_order。
6.3.3 内存模型,顺序一致性与memory_order
内存模型通常是一个硬件上的概念,表示的是机器指令是以什么样的顺序被处理器执行的。如果处理器的执行顺序总是按指令顺序,通常称这样的内存模型为强顺序的。反之,则为弱顺序的。如果允许编译器在单个线程中打乱指令的运行顺序,则可能提升并行程序的性能。弱顺序内存模型的平台,如果要保证指令执行顺序,通常要在汇编指令中加入一条所谓的内存栅栏指令。例如PowerPC中的sync指令迫使已经进入流水线中的指令都完成后处理器才执行sync以后的指令。
c++11中的内存模型,要保证代码的顺序一致性,必须同时做到:
1.编译器保证原子操作的指令间顺序不变,即保证产生的读写原子类型的变量的机器指令与代码编写者看到的是一致的。
2.处理器对原子操作的汇编指令的执行顺序不变。对于x86这样的强顺序的体系结构而言,并没有问题;而对于PowerPC这样的弱顺序的体系结构而言,则要求编译器在每次原子操作后加入内存栅栏。
c++11中,原子类型的成员函数总是保证了顺序一致性。对于x86平台来说,禁止了编译器对原子类型变量间的重排序优化;对于PowerPC这样的平台,不仅禁止了编译器优化,还插入了大量的内存栅栏。对于意图提高性能的多线程程序而言,无疑是一种性能伤害。
c++11中,设计者给出的解决方式是让程序员为原子操作指定所谓的内存顺序:memory_order。
(相关使用,未完待续。。。)
6.4 线程局部存储
线程局部存储变量,就是拥有线程生命期及线程可见性的变量。c++11使用thread_local修饰符声明线性局部存储变量。TLS变量的内存的静态/动态分配问题经常被讨论。而c++11标准只对语法做了统一,对其实现并没有做任何性能上的规定。如果想得到最佳的平台上的TLS运行性能,最好还是阅读代码运行平台的相关文档。
6.5 快速退出:quick_exit与at_quick_exit
有关终止的函数:
terminate:terminate函数式c++异常处理的一部分,包含在<exception>头文件里。没有被捕捉的异常就会导致terminate函数的调用。terminate函数在默认情况下,会调用abort函数,不过用户可以通过set_terminate函数来改变默认行为。
abort:abort函数不会调用任何析构函数,默认情况下,它会向合乎POSIX标准的系统抛出一个信号:SIGABRT。如果为信号设定一个信号处理程序的话,操作系统将默认地释放进程所有的资源,从而终止程序。
exit:exit函数会正常调动自动变量的析构函数,还会调用atexit注册的函数,属于正常退出的范畴。
main或者使用exit函数调用结束程序,会导致类的析构函数依次将零散的内存还给操作系统,这是意见费时的工作,实际上,这些内存将在进程结束时由操作系统统一回收。这种情况下,我们往往需要能够更快地退出程序。
在多线程情况下,我们要使用 exit 函数来退出程序的话,通常需要向线程发出一个信号,并等待线程结束后再执行析构函数、atexit 注册的函数等。这样的退出方式有的时候并不能够像预期那样工作,比如说线程中的程序在等待 I/O 行结束等。在一些更为复杂的情况下,可能还会遭遇到一些因为信号顺序而导致的死锁状况。一旦出现了这样的问题,程序往往就会被“卡死”而无法退出。
为此c++11标准引入了quick_exit函数,该函数不执行析构函数而只是使程序终止。quick_exit函数也属于正常退出。使用at_quick_exit注册函数也可以在quick_exit的时候被调用。
相关文章:

《深入理解C++11:C++11新特性解析与应用》笔记六
第六章 提高性能及操作硬件的能力 6.1 常量表达式 6.1.1 运行时常量性与编译时常量性 大多数情况下,const描述的是运行时常量性,也即是运行时数据的不可更改性。但有时候我们需要的却是编译时的常量性,这是const关键字无法保证的。例如&am…...
C# 基于事件的观察者模式
观察者模式是一种软件设计模式,用于定义对象之间的一对多依赖关系,当一个对象的状态发生变化时,它的所有依赖者(观察者)都将得到通知并自动更新。这种模式通过解耦合主题和观察者来提高对象的灵活性。 定义 观察者模式…...

ARM CCA机密计算软件架构之软件堆栈概述
Arm CCA平台通过硬件添加和固件组件的混合方式实现,例如在处理元素(PEs)中的RME以及特定的固件组件,特别是监视器和领域管理监视器。本节介绍Arm CCA平台的软件堆栈。 软件堆栈概述 领域VM的执行旨在与Normal world(正常世界)隔离,领域VM由Normal world Host(正常世界…...

《Python机器学习原理与算法实现》学习笔记
以下为《Python机器学习原理与算法实现》(杨维忠 张甜 著 2023年2月新书 清华大学出版社)的学习笔记。 根据输入数据是否具有“响应变量”信息,机器学习被分为“监督式学习”和“非监督式学习”。 “监督式学习”即输入数据中即有X变量&…...

k8s集群通过helm部署skywalking
1、安装helm 下载脚本安装 ~# curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 ~# chmod 700 get_helm.sh ~# ./get_helm.sh或者下载包进行安装 ~# wget https://get.helm.sh/helm-canary-linux-amd64.tar.gz ~# mv helm …...

介绍一款PDF在线工具
PDF是我们日常工作中的一种常见格式,其处理也是我们工作的重要基础性环节,一款可靠的处理工具显得十分重要。 完全免费、易于使用、丰富的PDF处理工具,包括:合并、拆分、压缩、转换、旋转和解锁PDF文件,以及给PDF文件…...
docker学习——汇总版
历时一个月将docker系统的学习了一下,并且记录了详细的笔记和实践过程。 希望能对工作需要的小伙伴们有所帮助~ docker基础篇 docker学习(一、docker与VM对比) docker学习(二、安装docker) docker学习(…...

百度沧海文件存储CFS推出新一代Namespace架构
随着移动互联网、物联网、AI 计算等技术和市场的迅速发展,数据规模指数级膨胀,对于分布式文件系统作为大规模数据场景的存储底座提出了更高的要求。已有分布式文件系统解决方案存在着短板,只能适应有限的场景: >> 新型分布式…...

16-网络安全框架及模型-BiBa完整性模型
目录 BiBa完整性模型 1 背景概述 2 模型原理 3 主要特性 4 优势和局限性 5 应用场景 BiBa完整性模型 1 背景概述 Biba完整性模型是用于保护数据完整性的模型,它的主要目标是确保数据的准确性和一致性,防止未授权的修改和破坏。在这个模型中&#…...

ssm基于冲突动态监测算法的健身房预约系统的设计与实现论文
摘 要 传统办法管理信息首先需要花费的时间比较多,其次数据出错率比较高,而且对错误的数据进行更改也比较困难,最后,检索数据费事费力。因此,在计算机上安装健身房预约系统软件来发挥其高效地信息处理的作用ÿ…...
基于 Element UI 适用于 Vue 2 版本的虚拟列表选择器组件el-select
背景:在某些使用情况下,单个选择器可能最终加载数万行数据。 将这么多的数据渲染至 DOM 中可能会给浏览器带来负担,从而造成性能问题。 ——vue3element-plus有现成的轮子。而vue2element-ui没有。 以下 文章大部分 摘自 源组件中的README.md…...
java常见面试题:请解释一下Java中的常用分布式框架,如Spring Boot、Dubbo等。
下面我将详细介绍Java中的两个常用分布式框架:Spring Boot和Dubbo。 1. Spring Boot Spring Boot是一个用于创建独立、可运行的、生产级别的Spring应用程序的框架。它简化了Spring应用程序的创建和部署,使得开发人员能够专注于编写业务逻辑,…...

FreeRTOS列表与列表项相关知识总结以及列表项的插入与删除实战
1.列表与列表项概念及结构体介绍 1.1列表项简介 列表相当于链表,列表项相当于节点,FreeRTOS 中的列表是一个双向环形链表 1.2 列表、列表项、迷你列表项结构体 1)列表结构体 typedef struct xLIST { listFIRST_LIST_INTEGRITY_CHECK_VAL…...

07|输出解析:用OutputParser生成鲜花推荐列表
07|输出解析:用OutputParser生成鲜花推荐列表 模型 I/O Pipeline 下面先来看看 LangChain 中的输出解析器究竟是什么,有哪些种类。 LangChain 中的输出解析器 语言模型输出的是文本,这是给人类阅读的。但很多时候,你…...
cfa一级考生复习经验分享系列(十二)
背景:就职于央企金融机构,本科金融背景,一直在传统金融行业工作。工作比较忙,用了45天准备考试,几乎每天在6小时以上。 写在前面的话 先讲一下,整体一级考下来,我觉得知识点多,偏基础…...

【损失函数】SmoothL1Loss 平滑L1损失函数
1、介绍 torch.nn.SmoothL1Loss 是 PyTorch 中的一个损失函数,通常用于回归问题。它是 L1 损失和 L2 损失的结合,旨在减少对异常值的敏感性。 loss_function nn.SmoothL1Loss(reductionmean, beta1.0) 2、参数 size_average (已弃用): 以前用于确定是…...

Go语言中的HTTP重定向
大家好,我是你们可爱的编程小助手,今天我们要一起探讨如何使用Go语言实现HTTP重定向,让我们开始吧! 大家都知道,网站开发中有时候需要将用户的请求从一个URL导向到另一个URL。比如说,你可能想将旧的URL结构…...

ORACLE P6 v23.12 最新虚拟机(VM)全套系统环境分享
引言 根据上周的计划,我简单制作了两套基于ORACLE Primavera P6 最新发布的23.12版本预构建了虚拟机环境,里面包含了全套P6 最新版应用服务 此虚拟机仅用于演示、培训和测试目的。如您在生产环境中使用此虚拟机,请先与Oracle Primavera销售代…...
鸿蒙开发ArkTS基础学习-开发准备工具配置
文章目录 前言1. 准备工作2.开发文档3.鸿蒙开发路径一.详情介绍二.DevEco Studio安装详解-开发环境搭建2.1配置开发环境欢迎各位读者阅读本文,今天我们将介绍鸿蒙(HarmonyOS)应用开发的入门步骤,特别是在准备工作和开发环境搭建方面的重要信息。本文将对鸿蒙官方网站的关键…...

WEB 3D技术 three.js 雾 基础使用讲解
本文 我们说一下 雾 在three.js中有一个 Fog类 它可以创建线性雾的一个效果 她就是模仿现实世界中 雾的一个效果 你看到远处物体会组件模糊 直到完全被雾掩盖 在 three.js 中 有两种雾的形式 一种是线性的 一种是指数的 个人觉得 线性的会看着自然一些 他是 从相机位置开始 雾…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...

佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...

使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...

20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...
提升移动端网页调试效率:WebDebugX 与常见工具组合实践
在日常移动端开发中,网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时,开发者迫切需要一套高效、可靠且跨平台的调试方案。过去,我们或多或少使用过 Chrome DevTools、Remote Debug…...

实战三:开发网页端界面完成黑白视频转为彩色视频
一、需求描述 设计一个简单的视频上色应用,用户可以通过网页界面上传黑白视频,系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观,不需要了解技术细节。 效果图 二、实现思路 总体思路: 用户通过Gradio界面上…...