C++奇迹之旅:左右值&&左右值引用&&move应用(文末送书)

C++奇迹之旅:左右值&&左右值引用&&move应用(文末送书)

🎬 鸽芷咕个人主页
 🔥 个人专栏: 《C++干货基地》《粉丝福利》

⛺️生活的理想,就是为了理想的生活!


⛳️ 写在前面参与规则

  • 参与方式:关注博主、点赞收藏评论,任意评论(每人最多评论三次)
  • ⛳️本次送书1~3取决于阅读量,阅读量越多,送的越多
  • 📆 活动时间至:2024-11-23 20:00:00 | 随机抽取由博主动态公布抽奖结果

如有特别需要的同学可添加博主微信进入粉丝福利群发放福利哦!

🔥注:活动结束后,会私信中奖粉丝的,各位注意查看私信哦!

⛳️ 文章末尾扫码加入粉丝群,不定期发放粉丝福利,各种专业书籍免费赠送

文章目录


引入

哈喽各位铁汁们好啊,我是博主鸽芷咕《C++干货基地》是由我的襄阳家乡零食基地有感而发,不知道各位的城市有没有这种实惠又全面的零食基地呢?C++ 本身作为一门篇底层的一种语言,世面的免费课程大多都没有教明白。所以本篇专栏的内容全是干货让大家从底层了解C++,把更多的知识由抽象到简单通俗易懂。

一、 auto

C++98中auto是一个存储类型说明符,表示变量是局部自动存储类型。但是在局部域中定义变量时,默认就是自动存储类型,因此C++98中auto的用法显得并不太有价值。

C++11中,auto不再是存储类型说明符,而是一种实现"自动类型推导"的关键字。也就是说,当你使用auto定义一个变量时,编译器会根据变量的初始化表达式自动推导出该变量的类型。

比如:

auto x =5;// x的类型会被推导为intauto y =3.14;// y的类型会被推导为double

C++11中auto的使用需要满足两个条件:

  1. 必须有显式的初始化表达式,编译器才能根据这个表达式推导出变量的类型。

. 初始化表达式必须能唯一地确定变量的类型,编译器才能进行正确的推导。

在这里插入图片描述

1.1 decltype

在C++11中引入了decltype关键字,它可以用来获取表达式的类型。与auto不同,decltype不会进行类型推导,而是直接采用表达式的类型。

使用decltype的一般形式是:

decltype(expression) variable;

这里,expression是一个合法的表达式,decltype会根据表达式推导出其类型,并将该类型应用到变量的声明中。

比如:

int x =5;decltype(x) y =10;// y的类型是int std::vector<int> v ={1,2,3};decltype(v.begin()) it = v.begin();// it的类型是std::vector<int>::iterator
在这里插入图片描述
关键字decltype将变量的类型声明为表达式指定的类型。
// decltype的一些使用使用场景template<classT1,classT2>voidF(T1 t1, T2 t2){decltype(t1 * t2) ret; cout <<typeid(ret).name()<< endl;}intmain(){constint x =1;double y =2.2;decltype(x * y) ret;//ret的类型是doubledecltype(&x) p;//p的类型是int* cout <<typeid(ret).name()<< endl; cout <<typeid(p).name()<< endl;F(1,'a');return0;}
在这里插入图片描述

1.2 nullptr

由于C++中NULL被定义成字面量0,这样就可能回带来一些问题,因为0既能指针常量,又能表示整形常量。所以出于清晰和安全的角度考虑,C++11中新增了nullptr,用于表示空指针

#ifndefNULL//检查是否已经定义了 `NULL` 宏。如果未定义,则执行下面的代码块#ifdef__cplusplus//检查是否正在使用 C++ 编译器。如果是 C++ 编译器,则执行 `#define NULL 0` 语句。#defineNULL0#else// 如果不是 C++ 编译器,则执行 `#define NULL ((void *)0)` 语句。#defineNULL((void*)0)#endif#endif

这段代码的主要目的是确保 NULL 宏被正确定义,以适应不同的编程环境。
为什么会有两种不同的定义?

在 C 语言中,NULL 通常被定义为 ((void *)0),这是因为 C 语言没有特定的空指针类型。使用 ((void *)0) 可以将 NULL 定义为一个空指针,可以赋值给任何指针类型。

而在 C++ 中,NULL 通常被定义为 0。这是因为 C++ 引入了 nullptr 关键字,代表空指针,因此 NULL 可以被定义为 0

二、STL中一些变化

新容器 :
用橘色圈起来是C++11中的一些几个新容器,但是实际最有用的是unordered_map和
unordered_set。。

在这里插入图片描述


