《深入理解C++11:C++11新特性解析与应用》笔记七

第七章 为改变思考方式而改变

7.1 指针空值--nullptr

7.1.1 指针空值:从0到NULL,再到nullptr

传统C头文件里NULL是一个宏定义:

在函数重载同时出现int和char *参数版本的函数时,使用NULL作为参数调用函数会调用int参数版本,而不是我们想要的char*参数版本。 引起该问的元凶是字面常量0的二义性,既可以是一个整型,也可以是一个无类型指针void*。除非对字面常量0进行强制类型转换b=并调用,否则编译器总是会优先把0看作是一个整型常量。

c++出于兼容性考虑,并没有消除字面常量0的二义性。但是引入了作为指针空值类型的常量nullptr,指针空值类型被命名为nullptr_t。nullptr是关键字,nullptr是有类型的,只能被隐式转化为指针类型,所以nullptr做参数可以成功调用char*版本的函数。

7.1.2 nullptr和nullptr_t

c++11标准不仅定义了指针空值常量nullptr,也定义了其指针空值类型nullptr_t。c++11标准严格规定了数据间的关系:

1.所有定义为nullptr_t类型的数据都是等价的,行为也是完全一致的。

2.nullptr_t类型数据可以隐式转换成任意一个指针类型。

3.nullptr_t类型数据不能转换为非指针类型。

4.nullptr_t类型数据不适用于算术运算表达式。

5.nullptr_t类型数据可以用于关系运算表达式,但仅能与nullptr_t类型数据或者指针类型数据进行比较,当且仅当关系运算符为==、<=、>=等时返回true。

7.1.3 一些关于nullptr规则的讨论

nullptr类型数据所占用的内存空间大小跟void*相同。

nullptr到任何指针的转换是隐式的,而(void*)0则必须经过类型转换后才能使用。

nullptr_t对象的地址可以被用户使用。虽然nullptr也是一个nullptr_t的对象,但它被定义为一个右值常量,因此用户不能获得nullptr的地址。

7.2 默认函数的控制

7.2.1 类与默认函数

在c++中声明自定义的类,编译器会默认生成一些被称为默认函数的成员函数,包括:

构造函数、拷贝构造函数、拷贝赋值函数(operator=)、移动构造函数、移动拷贝函数、析构函数。

c++编译器还会为以下这些自定义类型提供全局默认操作符函数:

operator ,,operator &,operator &&,operator *,operator ->,operator ->*,operator new,operator delete。

一旦实现了这些函数的自定义版本,则编译器不会再为该类自动生成默认版本。声明了带参的构造函数版本,必须声明不带参版本以完成无参的变量初始化。声明了自定义版本的构造函数,有可能导致自定义类型不再是POD的。

C++11中,可以在默认函数定义或者声明时加上"= default"来显式地指示编译器生成该函数的默认版本。另一方面,有时候又希望可以限制一些默认函数的生成,譬如有时候需要禁止使用拷贝构造函数,c++11中,可以在默认函数定义或者声明时加上“= delete”来显式地指示编译器不生成函数的缺省版本。

7.2.2 “= default”与“= deleted”

c++11中,也可以在类定义外显式指定缺省版本,这样可以对一个class定义提供多个实现版本,通过选择性编译,从而在提供缺省函数和自定义版本间切换。

c++11并不要求编译器为“operator ==”之类的函数提供缺省实现,但是将其声明为显式缺省的话,编译器会按照某些标准行为为其生成所需的版本。

显式删除可以避免用户使用一些不应该使用的类的成员函数,也可以避免编译器做一些不必要的隐式数据类型转换,例如:

隐式删除不应该和explicit同用。在下面的例子中:

显式删除导致显式构造的cc变量编译出错,但是Func调用中,编译器会尝试隐式地将char转为int,从而调用一次ConvType(int)构造函数,因而能通过编译。

显式删除并不局限于缺省版本的类成员函数或者全局函数上,对于普通函数,依然可以通过显式删除来禁止类型转换。

