C++进阶:C++11新增语法(1)

news2024/10/1 15:17:25

文章目录

    • 1. 统一的列表初始化
      • 1.1 {}初始化
    • 1.2 std::initializer_list
    • 2. 声明
      • 2.1 auto
      • 2.2 decltype
      • 3.3 nullptr
    • 4 范围for循环
    • 5 STL中一些变化
    • 6 右值引用和移动语义
      • 6.1 左值引用和右值引用
      • 6.2 左值引用与右值引用特性
      • 6.3 左值引用和右值引用使用场景和意义
      • 6.4 右值引用及其一些更深入的使用场景分析
      • 6.5 完美转发

1. 统一的列表初始化

1.1 {}初始化

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

struct Point
{
	int _x;
	int _y;
};

int main()
{	//初始化数组 内容确定
	int array1[] = { 1, 2, 3, 4, 5 };
	//初始化数组 第一个参数为0
	int array2[5] = { 0 };
	//初始化结构体 _x = 1, _y = 2
	Point p = { 1, 2 };
	return 0;
}

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

struct Point
{
	int _x;
	int _y;
};
int main()
{
	//正常初始化 还是有 = 的
	int x1 = 1;
	//C++新增初始化 不仅可以用{},而且=都可以省
	int x2{ 2 };
	//数组 自定义类型用{}初始化也可以省略 = 
	int array1[]{ 1, 2, 3, 4, 5 };
	int array2[5]{ 0 };
	Point p{ 1, 2 };
	
	// C++11中列表初始化也可以适用于new表达式中
	int* pa = new int[4]{ 0 };
	return 0;
}

3️⃣创建对象时也可以使用列表初始化方式调用构造函数初始化

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(2022, 1, 1); //以前的方式

	// C++11支持的列表初始化,这里会调用构造函数初始化
	// Date类的初始化接受3个参数,列表就可以按顺序发进这三个值
	Date d2{ 2022, 1, 2 };
	Date d3 = { 2022, 1, 3 };
	
	return 0;
}

1.2 std::initializer_list

在这里插入图片描述
在C++11中,诸如 {1,3,42,3 }这样用{ }组合起来的列表是有类型的,这个类型就是 initializer_list

int main()
{
	// 用auto自动识别类型
	auto il = { 10, 20, 30 };
	// class std::initializer_list<int>
	cout << typeid(il).name() << endl;
	return 0;
}

📝这个类型常作为C++_STL库中一些容器的构造函数参数,这也是这些容器能够支持列表初始化(就是{})的原因,C++_STL容器的构造函数在C++11中重载了initializer_list的版本,{1,3,42,3 }这样的列表会隐式类型转换成initializer_list,随后作为参数传入容器的构造函数。

在这里插入图片描述

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

int main()
{
	//容器支持{}初始化
	vector<int> v = { 1,2,3,4 };
	list<int> lt = { 1,2 };
	// 这里{"sort", "排序"}会先初始化构造一个pair对象
	map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };
	// 使用大括号对容器赋值
	v = { 10, 20, 30 };
	return 0;
}

2. 声明

2.1 auto

C++11提供了多种简化声明的方式,尤其是在使用模板时。
在C++98中auto是一个存储类型的说明符,表明变量是局部自动存储类型,但是局部域中定义局部的变量默认就是自动存储类型,所以auto就没什么价值了。
📋C++11中废弃auto原来的用法,将其用于实现自动类型推断。这样要求必须进行显示初始化,让编译器将定义对象的类型设置为初始化值的类型

int main()
{
	int i = 10;
	//识别指针类型
	auto p = &i;
	cout << typeid(p).name() << endl;

	map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };
	//识别模板容器迭代器
	auto it = dict.begin();

	return 0;
}

2.2 decltype

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

	int x;
	//推断x的类型,将y声明为该类型
	decltype(x) y;