容器中的一些新方法
如果我们再细细去看会发现基本每个容器中都增加了一些C++11的方法,但是其实很多都是用得
比较少的。
比如提供了cbegin和cend方法返回const迭代器等等,但是实际意义不大,因为begin和end也是
可以返回const迭代器的,这些都是属于锦上添花的操作。
实际上C++11更新后,容器中增加的新方法最后用的插入接口函数的右值引用版本:

  1. https://legacy.cplusplus.com/reference/vector/vector/emplace_back/
在这里插入图片描述

https://legacy.cplusplus.com/reference/map/map/emplace/

在这里插入图片描述


但是这些接口到底意义在哪?网上都说他们能提高效率,他们是如何提高效率的?
请看下面的右值引用和移动语义章节的讲解。另外emplace还涉及模板的可变参数,也需要再继续深入学习后面章节的知识。

https://legacy.cplusplus.com/reference/map/map/insert/

在这里插入图片描述

https://legacy.cplusplus.com/reference/vector/vector/push_back/

在这里插入图片描述

三、右值引用和移动语义

3.1 左值引用和右值引用

传统的C++语法中就有引用的语法,而C++11中新增了的右值引用语法特性,所以从现在开始我们
之前学习的引用就叫做左值引用。无论左值引用还是右值引用,都是给对象取别名.

什么是左值?什么是左值引用?

左值是一个表示数据的表达式(如变量名或解引用的指针),**我们可以获取它的地址+可以对它赋
值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。**定义时const修饰符后的左
值,不能给他赋值,但是可以取它的地址。左值引用就是给左值的引用,给左值取别名。

//以下的p , b ,c, *p都是左值//左值可以取地址int* p =newint(0);int b =1;constint c = b; string s("1111111"); s[0]; cout <<&c << endl; cout <<&s[0]<< endl;
在这里插入图片描述


什么是右值?什么是右值引用?
右值也是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值(这个不能是左值引
用返回)等等,**右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能
取地址。**右值引用就是对右值的引用,给右值取别名。

//右值:不能取地址double x =1.1, y =2.2;//以下几个都是常见的右值,敞亮临时对象,匿名对象10; x + y;fmin(x, y);string("111111"); cout <<&10<< endl; cout <<&(x+y)<< endl; cout <<&(fmin(x, y)<< endl; cout <<&string("1111")<< endl;// 这里编译会报错:error C2106: “=”: 左操作数必须为左值10=1; x + y =1;fmin(x, y)=1;
在这里插入图片描述


注意:的是右值是不能取地址的,但是给右值取别名后,会导致右值被存储到特定位置,且可
以取到该位置的地址,也就是说例如:不能取字面量10的地址,但是rr1引用后,可以对rr1取地
址,也可以修改rr1。如果不想rr1被修改,可以用const int&& rr1 去引用,是不是感觉很神奇,
这个了解一下实际中右值引用的使用场景并不在于此,这个特性也不重要。

