【C++11】C++11新纪元:深入探索右值引用与移动语义

news2024/9/19 11:11:04

📝个人主页🌹:Eternity._
⏩收录专栏⏪:C++ “ 登神长阶 ”
🤡往期回顾🤡:位图与布隆过滤器
🌹🌹期待您的关注 🌹🌹

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

❀C++11

  • 📒1. C++11简介
  • 📜2. 统一的列表初始化
    • 🌸{ }初始化
    • 🌺initializer_list
  • 📚3. decltype与新容器 array
    • 🎩decltype
    • 🎈新容器 array
  • 📝4. 右值引用和移动语义
    • ⛰️左值引用和右值引用
    • 🌄左值引用与右值引用比较
    • 🌞右值引用使用场景和意义
    • ⭐完美转发
  • 📙5. 新的类功能
  • 📖6. 总结


前言:在C++的悠久历史中,每一次标准的更新都如同为这门强大的编程语言注入了新的活力。C++11,作为这一进程中的一个重要里程碑,不仅带来了众多新特性,还深刻改变了C++编程的范式,其中右值引用(Rvalue References)无疑是最为引人注目的特性之一

在传统的C++编程中,我们习惯于通过左值(Lvalues)来引用和操作对象,这些左值通常指向具有持久身份的对象。然而,随着C++应用的日益复杂和对性能要求的不断提高,如何高效地处理临时对象(即右值,Rvalues)成为了亟待解决的问题。C++11引入的右值引用,正是为了填补这一空白,它允许我们直接引用即将被销毁的临时对象,从而开启了C++编程的新纪元

本篇将带您深入探索C++11中的右值引用及其相关特性,包括移动语义(MoveSemantics)、完美转发(Perfect Forwarding)等。我们将从基础概念讲起,逐步深入到实际应用和最佳实践,旨在帮助您全面理解并掌握这一强大的编程工具

让我们一起踏上学习的旅程,探索它带来的无尽可能!


📒1. C++11简介

C++11是C++编程语言的一个重大更新版本,也被称为C++标准第三版,正式名称为ISO/IEC 14882:2011 - Information technology – Programming languages – C++

相比于C++98/03,C++11则带来了数量可观的变化,其中包含了约140个新特性,以及对C++03标准中约600个缺陷的修正,这使得C++11更像是从C++98/03中孕育出的一种新语言。相比较而言,C++11能更好地用于系统开发和库开发、语法更加泛华和简单化、更加稳定和安全,不仅功能更强大,而且能提升程序员的开发效率,公司实际项目开发中也用得比较多,所以我们要作为一个重点去学习

C++11标准的发布对C++编程产生了深远的影响,推动了C++语言的现代化和性能提升。随着各大主流编译器(如GCC、Clang、MSVC等)对C++11语法的支持逐渐完善,越来越多的项目开始采用C++11标准进行开发。同时,相关的技术书籍和教程也相继更新,以支持C++11的新特性

总之,C++11是C++编程语言发展历程中的一个重要里程碑,它带来了众多新特性和改进,为C++程序员提供了更加强大和灵活的工具来编写高效、可维护的代码

C++11介绍


📜2. 统一的列表初始化

在C++11及以后的版本中,引入了统一的列表初始化(Uniform Initialization)或称为初始化列表(Initialization List),这是一种新的初始化语法,使用大括号{}来初始化变量。统一的列表初始化不仅提高了代码的一致性和可读性,还解决了之前初始化语法中的一些歧义和限制


🌸{ }初始化

在C++98中,标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定

代码示例 (C++):

// C++98
struct Pxt
{
	int _x;
	int _y;
};
int main()
{
	Pxt p = { 1, 2 };
	return 0;
}

C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用初始化列表时,可添加等号(=),也可不添加

代码示例 (C++):

// C++11
struct Pxt
{
	int _x;
	int _y;
};
int main()
{
	int a = 1;
	int b{ 2 };
	Pxt p{ 1, 2 };
	
	// C++11中列表初始化也可以适用于new表达式中
	int* pa = new int[4]{ 0 };
	return 0;
}

我们的自定义创建的对象也可以使用列表初始化方式调用构造函数初始化