// decltype的一些使用使用场景
template<class T1, class T2>
void F(T1 t1, T2 t2)
{
	//先让模板推断类型 再让decltype推断二者的乘积的类型
	decltype(t1 * t2) ret;
	// 根据传入类型的不同,打印不同的类型的名称
	cout << typeid(ret).name() << endl;
}

int main()
{
	const int x = 1;
	double y = 2.2;
	// ret的类型是double
	decltype(x * y) ret; 
	// p的类型是int*
	decltype(&x) p; 
	
	F(1, 'a');
	return 0;
}

3.3 nullptr

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

4 范围for循环

如果容器或自定义类型支持迭代器以及迭代器相关的重载,就能支持范围for循环。

示例:

//这里的函数只声明了 没实现 只能通过语法层,内部实现后就能成功使用范围for
class Dateiterator
{
public:
	typedef DateNode Node;
	Node* _node;
	Dateiterator(Node* node = nullptr)
	{}
	//->重载
	int* operator->()
	{};
	//!=重载
	bool operator!=(const Dateiterator& l) {};
	//==重载
	bool operator==(const Dateiterator& l) {};
	//前置++
	Dateiterator& operator++(){}
	//*重载
	int& operator*(){}
	//!=重载
	bool operator!=(const Dateiterator* node){}
};

class Date
{
public:
	typedef DateNode Node;
	typedef Dateiterator iterator;

	iterator begin()
	{
		return iterator(_root);
	}

	iterator end()
	{
		return  iterator(nullptr);
	}

private:
	Node* _root;
};

int main()
{
	Date s;
	for (auto e : s)
	{
	}
}

5 STL中一些变化

新容器:
在这里插入图片描述
其中unordered_map和unordered_set是最有用的,这里不做过多介绍。

容器中的一些新方法
C++11更新后,容器中增加的新方法最后用的插入接口函数右值引用版本,这个版本能提高容器插入的效率。
在这里插入图片描述
相应的,构造函数也会有右值引用的版本
在这里插入图片描述

稍后会讲解何为右值引用。

6 右值引用和移动语义

6.1 左值引用和右值引用

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

🔎那么,何为左值?何为左值引用?

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

// 以下的a、b、c都是左值
// 都可以取地址 在=左边 可以修改(const修饰的不可以)
int a = 1;
const int b = 2;
int* c = new int(0);

//取地址
&a;

//修改 出现在=左边
a = 2;

//左值引用就是对左值的引用
int& _a = a;//_a是a的左值引用

🔎什么是右值?什么是右值引用?

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

double x = 1.1, y = 2.2;
// 以下几个都是常见的右值
10;
x + y;
fmin(x, y);

// 对右值的右值引用
int&& rr1 = 10;

// 这里编译会报错:error C2106: “=”: 左操作数必须为左值
// 右值不能修改,不能取地址
10 = 1;
&10;

右值不能被修改和取地址,但是右值被右值引用后可以通过右值引用进行取地址和修改。原先的右值是个临时变量,生命周期很短,但是给它起了右值引用后,右值被存储到特定位置,就可以取地址或者修改了。

double x = 1.1, y = 2.2;
int&& rr1 = 10;
const double&& rr2 = x + y;

//右值引用可以修改数据
rr1 = 20;
//当然 const修饰的版本不能修改
rr2 = 5.5; // 报错
//但都可以取地址
&rr1;
&rr2;

6.2 左值引用与右值引用特性

左值引用:

  1. 左值引用只能引用左值,不能引用右值
  2. 但是const左值引用既可引用左值,也可引用右值
int x = 10;
//left是x的别名 
int& left = x;
//const 左值引用既可以引用左值 又可以引用右值
const int& left2 = 10;
const int& left2 = x;

右值引用:

  1. 右值引用只能右值,不能引用左值
  2. 但是右值引用可以引用move以后的左值
// 右值引用只能右值,不能引用左值。
int&& r1 = 10;