7.3 lambda函数

7.3.1 lambda的一些历史

7.3.2 c++11中的lambda函数

lambda函数的语法定义:

捕捉列表由多个捕捉项组成,以逗号分割,有如下形式:

[var]表示值传递方式捕捉变量var,[=]表示值传递方式捕捉所有父作用域的变量,[&var]表示引用传递捕捉变量var。[&]表示引用捕捉所有父作用域的变量。[this]表示值传递方式捕捉当前this指针。还可以进行组合,例如[=,&a,&b]表示引用传递方式捕捉a和b,其他变量以值传递方式捕捉。

默认情况下lambda函数总是一个const函数,mutable可以取消其常量性,使用时参数列表不可省略。

不需要返回值的时候也可以连同符号->一起省略。

c++11标准规定在块作用域以外的lambda函数捕捉列表必须为空,而在块作用域中的lambda函数仅能捕捉父作用域中的自动变量。

7.3.3 lambda与仿函数

在c++11之前,我们在STL中会用到一种特别的对象,称之为函数对象,或者仿函数functor,也就是重定义了成员函数operator()的一种自定义类型对象。在使用它的时候,在代码层面跟函数的使用一样,但本质却是一种对象。仿函数是编译器实现lambda的一种方式。现阶段,通常编译器会把lambda函数转化为一个仿函数对象。

7.3.4 lambda的基础使用

lambda可用于实现局部函数。

7.3.5 关于lambda的一些问题及有趣的实验

如果需要捕捉的值称为lambda函数的常量,通常需要使用按值传递的方式捕捉。反之,需要捕捉的值称为lambda函数运行时的变量,则应采用按引用方式进行捕捉。

lambda的类型并非简单的函数指针或者自定义类型,lambda的类型被定义为闭包的类,每个lambda表达式会产生一个闭包类型的临时对象(右值)。不过c++标准允许lambda表达式向函数指针的转换,前提是函数没有捕捉任何变量,且函数指针所示的函数原型,必须跟lambda函数有这相同的调用方式。

lambda函数的常量性及mutable关键字。现有c++11标准中lambda等价的是有常量成员函数operator()的仿函数,捕捉列表中的变量都会成为等价仿函数的成员变量,而常量成员函数中改变其值是不允许的,因而在按值捕捉的变量在没有声明为mutable的lambda函数中改变其会导致编译器报错。可以通过mutable修饰符消除其常量性,不过更推荐使用引用捕捉。

7.3.6 lambda与STL

当循环次数较多时,内联的lambda函数比函数指针性能好。函数指针应用范围相对狭小,特别是需要具备一些运行时才能决定的状态时,以前或许会使用仿函数,现在则可以选用lambda。

7.3.7 更多的一些关于lambda的讨论

在现有c++11中,lambda不是仿函数的完全替代者,这点很大程度上是由lambda的捕捉列表的限制造成的。仿函数可以被定义以后在不同的作用域范围内取得初始值,这使得仿函数天生具有跨作用域共享的特征。lambda函数被设计的目的,就是要就地书写,就地使用。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PiflowX组件-WriteToUpsertKafka

WriteToUpsertKafka组件 组件说明 以upsert方式往Kafka topic中写数据。 计算引擎 flink 有界性 Streaming Upsert Mode 组件分组 kafka 端口 Inport&#xff1a;默认端口 outport&#xff1a;默认端口 组件属性 名称展示名称默认值允许值是否必填描述例子kafka_h…

Java集合/泛型篇----第六篇

系列文章目录 文章目录 系列文章目录前言一、HashTable(线程安全)二、TreeMap(可排序)三、LinkHashMap(记录插入顺序)四、泛型类前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去…

chrome浏览器记录不住网站登录状态,退出后再打开就需要重新登陆的解决办法