代码示例 (C++):

class Date
{
public:
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
	{
		cout << "Date(int year, int month, int day)" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2024, 7, 28); // old style
	
	// C++11支持的列表初始化,这里会调用构造函数初始化
	Date d2{ 2024, 7, 29 };
	Date d3 = { 2024, 7, 30 };

	// 这里的 vector 和上面的 Date 不太一样
	vector<int> v = { 1,2,3,4,5 };

	return 0;
}

列表初始化时,必须要跟对应的构造函数参数个数匹配,Data中只能有三个参数,但是vector的参数可以有很多个,列表初始化也支持隐式类型转换

vector<Data> vd = { {2024, 7, 28},{2024, 7, 29},{2024, 7, 30} };

🌺initializer_list

initializer_list 是 C++11 引入的一个特性,它提供了一种方式来初始化容器类对象或函数参数列表,使得可以使用花括号 { } 来直接初始化对象或传递参数。initializer_list 是一个轻量级的模板类,它用于表示一个给定类型的值的数组,但大小是固定的,且生命周期与包含它的对象相同

initializer_list介绍文档

代码示例 (C++):

int main()
{
	auto it = { 1 ,2 };
	// initializer_list
	cout << typeid(it).name() << endl;
	return 0;
}

在这里插入图片描述


在这里插入图片描述
在这里插入图片描述

注意:initializer_list的迭代器就是原生指针


std::initializer_list一般是作为构造函数的参数,C++11对STL中的不少容器就增加 std::initializer_list作为参数的构造函数,这样初始化容器对象就更方便了。也可以作为operator=的参数,这样就可以用大括号赋值

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

我们当初在模拟实现这些STL容器时,并没有实现initializer_list,今天我们以vector为例子,实现一下initializer_list的构造

代码示例 (C++):

vector(initializer_list<T> lt)
{
	reverse(lt.size());
	for(auto& e : lt)
	{
		push_back(e);
	}
}

📚3. decltype与新容器 array

🎩decltype

decltype 是 C++11 引入的一个关键字,它作为操作符用于查询表达式的数据类型。这个操作符主要用于泛型编程中,特别是在模板编程中,当需要推导表达式的类型但又不想实际执行该表达式时,decltype 显得尤为有用

代码示例 (C++):

int main()
{
	const int x = 1;
	double y = 2.2;
	
	decltype(x * y) ret; // ret的类型是double
	decltype(&x) p; // p的类型是int*
	
	cout << typeid(ret).name() << endl;
	cout << typeid(p).name() << endl;

	return 0;
}

关键字decltype将变量的类型声明为表达式指定的类型


🎈新容器 array

在C++中,std::array 是一个固定大小的容器,它提供了类似于数组的接口,但它是标准库的一部分,因此提供了更多的安全性和灵活性。std::array 定义在头文件 <array> 中,是一个模板类,可以存储任何类型的固定数量元素

在这里插入图片描述

array<int, 10> a; // a[10]

vector<int> v(10, 0) 
// 因为有vector的存在,让array的出现有点“小丑”,而且vector 似乎比array好用

因为有vector的存在,让array的出现有点“小丑”,而且vector 似乎比array好用,这里我就不细说了,想了解的朋友可以了解一下

array介绍文档


📝4. 右值引用和移动语义

右值引用

  • 在C++中,表达式根据它们是否可以被修改分为左值(lvalue)和右值(rvalue)。左值是可以被取地址的表达式,通常对应于具有持久状态的实体(如变量)。而右值则是不可以被取地址的临时对象或字面值,它们通常表示计算的结果或函数返回的临时对象。
  • 右值引用是C++11引入的一种新类型的引用,它通过类型后加&&来表示。右值引用可以绑定到右值上,但也可以绑定到左值上(需要std::move来显式转换)。右值引用的主要目的是允许函数或操作以“移动”而不是“复制”的方式处理资源,这通常意味着资源的所有权从源对象转移到目标对象,源对象则变为一个安全可销毁的状态。

移动语义