// error C2440: “初始化”: 无法从“int”转换为“int &&”
// message : 无法将左值绑定到右值引用
int a = 10;
int&& r2 = a;

// 右值引用可以引用move以后的左值
//move: 将左值以右值的方式返回
int&& r3 = std::move(a);

6.3 左值引用和右值引用使用场景和意义

先模拟实现一个没有移动构造的string,方便对后续的知识点进行解释:(代码内容不重要,只知道是一个没有实现移动构造的string类就行),想详细了解可以看C++_string简单源码剖析:string模拟实现
这里也贴一份:

namespace bit
{
	class string
	{
	public:
		typedef char* iterator;
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		string(const char* str = "")
			:_size(strlen(str))
			, _capacity(_size)
		{
			//cout << "string(char* str)" << endl;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
		// s1.swap(s2)
		void swap(string& s)
		{
			::swap(_str, s._str);
			::swap(_size, s._size);
			::swap(_capacity, s._capacity);
		}
		// 拷贝构造
		string(const string& s)
			:_str(nullptr)
		{
			cout << "string(const string& s) -- 深拷贝" << endl;
			string tmp(s._str);
			swap(tmp);
		}
		// 赋值重载
		string& operator=(const string& s)
		{
			cout << "string& operator=(string s) -- 深拷贝" << endl;
			string tmp(s);
			swap(tmp);
			return *this;
		}
		// 移动构造
		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()
		{
			delete[] _str;
			_str = nullptr;
		}
		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}
		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;
				_capacity = n;
			}
		}
		void push_back(char ch)
		{
			if (_size >= _capacity)
			{
				size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
				reserve(newcapacity);
			}
			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
		}
		//string operator+=(char ch)
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}
		const char* c_str() const
		{
			return _str;
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity; // 不包含最后做标识的\0
	};
}

回到正题

📝左值引用作为函数的参数和返回值是,可以减少拷贝,有效的提高效率
在这里插入图片描述

在这里插入图片描述

左值引用的短板
📝但是当函数返回对象是一个局部变量,出了函数作用域就不存在了,就不能使用左值引用返回,只能传值返回。例如:string function2(string s1)函数中可以看到,这里只能使用传值返回,传值返回会导致至少1次拷贝构造(如果是一些旧一点的编译器可能是两次拷贝构造)
在这里插入图片描述

右值引用和移动语义解决上述问题
📝在bit::string中增加移动构造,📋以右值引用为参数的拷贝构造一般成为移动构造移动构造本质是将参数右值的资源窃取过来,占位已有,那么就不用做深拷贝了,所以它叫做移动构造,就是窃取别人的资源来构造自己。

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

在这里插入图片描述

不仅仅有移动构造,还有移动赋值
在bit::string类中增加移动赋值函数,再去调用bit::to_string(1234),不过这次是将bit::to_string(1234)返回的右值对象赋值给s对象,这时调用的是移动构造。下面的情况是调用移动赋值。

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

//像这样的情况,先无参构造,再对其赋值,就会调用移动赋值
int main()
{
	bit::string ret1;
	ret1 = bit::to_string(1234);
	return 0;
}
//输出 string& operator=(string&& s) -- 移动语义

STL中的容器都是增加了移动构造和移动赋值
在这里插入图片描述
在这里插入图片描述

由于编译器的优化问题,我们很多时候都看不到构造的细节,这里讲讲常见的优化场景:
在这里插入图片描述

在这里插入图片描述

6.4 右值引用及其一些更深入的使用场景分析

因为STL的大部分容器都有重载右值引用版本,所以在C++_list简单源码剖析:list模拟实现中,我们也可以对模拟实现的list添加右值引用版本,针对添加的是右值引用版本的insert(list),push_back(list)和移动构造(ListNode):

//listNode
ListNode(T&& data)
	:_next(nullptr)
	, _prev(nullptr)
	, _data(data)
{}