intmain(){double x =1.1, y =2.2;int&& rr1 =10;constdouble&& rr2 = x + y; rr1 =20; rr2 =5.5;// 报错return0;}

3.2 左值引用与右值引用比较

左值引用总结:

  1. 左值引用只能引用左值,不能引用右值。
  2. 但是const左值引用既可引用左值,也可引用右值。
intmain(){// 左值引用只能引用左值,不能引用右值。int a =10;int& ra1 = a;// ra为a的别名//int& ra2 = 10; // 编译失败,因为10是右值// const左值引用既可引用左值,也可引用右值。constint& ra3 =10;constint& ra4 = a;return0;}

右值引用总结:

  1. 右值引用只能是右值,不能引用左值。
  2. 但是右值引用可以move以后的左值。
intmain(){// 右值引用只能右值,不能引用左值。int&& r1 =10;// error C2440: “初始化”: 无法从“int”转换为“int &&”// message : 无法将左值绑定到右值引用int a =10;//int&& r2 = a;//错误// 右值引用可以引用move以后的左值int&& r3 = std::move(a);return0;}

3.3 move

在这里插入图片描述


好的,让我们来详细解释一下 std::move 这个C++11标准库函数。

std::move 的作用是将一个左值转换为一个右值引用。它的定义如下:

template<classT>typenamestd::remove_reference<T>::type&&move(T&& arg)noexcept;1. `template<classT>`: 这是一个函数模板,可以接受任意类型的参数。 2. `typenamestd::remove_reference<T>::type&&`: 这是函数的返回类型。它使用 `std::remove_reference` 去除参数 `T` 的引用属性,然后返回一个右值引用。 3. `T&& arg`: 这是函数的参数。它使用了"完美转发"的技术,可以接受任意类型的参数,无论是左值还是右值。 4. `noexcept`: 这个关键字表示该函数不会抛出任何异常。 

那么 std::move 具体是怎么工作的呢?

  1. 当你传递一个左值给 std::move 时,它会将这个左值转换为一个右值引用。这样可以让你"窃取"这个对象的资源,而不是复制它。
  2. 当你传递一个右值给 std::move 时,它会直接返回这个右值引用,不会进行任何转换。

举个例子:

假设我们有一个 std::string 类型的对象:

std::string str ="Hello, world!";

在这里,str 是一个左值。如果我们想将 str 的内容移动到另一个 std::string 对象中,而不是进行复制,我们可以使用 std::move 来实现:

std::string str2 = std::move(str);

在这个例子中:

  1. std::move(str)str 这个左值转换为一个右值引用。
  2. std::string str2 = std::move(str); 将这个右值引用绑定到新的 str2 对象上。
  3. 这样做之后,str 对象就被"窃取"了,它的内容已经转移到了 str2 上。str 现在处于一种"已移动"的状态,不再拥有任何资源。

这个过程中,我们没有进行任何复制操作,只是简单地转移了对象的所有权。这种移动语义可以大大提高程序的性能,因为它避免了不必要的内存拷贝。

在这里插入图片描述


**另一个例子:**是在函数参数传递中使用 std::move:

voidprocessString(std::string&& s){// 在这里使用 s 对象,并可能会移动它的内容} std::string str ="Hello";processString(std::move(str));

在这个例子中:

  1. processString(std::move(str))str 这个左值转换为一个右值引用,然后传递给 processString 函数。
  2. processString 内部,s 参数就是一个右值引用,我们可以对它进行移动操作,而不是复制操作。

3.4 右值引用临时对象

在这段代码中,int&& rr1 = x + 10;看起来似乎违背了右值引用只能引用右值的规则,这是为什么呢?

// 底层汇编等实现和上层语法表达的意义,有时是背离的,所以不要结合到一起去理解,互相佐证intmain(){int x =0;int& r1 = x;int&& rr1 = x +10;return0;}
在这里插入图片描述

这里的关键在于 C++11 引入了两种不同的引用类型:

  1. 左值引用(int& r1 = x;): r1只能引用左值。
  2. 右值引用(int&& rr1 = x + 10;): rr1可以引用右值。

C++11 也引入了一个特殊情况,称为==“萃取临时对象”==。当表达式x + 10的结果是一个临时对象时,它会被视为一个右值,因此可以使用右值引用来引用它。


四、精品图书推荐《你好,C++》

在这里插入图片描述

4.1 本书内容

《你好,C++》主要介绍C++开发环境的搭建、基础语法知识、面向对象编程思想以及标准模板库的应用,特别针对初学者在学习C++过程中可能遇到的难点提供了解决方案。全书共分13章,以一个工资程序的不断优化和完善为线索,展示了如何利用C++进行设计和解决实际问题。《你好,C++》遵循最新的C++23标准,更新了所有代码和讲解,并增加了新标准引入的特性,如智能指针、std::thread等,使读者能够掌握最新的C++知识。每章末尾都配有“本章练习”,帮助读者通过实际编写代码来加深对知识的理解。

4.2 本书作者

陈良乔,程序员、自由撰稿人,毕业于西安交通大学。敏而好学,乐于分享,连续六年获得微软最有价值专家(MVP)称号。因撰写一系列Visual Studio 2010技术文章和Windows 7开发技术文章,2010年获“MSDN原创之星”称号。曾组织翻译Bjane Stroustrup的《C++11 FAQ》。著有《我的第一本C++书》和《C语言程序伴侣》。

4.3本书适合读者

《你好,C++》语言风格轻松幽默,还收录了一些公司的典型笔试题,无论是C++初学者还是中级程序员提升技能,都是极佳的选择。同时,《你好,C++》也适合作为应用型本科及高职高专相关专业的教材使用。

购买链接

图书购买链接:https://item.jd.com/10118841110121.html

在这里插入图片描述

粉丝福利&参与规则

  • 参与方式:关注博主、点赞收藏评论,任意评论(每人最多评论三次)
  • ⛳️本次送书1~3取决于阅读量,阅读量越多,送的越多
  • 📆 活动时间至:2024-11-25 20:00:00 | 随机抽取由博主动态公布抽奖结果

如有特别需要的同学可添加博主微信进入粉丝福利群发放福利哦!

🔥注:活动结束后,会私信中奖粉丝的,各位注意查看私信哦!

在这里插入图片描述

Read more

Flutter 三方库 flutter_adaptive_scaffold 的鸿蒙化适配指南 - 掌握一套代码适配全场景终端的自适应架构技术、助力鸿蒙应用构建从手机到平板及折叠屏的极致无缝交互体系

Flutter 三方库 flutter_adaptive_scaffold 的鸿蒙化适配指南 - 掌握一套代码适配全场景终端的自适应架构技术、助力鸿蒙应用构建从手机到平板及折叠屏的极致无缝交互体系

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 flutter_adaptive_scaffold 的鸿蒙化适配指南 - 掌握一套代码适配全场景终端的自适应架构技术、助力鸿蒙应用构建从手机到平板及折叠屏的极致无缝交互体系 前言 在 OpenHarmony 鸿蒙应用追求“万物互联、全场景覆盖”的伟大进程中,屏幕尺寸的多样性(从 6 英寸手机到 12 英寸平板,再到 2D/3D 模式切换的折叠屏)是每一位 UI 开发者必须正面迎接的挑战。如何在不为每种设备重写 UI 的前提下,实现导航栏自动从“底部”平滑流转到“侧边”?如何在宽屏模式下自动开启“双栏(Master-Detail)”布局?flutter_adaptive_scaffold 作为一个由 Flutter

By Ne0inhk
在 macOS 上通过 Docker 本地安装 OpenClaw 完整教程

在 macOS 上通过 Docker 本地安装 OpenClaw 完整教程

在 macOS 上通过 Docker 本地安装 OpenClaw 完整教程 什么是 OpenClaw?—— 你的本地 AI 智能体执行框架 OpenClaw 不仅仅是一个聊天机器人,而是一个功能强大的 AI 智能体执行框架。你可以把它想象成一个能自主思考、调用工具、并替你完成复杂任务的数字员工。 🧠 核心概念 * 智能体:OpenClaw 的核心大脑。它能理解你的自然语言指令,拆解任务,并决定调用哪些工具来执行。 * 网关:所有外部访问的入口。它负责处理 WebSocket 连接、管理设备配对、路由消息,是你与智能体交互的桥梁。 * 技能:智能体可调用的具体工具,比如访问文件、操作浏览器、发送消息、查询数据库等。你可以根据需要扩展技能库。 * 记忆:OpenClaw 可以存储对话历史和重要信息,实现长期记忆和上下文理解,让交互更连贯。 * 通道:连接外部聊天平台的渠道,如

By Ne0inhk
HarmonyOS6半年磨一剑 - RcIcon组件实战案例集与应用开发指南

HarmonyOS6半年磨一剑 - RcIcon组件实战案例集与应用开发指南

文章目录 * 前言 * 项目简介 * 核心特性 * 开源计划 * rchoui官网 * 文档概述 * 第一章: 基础用法实战 * 1.1 三种符号引用方式 * 1.2 应用场景 - 工具栏快速导航 * 第二章: 尺寸系统实战 * 2.1 响应式尺寸配置 * 2.2 应用场景 - 统一设计系统尺寸规范 * 第三章: 颜色系统实战 * 3.1 多彩色系配置 * 3.2 应用场景 - 状态指示系统 * 第四章: 双风格系统实战 * 4.1 线型与实底风格对比 * 4.2 应用场景 - 底部导航栏 * 第五章: 圆角系统实战 * 5.

By Ne0inhk
Flutter 组件 short_uuids 适配鸿蒙 HarmonyOS 实战:唯一标识微缩技术,构建高性能短 ID 生成与分布式索引架构

Flutter 组件 short_uuids 适配鸿蒙 HarmonyOS 实战:唯一标识微缩技术,构建高性能短 ID 生成与分布式索引架构

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 short_uuids 适配鸿蒙 HarmonyOS 实战:唯一标识微缩技术,构建高性能短 ID 生成与分布式索引架构 前言 在鸿蒙(OpenHarmony)生态迈向万物互联、涉及海量离线资源标识、蓝牙广播载荷(BLE Payload)及二维码数据极限压缩的背景下,如何生成既能保留 UUID 强随机性、又能极大缩减字符长度的唯一标识符,已成为优化存储与通讯效率的“空间必修课”。在鸿蒙设备这类强调分布式软总线传输与每一字节功耗敏感的环境下,如果应用依然直接传输长度达 36 字符的标准 UUID,由于由于有效载荷溢出,极易由于由于传输协议限制导致数据截断或多次分包带来的延迟。 我们需要一种能够实现高进制转换、支持双向编解码且具备低碰撞概率的短 ID 生成方案。 short_uuids 为 Flutter 开发者引入了将标准 UUID 转化为短格式字符串的高性能算法。它利用

By Ne0inhk