chrome浏览器记录不住网站登录状态&#xff0c;退出后再打开就需要重新登陆&#xff0c;比较繁琐。 解决办法&#xff1a; 1、chrome浏览器右上角三个竖的点&#xff0c;然后进入“设置”&#xff08;Settings&#xff09;&#xff0c;选择“隐私与安全”&#xff08;Privacy…

I.MX8QM flexcan移植

Android SDK&#xff1a;imx8_13.0.0_1.2.0(android 13 u-boot 2022.04 kernel 5.15.74) 一、kernel 内核配置&#xff1a; # 相应的defconfig中添加使能下面两个宏。 # 官方默认的配置可能是以模块的方式编译&#xff0c;这里直接将can驱动编译到内核中 CONFIG_CANy CONFIG…

FingerprintService启动-Android13

FingerprintService启动-Android13 1、指纹服务启动1.1 rc启动Binder对接指纹厂商TA库1.2 FingerprintService启动1.2.1 SystemServer启动FingerprintService1.2.2 注册Binder服务fingerprint 2、获取底层信息2.1 AIDL 对接TA中获取2.2 指纹类型判断 android13-release 1、指纹…

SpringBoot2.7.12整合Knife4j

SpringBoot2.7.12整合Knife4j 是什么 Knife4j是一个集Swagger2 和 OpenAPI3为一体的增强解决方案 添加依赖 <!--引入Knife4j的官方start包,该指南选择Spring Boot版本<3.0,开发者需要注意--> <dependency><groupId>com.github.xiaoymin</groupId>&l…

Intellij建议用String替换StringBuilder

文章目录 前言String 和 StringBuilder 性能对比String 和 StringBuilder 使用的字节码对比总结 本文收发地址 https://blog.csdn.net/CSqingchen/article/details/135324313 最新更新地址 https://gitee.com/chenjim/chenjimblog 前言 最近编码时看到 Intellij 建议使用 Stri…

[附代码]稳态视觉诱发电位SSVEP之预训练模型提高性能

SSVEP 之深度学习 深度学习已经被广泛运用在脑电信号分析来提高脑机接口的性能,这是一个end-to-end的方法,简单来说,只要搭建好深度学习网络,做好特征工程,然后分类即可,对于一个刚刚接触脑机接口领域深度学习的学习者来说,可以先忽略中间的数学相关的东西,先建一个网…

Linux常用命令大全总结及讲解(超详细版)

前言&#xff1a; Linux 是一个基于Linux 内核的开源类Unix 操作系统&#xff0c;Linus Torvalds于 1991 年 9 月 17 日首次发布的操作系统内核。Linux 通常打包为Linux 发行版。 Linux 最初是为基于Intel x86架构的个人计算机开发的&#xff0c;但此后被移植到的平台比任何其…

【大数据面试知识点】分区器Partitioner:HashPartitioner、RangePartitioner

Spark HashParitioner的弊端是什么&#xff1f; HashPartitioner分区的原理很简单&#xff0c;对于给定的key&#xff0c;计算其hashCode&#xff0c;并除于分区的个数取余&#xff0c;如果余数小于0&#xff0c;则用余数分区的个数&#xff0c;最后返回的值就是这个key所属的…

【NLP论文】02 TF-IDF 关键词权值计算

之前写了一篇关于关键词词库构建的文章&#xff0c;没想到反响还不错&#xff0c;最近有空把接下来的两篇补完&#xff0c;也继续使用物流关键词词库举例&#xff0c;本篇文章承接关键词词库构建并以其为基础&#xff0c;将计算各关键词的 TF-IDF 权值&#xff0c;TF-IDF 权值主…

D45D46|动态规划之子序列问题

300.最长递增子序列&#xff1a; 初始思路&#xff1a; 动态规划五部曲&#xff1a; 1&#xff09;dp数组的定义&#xff0c;dp[i]表述数组第i个元素大于前面几个值&#xff1b; 2&#xff09;dp数组的迭代&#xff0c;min nums[x]表示递增数组中的最后一个值&#xff0c;如…