//list
void push_back(T&& x)
{
	insert(end(), x);
}

iterator insert(iterator pos, T&& x)
{
	Node* cur = pos._node;
	Node* newnode = new Node(x);
	Node* prev = cur->_prev;

	// prev  newnode  cur
	prev->_next = newnode;
	newnode->_prev = prev;
	newnode->_next = cur;
	cur->_prev = newnode;

	return iterator(newnode);
}

📝写个测试案例去测试实际情况

int main()
{
	bit::list<bit::string> lt;
	lt.push_back(bit::string("11111"));
}

📝但是,结果却出奇的发现,实际lt并没有调用任何的移动构造,这是为什么😕?
在这里插入图片描述
📝经过调试我们发现,我们传入右值bit::string(“11111”)后,😧确实是进入了list右值版本的push_back,但是却在下一级的调用中,却没有调用list右值版本的insert,
在这里插入图片描述
要解释这个原因,我们就先讲讲右值引用本身的特性,🔎为什么右值不能被修改,但是却通过右值引用可以修改?

💡原因是:右值引用本身是左值只有右值引用本身的属性是左值,才能转移他的资源。
在这里插入图片描述
💡要解决这样的问题很简单,就是在每次向下级传值时,都对传入的值move一下,就可以保证传入的值是一个右值,从而去调用一个右值版本

//修改后的版本
//listNode
ListNode(T&& data)
	:_next(nullptr)
	, _prev(nullptr)
	, _data(move(data))
{}

//list
void push_back(T&& x)
{
	insert(end(), move(x));
}

iterator insert(iterator pos, T&& x)
{
	Node* cur = pos._node;
	Node* newnode = new Node(move(x));
	Node* prev = cur->_prev;

	// prev  newnode  cur
	prev->_next = newnode;
	newnode->_prev = prev;
	newnode->_next = cur;
	cur->_prev = newnode;

	return iterator(newnode);
}

在这里插入图片描述
此时就可以成功使用移动构造了。


这里再介绍一次move函数
📋当需要用右值引用去引用一个左值时,可以通过move函数将左值转化为右值。C++11中,std::move()函数位于头文件中,该函数名字具有迷惑性,它并不搬移任何东西,唯一的功能就是将一个左值强制转化为右值引用,然后实现移动语义

6.5 完美转发

模板中的&& 万能引用

📝模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值

//万能引用👇
template<class T>
void PerfectForward(T&& t)
{
	Fun(t);
}

📝模板的万能引用只是提供了能够同时接收左值引用和右值引用的能力

int main()
{
	int a;
	const int b = 8;

	PerfectForward(a); // 左值
	PerfectForward(std::move(a)); // 右值
	PerfectForward(b); // const 左值
	PerfectForward(std::move(b)); // const 右值
	return 0;
}

📝输出结果:PerfectForward函数都成功接受了四种参数并成功调用
在这里插入图片描述
📝但是,引用类型的唯一作用就是限制了接收的类型,后续使用中都退化成了左值(无论是成为左值引用还是右值引用,引用本身还是左值),我们希望能够在传递过程中保持它的左值或者右值的属性, 就需要用我们下面学习的完美转发

📋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<class T>
void PerfectForward(T&& t)
{
	//完美转发
	//将t转化为本来的类型
	//由此可以传入不同类型的值而调用不同接口的Fun函数
	Fun(std::forward<T>(t));
}

int main()
{
	int a;
	const int b = 8;

	PerfectForward(a); // 左值
	PerfectForward(std::move(a)); // 右值
	PerfectForward(b); // const 左值
	PerfectForward(std::move(b)); // const 右值
	return 0;
}



本文就到这里,感谢你看到这里❤️❤️! 我知道一些人看文章喜欢静静看,不评论🤔,但是他会点赞😍,这样的人,帅气低调有内涵😎,美丽大方很优雅😊,明人不说暗话,要你手上的一个点赞😘!

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

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

相关文章