移动语义允许对象通过转移其资源(如动态分配的内存)而不是复制它们来初始化或赋值另一个对象。这通常是通过一个特殊的成员函数——移动构造函数和移动赋值操作符来实现的。这两个函数都接受右值引用作为参数,表示它们可以从一个即将被销毁的对象中“窃取”资源。

  • 移动构造函数: 接受一个右值引用参数,用于初始化新对象,通过转移源对象的资源而不是复制它们,从而避免不必要的资源分配和复制。
  • 移动赋值操作符: 同样接受一个右值引用参数,用于将一个对象的资源转移到另一个已经存在的对象上,并将源对象置于一个可析构的状态。

⛰️左值引用和右值引用

左值引用

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

代码示例 (C++):

int main()
{
	// p、b、c、*p都是左值
	int* p = new int(0);
	int b = 1;
	const int c = 2;

	// 对上面左值的左值引用
	int*& rp = p;
	int& rb = b;
	const int& rc = c;
	int& pvalue = *p;

	return 0;
}

右值引用

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

代码示例 (C++):

int main()
{
	double x = 1.1, y = 2.2;
	
	// 常见的右值
	10;
	x + y;
	fmin(x, y);
	
	// 对右值的右值引用
	int&& rr1 = 10;
	double&& rr2 = x + y;
	double&& rr3 = fmin(x, y);

	return 0;
}

左操作数必须为左值,否则就会报错

int main()
{
	// 这里编译会报错:error C2106: “=”: 左操作数必须为左值
	10 = 1;
	x + y = 1;
	fmin(x, y) = 1;
}

注意:

  • 不能用一个值能不能修改来区分左右值,右值是不能取地址的
  • 给右值取别名后,会导致右值被存储到特定位置,且可以取到该位置的地址

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

左值引用:

  • 左值引用只能引用左值,不能引用右值
  • 但是const左值引用既可引用左值,也可引用右值

代码示例 (C++):

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

	return 0;
}

右值引用:

  • 右值引用只能右值,不能引用左值
  • 但是右值引用可以move以后的左值

代码示例 (C++):

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

move

当你对一个对象使用move时,你实际上是在告诉编译器:“这个对象我之后可能不再需要了,或者我可以接受它处于某种未定义状态,所以你可以安全地‘窃取’它的资源。”,从而变成将亡值,然后,编译器会寻找接收该对象的函数是否支持移动语义(即是否有一个接受右值引用的构造函数或赋值运算符)

在这里插入图片描述

代码示例 (C++):

int main()
{
	string s1("hello world");
	string s2 = s1;
	string s3 = move(s1);
	return 0;
}

在这里插入图片描述
直接将s1中的资源“移动”到了s3中


🌞右值引用使用场景和意义

右值引用让我们能够在一些函数中直接使用右值,而不用开空间而节省资源,而在STL的很多容器中也够重载了右值引用

在这里插入图片描述
在这里插入图片描述

右值引用的使用可以让很多场景得到更高的效率来实现功能

移动构造,移动赋值代码示例 (string为例):

// 移动构造
string(string&& s)
	:_str(nullptr)
	,_size(0)
	,_capacity(0)
{
	cout << "string(string&& s) -- 移动语义" << endl;
	swap(s);
}

// 移动赋值
string& operator=(string&& s)
{
	cout << "string& operator=(string&& s) -- 移动语义" << endl;
	swap(s);
	return *this;
}

左值引用做参数可以减少拷贝,提高效率的使用场景和价值,但是当函数返回对象是一个局部变量,出了函数作用域就不存在了,就不能使用左值引用返回,只能传值返回

代码示例 :

string to_string(int x)
{
	string ret;
	while (x)
	{
		int val = x % 10;
		x /= 10;
		ret += ('0' + val);
	}
	reverse(ret.begin(), ret.end());

	return ret;
}

int main()
{
	string ret = to_string(1234);
	return 0;
}

在这里插入图片描述

  • to_string的返回值是一个右值,用这个右值构造ret2,如果没有移动构造,调用就会匹配调用拷贝构造,因为const左值引用是可以引用右值的,这里就是一个深拷贝
  • 移动构造本质是将参数右值的资源窃取过来,占位已有,那么就不用做深拷贝了,所以它叫做移动构造,就是窃取别人的资源来构造自己,而移动构造中没有新开空间,拷贝数据,所以效率就提高了
  • 如果既有拷贝构造又有移动构造调用就会匹配调用移动构造,因为编译器会选择最匹配的参数调用。那么这里就是一个移动语义