图的应用--最小生成树

一. 前言 生成树的概念&#xff1a; 生成树是图的极小连通子图&#xff0c;也就是去掉一条边就非连通的图&#xff0c;并且这个图的所有顶点都由边连接在一起&#xff0c;但不存在回路。 生成树的特点&#xff1a;一个图可以有很多个不同的生成树。生成树的顶点个数和图的顶点个…

另一棵树的子树 - 力扣(LeetCode)C语言

572. 另一棵树的子树 - 力扣&#xff08;LeetCode&#xff09;&#xff08;点击前面链接即可查看题目&#xff09; 一、题目 给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在&#xff0c;返回 true &#xff1b;否则&…

反转链表 II(LeetCode)

题目 给你单链表的头指针 和两个整数 和 &#xff0c;其中 。请你反转从位置 到位置 的链表节点&#xff0c;返回 反转后的链表 。 解题 class ListNode:def __init__(self, value0, nextNone):self.value valueself.next nextdef reverseBetween(head: ListNode, lef…

即使是 Linux 用户也应该看看这篇文章。

关注公众号网络研究观获取更多内容。 安全启动至少从 Windows 8 开始就成为一项标准功能。 顾名思义&#xff0c;该功能可保护启动过程。 通过对启动过程中使用的任何软件&#xff08;“固件”&#xff09;进行数字签名&#xff0c;可确保启动过程的完整性。 与任何数字签名…

量产部落SM2258XT开卡软件,SM2258XT主控128G SSD固态卡死修复

故障现象&#xff1a;连接此固态硬盘后电脑就会卡死&#xff0c;拔掉重新连接概率性显示盘符&#xff0c;显示了之后也不能正常操作&#xff0c;一点击打开&#xff0c;电脑就立马卡死。 解决过程&#xff1a;下载了很多款量产工具&#xff0c;都不能开卡成功&#xff0c;点击…

【传知代码】受限前缀注意机制的事件关系识别(论文复现)

在自然语言处理的前沿&#xff0c;受限前缀注意机制正逐渐成为事件关系识别领域的关键技术。随着文本数据的不断增长和复杂性的提升&#xff0c;识别文本中事件之间的关系变得愈发重要和具有挑战性。受限前缀注意机制作为一种新兴的技术手段&#xff0c;通过有效地捕获文本中的…

不同太阳光波长定制设备太阳光模拟器

日光中不同波长的作用 太阳光不仅是地球上生命活动的能量来源&#xff0c;也对人类的生活产生了深刻的影响。了解太阳光的特性和对地球生态系统的贡献&#xff0c;对于我们保护环境、合理利用太阳能资源具有重要意义。科迎法电气作为太阳光模拟器的制造商可以根据不同的需求定制…

【Verilog-CBB】开发与验证(4)——RS前向打拍器

引言 我们知道时序路径的建立时间收敛依赖于触发器之间的组合逻辑延迟&#xff0c;以及时钟周期。对于一个确定的设计一般来说&#xff0c;时钟频率不会有太多的让步&#xff0c;因此修时序的时候主要发力点就是减小触发器之间组合逻辑延迟。一方面可以通过组合逻辑优化来降低…

Studying-代码随想录训练营day56| 108.冗余连接、109.冗余连接II

第56天&#xff0c;图论06&#xff0c;并查集题目类型冗余连接(ง •_•)ง&#x1f4aa;&#xff0c;编程语言&#xff1a;C 目录 108.冗余连接 109.冗余连接II 总结 108.冗余连接 文档讲解&#xff1a;手撕冗余连接 题目&#xff1a;108. 冗余连接 (kamacoder.com) 学习&…

SpringSecurity-3(认证和授权+SpringSecurity入门案例+自定义认证+数据库认证)

SpringSecurity使用数据库数据完成认证 5 SpringSecurity使用数据库数据完成认证5.1 认证流程分析5.1.1 UsernamePasswordAuthenticationFilter5.1.2 AuthenticationManager5.1.3 AbstractUserDetailsAuthenticationProvider5.1.4 AbstractUserDetailsAuthenticationProvider中…

跟《经济学人》学英文:2024年08月03日这期 How to make tourism work for locals and visitors alike

How to make tourism work for locals and visitors alike Holidays don’t have to be hell 原文&#xff1a; The dollar is hovering near a two-decade high. That has unleashed a multitude of American tourists keen to exercise their newfound spending power, m…

图的应用---最短路径

目录 一. 前言 二. Dijkstra算法 三. Floyd算法 一. 前言 所谓最短路径&#xff1a;就是在有向网当中&#xff0c;A点&#xff08;源点&#xff09;到达B点&#xff08;终点&#xff09;的多条路径中&#xff0c;寻找一条各边权值之和最小的路径&#xff0c;即最短路径。 如果…

Linux--TCP协议

传输层协议TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff09;是一种面向连接的、可靠的、基于字节流的传输层通信协议&#xff0c;会对数据的传输进行一个详细的控制。 TCP协议段格式 TCP协议段格式是TCP协议在网络传输数据时所使用的结构单…

Google paly开发者账号因恶意软件被封,也能申诉成功?怎么做?

想必在Google Play上摸爬滚打多年的各位开发者&#xff0c;在面对谷歌那似乎越来越严格的审核机制&#xff0c;都遭遇过应用被下架、账号被封的困境。 尤其是当你的应用被莫名其妙地判定为恶意软件&#xff0c;然后账号被封&#xff0c;更是让人头疼不已。 但别急&#xff0c;虽…

flutter开发环境搭建与android studio 安装配置

flutter开发环境搭建与android studio 安装配置 安装 android studio 下载安装 Android Studio 开发工具 Android Studio官网安装的时看到配置路径就换成自己其他盘的路径即可&#xff0c;其他的一路下一步就ok安装完毕&#xff0c;运行打开缺少 android sdk 按照提示下载即可…

python nicegui实现TodoList应用案例

参考&#xff1a; https://nicegui.io/ https://nicegui.io/#demos 版本 nicegui 1.4.30代码&#xff1a; python todolist.py from nicegui import ui from typing import List import jsonclass Todo:def __init__(self, text: str, completed: b…

连载|浅谈红队中的权限维持(六)-Linux 主机后门与Linux 隐藏文件

本文来源无问社区&#xff0c;更多实战内容&#xff0c;渗透思路可前往查看http://www.wwlib.cn/index.php/artread/artid/11584.html 0x01 Linux 主机后门 1、添加用户 一句话添加用户 useradd test;echo -e "123456n123456n" |passwd test 或者使用 openssl …

测试——Selenium

内容大纲: 什么是自动化测试 什么是Selenium Selenium工作原理 Selenium环境搭建 Selenium API 目录 1. 什么是自动化测试 2. 什么是Selenium 3. Selenium工作原理 4. Selenium环境搭建(java) 5. Selenium API 5.1 定位元素 5.1.1 CSS选择器定位元素 5.1.2 XPath定位元…

arr的谜思

arr到底是个啥&#xff1f; 在C语言中&#xff0c;arr 本身不是一个指针&#xff0c;而是一个数组名。然而&#xff0c;在大多数表达式中&#xff0c;数组名会被转换为指向数组首元素的指针。这种转换是自动的&#xff0c;并且是C语言的一个基本特性。 具体来说&#xff0c;当…

LDR6020快充线,科技与便捷的新宠

在快节奏的现代生活中&#xff0c;充电效率成为了我们不可忽视的一个重要因素。随着智能手机、平板电脑等电子设备的普及&#xff0c;快充线以其独特的优势逐渐成为充电设备市场的新宠。 快充线&#xff0c;相比于传统的普通充电线&#xff0c;快充线在充电速度上有着显著的提升…