在这里插入图片描述


有些场景下,可能真的需要用右值去引用左值实现移动语义。当需要用右值引用引用一个左值时,可以通过move函数将左值转化为右值。该函数名字具有迷惑性,它并不搬移任何东西,唯一的功能就是将一个左值强制转化为右值引用,然后实现移动语义

代码示例 :

void push_back (value_type&& val);

int main()
{
	list<pxt::string> lt;
	pxt::string s1("1111");

	// 这里调用的是拷贝构造
	lt.push_back(s1);
	
	// 下面调用都是移动构造
	lt.push_back("2222");
	lt.push_back(std::move(s1));
	
	return 0;
}

在这里插入图片描述


⭐完美转发

“完美转发”(Perfect Forwarding)是C++11及以后版本中引入的一个特性,它允许函数模板以完全相同的类型(包括const限定符和引用类型)转发其参数到另一个函数或模板。这通常通过模板和std::forward函数实现


模板中的&& 万能引用

我们写代码测试一下,如果是右值引用就调用函数打印右值引用,如果是左值引用就调用函数打印左值引用

代码示例 :

void Fun(int& x) { cout << "左值引用" << endl; }
void Fun(const int& x) { cout << "const 左值引用" << endl; }
void Fun(int&& x) { cout << "右值引用" << endl; }
void Fun(const int&& x) { cout << "const 右值引用" << endl; }

template<typename T>
void PerfectForward(T&& t)
{
	Fun(t);
}
int main()
{
	PerfectForward(10); // 右值
	int a;
	PerfectForward(a); // 左值
	PerfectForward(std::move(a)); // 右值
	const int b = 8;
	PerfectForward(b); // const 左值
	PerfectForward(std::move(b)); // const 右值
	
	return 0;
}

神奇的一幕发生了,我们运行发现结果全是左值引用
在这里插入图片描述

我们有以下结论:

  • 模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值
  • 模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力
  • 但是引用类型的唯一作用就是限制了接收的类型,后续使用中都退化成了左值

那我们如何能够在传递过程中保持它的左值或者右值的属性, 就需要用我们用到完美转发


完美转发

forward 它允许函数模板将参数转发到另一个函数时,保持其值类别(左值或右值)不变。这是通过模板的隐式类型转换和引用折叠规则实现的,完美转发在传参的过程中保留对象原生类型属性

template<typename T>
void PerfectForward(T&& t)
{
	Fun(forward<T>(t));
}

在这里插入图片描述


📙5. 新的类功能

C++11在原来的基础上新增了两个默认成员函数:移动构造函数和移动赋值运算符重载

关于这两个函数需要注意:

  • 如果你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任
    意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类
    型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,
    如果实现了就调用移动构造,没有实现就调用拷贝构造。
  • 如果你没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中
    的任意一个,那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数,对于内
    置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋
    值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造
    完全类似)
  • 如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值

禁止生成默认函数的关键字delete

如果能想要限制某些默认函数的生成,在C++98中,是该函数设置成private,并且只声明补丁
已,这样只要其他人想要调用就会报错。在C++11中更简单,只需在该函数声明加上=delete即
可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数

class Person
{
public:
	Person(const char* name = "", int age = 0)
		:_name(name)
		, _age(age)
	{}
	Person(const Person& p) = delete;
	
private:
	string _name;
	int _age;
};

📖6. 总结

在探索C++11的广阔特性时,右值引用无疑是一个令人兴奋且意义深远的新特性。它不仅为C++带来了移动语义和完美转发的能力,还极大地增强了C++代码的性能和灵活性。通过深入学习和实践右值引用,我们学会了如何更有效地管理资源,减少了不必要的拷贝操作,从而提高了程序的运行效率

在学习过程中,我们见证了右值引用如何与移动构造函数、移动赋值操作符以及std::move函数等配合使用,共同构建起了一套完整的移动语义体系。这套体系不仅优化了STL容器的性能,还为我们编写高性能的C++代码提供了强有力的支持

随着C++标准的不断演进,我们期待看到更多基于右值引用的新特性和优化,C++11的内容我们还没有完全了解,愿我们都能保持好奇心和求知欲,不断探索C++的无限可能,我们下期见!

在这里插入图片描述

希望本文能够为你提供有益的参考和启示,让我们一起在编程的道路上不断前行!
谢谢大家支持本篇到这里就结束了,祝大家天天开心!

在这里插入图片描述

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

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

相关文章

sentinel的使用以及springcloud整合sentinel

一、为什么要用到sentinel 首先我们要知道的是一个微服务项目如果一个服务挂载掉了&#xff0c;会出现什么情况&#xff0c;是不是回出现一个服务挂载而另一个服务还需要一直调用此服务就很容易导致和它有关联的服务不能被访问到&#xff0c;这也就是我们常常在生活中说到的雪崩…

关于 NASA 航空公司 cmapss 数据集剩余寿命(Rul)预测数据预处理的疑问。

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

微服务--熟练掌握网关(包括权限认证)

目录 一、网关的路由 二、网关登录的校验 2.1.鉴权思路分析 2.2.网关过滤器 2.3 自定义过滤器 2.3.1 自定义GatewayFilter 2.3.2 自定义GlobalFilter 2.4 登陆校验 具体的实现步骤&#xff1a; 一、在微服务中&#xff0c;我们在微服务的网关模块&#xff0c;就要进行…

K210视觉识别模块学习笔记8:Mx_yolo3本地模型训练环境搭建_部署模型到亚博canmv(失败)

今日开始学习K210视觉识别模块: 本地模型训练环境搭建 亚博智能 K210视觉识别模块...... 固件库: canmv_yahboom_v2.1.1.bin 本地训练 Mx_yolo3 这里就简单地提示一下下载安装哪些软件&#xff0c;然后主要是使用Mx_yolo3 进行本地训练模型的...... 本文不…

子网划分+汇总

子网划分的意义&#xff1a;为了使得ip地址充分得到使用&#xff0c;减少浪费 我们知道IP地址分为ABCDE五类&#xff08;这里不讨论DE类&#xff09; IP地址总共有32个字节&#xff0c;分成四个八个字节的二进制数 例如128.64.1.1 10000000.01000000.00000001.00000001 0…

1、AI测试辅助-提示词优化

AI测试辅助-提示词优化 一、基本规则二、提示词优化技巧&#xff1a;2.1 Prompt 逆向工程2.2 提示词框架2.2.1 CO-STAR 框架 2.3 提示词生成器 三、总结 一、基本规则 写提示词有个通用的基本规则&#xff0c;遵循这个规则基本上能解决大部分的问答&#xff1a; 角色任务要求 …

视频逐帧播放查看神器-android闪黑闪白等分析辅助工具

背景 刚好有学员朋友在群里问道有没有什么播放软件可以实现对视频的逐帧即一帧一帧播放。在做android系统开发时候经常会偶尔遇到有时候是闪黑&#xff0c;闪白等一瞬间现象的问题。这类问题要分析的话就不得不需要对设备录屏&#xff0c;然后对录屏进行逐帧播放查看现象&…

Python层内层外多图布局图基分析

&#x1f3af;要点 &#x1f3af;多层图和多路复用图结构模型 | &#x1f3af;图结构变换、读写图、聚类系数、可视化、同构、图基分析 | &#x1f3af;稀疏网络边数和节点数线性扩展 | &#x1f3af;耦合边的生成和惰性评估 | &#x1f3af;层内布局计算、多层网络绘图、层间…

初识C++: string类【标准库里的string】【string类的模拟实现】

关于string类&#xff0c;可以先看一下这个文档string文档。 一.标准库里的string 1.1auto关键字 &#xff08;1&#xff09; 在早期C/C中auto的含义是&#xff1a;使用auto修饰的变量&#xff0c;是具有自动存储器的局部变量&#xff0c;后来这个 不重要了。C11中&#xff…

Linux基础三

拼搏何惧路途难&#xff0c; 汗水淋漓志未残。 砥砺前行心不改&#xff0c; 终能登顶笑开颜。 目录 一&#xff0c;vi编辑器 1&#xff0c;命令模式 2&#xff0c;末行模式 3&#xff0c;编辑模式 二&#xff0c;Linux进程启动的几种方式 2.1前台启动进程 2.2后台启动进程 三&a…

Git 创建分支进行写作开发

第一次链接仓库提交 and 有SSH公匙 第一步&#xff1a; git clone 远程仓库链接clone 远程已有仓库到本地 第二部&#xff1a; cd 文件夹 第三步&#xff1a; git checkout -b <your_branch_name> 创建分支 第四步&#xff1a; git add . 将目前目录下的所有文件…

加密micropython写的程序为.mpy的方法

2024年7月26日 用虚拟机安装一个Linux&#xff0c;本例为CentOS7的Linux系统。 1.保证Linux能够连接网络。 2.进入root用户&#xff0c;使用下面的命令行安装gcc编译器&#xff1a; yum install gcc 3.安装完成后&#xff0c;查看gcc是否安装成功&#xff0c;用下面的命令…

Java从入门到精通(十三)~ 枚举和注解

晚上好&#xff0c;愿这深深的夜色给你带来安宁&#xff0c;让温馨的夜晚抚平你一天的疲惫&#xff0c;美好的梦想在这个寂静的夜晚悄悄成长。 文章目录 目录 前言 一、枚举 1.1 枚举的概念 ​编辑 1.2 枚举的特点 1.3 枚举的实际运用 1. 状态机&#xff0c;描述属性的…

App Inventor 2 低功耗蓝牙 BlueToothLE 拓展中文文档(完整翻译加强版)

低功耗蓝牙&#xff0c;也称为蓝牙LE 或简称 BLE&#xff0c;是一种类似于经典蓝牙的新通信协议&#xff0c;不同之处在于它旨在消耗更少的功耗和成本&#xff0c;同时保持同等的功能。 因此&#xff0c;低功耗蓝牙是与耗电资源有限的物联网设备进行通信的首选。BluetoothLE 扩…

【C++】C++11中R字符串的作用

在 C11 中添加了定义原始字符串的字面量 1.定义和基本使用 定义方式为&#xff1a; R"xxx(原始字符串)xxx"其中 () 两边的字符串可以省略&#xff0c;R只会处理括号中的字符串。 原始字面量 R 可以直接表示字符串的实际含义&#xff0c;而不需要额外对字符串做转义…

文件系统基础 (二)——文件的物理结构

目录 一. 前言二. 连续(顺序)分配三. 链接分配隐式链接显式链接 三. 索引分配单级索引分配多级索引分配混合索引分配混合索引分配相关计算 五. 总结 一. 前言 文件的物理结构就是研究文件的实现&#xff0c;即文件数据在物理存储设备上是如何分布和组织的。 文件的分配方式&a…

【计算机网络原理】网络层IP协议的总结和数据链路层以太网协议的总结.

˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如…

anaconda searchanaconda show | conda 检索包资源安装指定版本包指定源安装命令package

conda issuehttp://t.csdnimg.cn/ndZZK 目录 常规安装 检索包资源 获取指定包的安装源&安装指令 安装指定包 常规安装 conda 常规安装xxx包 conda install xxx conda install有可能会受限于channel导致报错PackagesNotFoundError: The following packages are not av…

RedisTemplate、StringRedisTemplate、序列化器配置

Lettuce和Jedis RedisTemplate是SpringDataRedis中对JedisApi的高度封装&#xff0c;提供了Redis各种操作、 异常处理及序列化&#xff0c;支持发布订阅。 首先我们要知道SpringData是Spring中数据操作的模块&#xff0c;包括对各种数据库的集成&#xff0c;比如我们之前学过…

SpringBoot轻松实现多数据源切换

一.需求背景 项目需要实现在多个数据源之间读写数据&#xff0c;例如在 A 数据源和 B 数据源读取数据&#xff0c;然后在 C 数据源写入数据 或者 部分业务数据从 A 数据源中读取、部分从B数据源中读取诸如此类需求。本文将简单模拟在SpringBoot项目中实现不同数据源之间读取数…