C++ 11【右值引用】

💓博主CSDN主页:麻辣韭菜💓

⏩专栏分类:C++修炼之路⏪

🚚代码仓库:C++高阶🚚

🌹关注我🫵带你学习更多C++知识
  🔝🔝

1.C++ 11 简介

目录

1.C++ 11 简介

2. 统一的列表初始化  

2.1 {}初始化  

 2.2 initializer_list

3.1 auto 

3.2 decltype

 4.右值引用和移动语义

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

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

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

5.完美转化

5.1模板中的&& 万能引用(引用折叠)

 5.2完美转发实际中的使用场景:


C++11是C++编程语言的一个主要更新版本,它在2011年被国际标准化组织(ISO)正式批准为ISO/IEC 14882:2011标准。这个版本引入了许多新特性和改进,旨在提高语言的表达能力、性能和易用性。以下是C++11的一些关键特性:

  1. 自动类型推断(auto关键字):允许编译器自动推断变量的类型,简化了代码编写,尤其是在模板元编程中。

  2. 基于范围的for循环:提供了一种更简洁的方式来遍历容器。

  3. lambda表达式:允许在代码中创建匿名函数,这在算法编程中非常有用。

  4. 智能指针:如std::unique_ptrstd::shared_ptr,提供了自动内存管理,减少了内存泄漏的风险。

  5. 右值引用和移动语义:允许更高效的资源管理,特别是对于临时对象。

  6. 变长模板参数:允许模板函数和类接受任意数量的模板参数。

  7. 委托构造函数:允许构造函数之间相互委托。

  8. 继承构造函数:允许派生类继承基类的构造函数。

  9. 线程库:引入了一个新的线程库,支持多线程编程。

  10. 原子操作和内存模型:提供了对多线程编程的支持,包括原子操作和内存顺序控制。

  11. 用户定义的字面量:允许用户定义新的字面量后缀。

  12. 静态断言:可以在编译时检查条件。

  13. 类型特征和类型遍历:提供了类型信息查询和类型操作的能力。

  14. 统一的初始化列表:简化了对象的初始化。

  15. 新的字符串字面量:如u8"",支持UTF-8编码。

  16. 匿名结构体和类:允许在声明中嵌套结构体和类。

  17. 属性(属性修饰符):如[[deprecated]],允许标记某些特性为已弃用。

  18. 正则表达式库:提供了正则表达式的处理能力。

C++11的这些特性使得C++语言更加强大和灵活,同时也使得编写的代码更加简洁和易于维护。

2. 统一的列表初始化  

2.1 {}初始化  

#include <iostream>using namespace std;struct Point
{int _x;int _y;
};
int main()
{int x1 = 1;int x2 = { 2 };// int x4(1);// 可以省略赋值符号int x3{ 3 };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;
}

​ 

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

我个人感觉还是添加等号(=),比较好。

class Date
{
public://explicit Date(int year, int month, int day)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); // old style// C++11支持的列表初始化,这里会调用构造函数初始化Date d2 = { 2024, 5, 20 };Date d3 { 2024, 5, 21 };return 0;
}

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

​ 

 2.2 initializer_list

它提供了一种便捷的方式来接收和使用初始化列表(initializer lists)。初始化列表是一组用花括号 {} 包围的初始化值。

在 C++11 之前,当你需要初始化一个容器或对象时,你通常需要使用单独的参数或者构造函数的多个参数。std::initializer_list 的引入使得你可以一次性传递一个值的列表,这在初始化数组、容器或自定义类型时非常有用。

#include <initializer_list>
#include <iostream>class MyClass {
public:MyClass(std::initializer_list<int> init) {for (auto value : init) {values.push_back(value);}}void print() const {for (auto value : values) {std::cout << value << " ";}std::cout << std::endl;}private:std::vector<int> values;
};int main() {MyClass obj1 = {1, 2, 3, 4, 5}; // 使用列表初始化 MyClassobj1.print(); // 输出: 1 2 3 4 5MyClass obj2{6, 7, 8, 9, 10}; // 直接初始化 MyClassobj2.print(); // 输出: 6 7 8 9 10return 0;
}

 std::initializer_list作为参数的构造函数,这样初始化容器对象就更方便了。也可以作为operator=

的参数,这样就可以用大括号赋值

class Date
{
public://explicit Date(int year, int month, int day)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()
{vector<int> v1 = { 1,2,3,4,5 };vector<int> v2 = { 10,20,30 };vector<int> v3 = { 10,20,30,1,1,2,2,2,2,2,2,1,1,1,1,1,1,1,1,2,1,1,2 };list<int> lt1 = { 1,2,3,4,5 };list<int> lt2 = { 10,20,30 };auto i1 = { 10,20,30,1,1,2,2,2,2,2,2,1,1,1,1,1,1,1,1,2,1,1,2 };auto i2 = { 10,20,30 };cout << typeid(i1).name() << endl;cout << typeid(i2).name() << endl;initializer_list<int>::iterator it1 = i1.begin();initializer_list<int>::iterator it2 = i2.begin();cout << it1 << endl;cout << it2 << endl;//*it1 = 1;initializer_list<int> i3 = { 10,20,30 };initializer_list<int>::iterator it3 = i3.begin();cout << it3 << endl;Date d1(2023,5,20);Date d2(2023,5,21);// initializer_list<Date>vector<Date> vd1 = {d1, d2};vector<Date> vd2 = { Date(2023,5,20), Date(2023,5,21) };vector<Date> vd3 = { {2023,5,20}, {2023,5,20} };map<string, string> dict = { {"sort", "排序"},{"string", "字符串"},{"Date", "日期"} };pair<string, string> kv1 = { "Date", "日期" };pair<string, string> kv2 { "Date", "日期" };return 0;
}

 

3.1 auto 

C++98 auto 是一个存储类型的说明符,表明变量是局部自动存储类型,但是局部域中定义局
部的变量默认就是自动存储类型,所以 auto 就没什么价值了。 C++11 中废弃 auto 原来的用法,将
其用于实现自动类型腿断。这样要求必须进行显示初始化,让编译器将定义对象的类型设置为初
始化值的类型。
int main()
{int i = 10;auto p = &i;auto pf = strcpy;cout << typeid(p).name() << endl;cout << typeid(pf).name() << endl;map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };//map<string, string>::iterator it = dict.begin();auto it = dict.begin();return 0;
}

3.2 decltype

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

int main()
{const int x = 1;double y = 2.2;cout << typeid(x * y).name() << endl;decltype(x * y) ret; // ret的类型是doubledecltype(&x) p;      // p的类型是const int*cout << typeid(ret).name() << endl;cout << typeid(ret).name() << endl;cout << typeid(p).name() << endl;// vector存储的类型跟x*y表达式返回值类型一致// decltype推导表达式类型,用这个类型实例化模板参数或者定义对象vector<decltype(x* y)> v;return 0;
}

 

 4.右值引用和移动语义

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

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

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

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

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);// 这里编译会报错:error C2106: “=”: 左操作数必须为左值10 = 1;x + y = 1;fmin(x, y) = 1;return 0;
}
需要注意的是右值是不能取地址的,但是给右值取别名后,会导致右值被存储到特定位置,且可
以取到该位置的地址,也就是说例如:不能取字面量10的地址,但是rr1引用后,可以对rr1取地
址,也可以修改rr1
int main()
{double x = 1.1, y = 2.2;int&& rr1 = 10;const double&& rr2 = x + y;rr1 = 20;rr2 = 5.5;  // 报错return 0;
}

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

左值引用总结:
1. 左值引用只能引用左值,不能引用右值。
2. 但是const左值引用既可引用左值,也可引用右值

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

右值引用总结:
1. 右值引用只能右值,不能引用左值。
2. 但是右值引用可以move以后的左值。

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

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

既然左值引用可以引用左值和引用右值,那C++11搞出右值引用岂不是多此一举?不要忘了左值引用还有短板没有解决。

我们通过代码来演示右值引用的意义

通过上面的代码示例我们可以看到 传值a+b会调用参数是右值引用的func函数。当然有人觉得这不能说明右值引用的意义。那如果传值是个自定义类型的参数比如string这样的容器又或者unordered_map 这样的?

比如下面这种场景

#pragma once
#include <iostream>
#include <list>
#include <vector>
#include <cassert>
#include <map>using namespace std;
namespace gx
{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(){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;}string operator+(char ch){string tmp(*this);tmp += ch;return tmp;}const char* c_str() const{return _str;}private:char* _str;size_t _size;size_t _capacity; // 不包含最后做标识的\0};
}
#include "String.h"
int main()
{gx::string s1("hello world");gx::string s2(s1);gx::string s3 = (s2 + '!');return 0;
}

s2+'!' 是个右值 右值又称为将亡值。将亡值也就意味着执行完这条语句。s2+'!'所在的空间会被析构函数回收。那我们这时再拷贝一个和它一样的大的空间岂不是浪费了?能不能把它的空间直接给s3? 

当然可以 我们在头文件String.h增加这段代码

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

 移动构造代价就比深拷贝的代价低了很多。效率就变高了!

之前的学习我们知道左值引用使用场景:

一是引用传参二是引用返回

但是引用返回如果是函数内的局部对象,是不能用引用返回的。

这就意味局部对象返回时是要拷贝的。那如果返回的局部对象是一颗红黑树?那拷贝就极大降低了效率。 

举个例子 string 的to_string这个函数。

gx::string to_string(int value){bool flag = true;if (value < 0){flag = false;value = 0 - value;}gx::string str;while (value > 0){int x = value % 10;value /= 10;str += ('0' + x);}if (flag == false){str += '-';}std::reverse(str.begin(), str.end());return str;}

 

那如果是C++11之后的编译器呢?

 

编译器直接优化成移动构造 

右值引用和移动语义解决上述问题:
gx ::string 中增加移动构造, 移动构造本质是将参数右值的资源窃取过来,占位已有,那么就不
用做深拷贝了,所以它叫做移动构造,就是窃取别人的资源来构造自己
移动构造中没有新开空间,拷贝数据,所以效率提高了。  
不仅仅有移动构造,还有移动赋值:
gx::string 类中增加移动赋值函数,再去调用gx ::to_string(1234) ,不过这次是将
gx::to_string(1234) 返回的右值对象赋值给 ret1 对象,这时调用的是移动构造。
// 移动赋值
string& operator=(string&& s)
{
cout << "string& operator=(string&& s) -- 移动语义" << endl;
swap(s);
return *this;
}

 

这里运行后,我们看到调用了一次移动构造和一次移动赋值。因为如果是用一个已经存在的对象
接收,编译器就没办法优化了。gx::to_string函数中会先用str生成构造生成一个临时对象,但是
我们可以看到,编译器很聪明的在这里把str识别成了右值,调用了移动构造。然后在把这个临时
对象做为gx::to_string函数调用的返回值赋值给ret1,这里调用的移动赋值

 

 C++11后STL中的容器都是增加了移动构造和移动赋值:

这里就列举移动构造感兴趣的可以去cplusplus官网看看。

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

按照语法,右值引用只能引用右值,但右值引用一定不能引用左值吗?因为:有些场景下,可能
真的需要用右值去引用左值实现移动语义。 当需要用右值引用引用一个左值时,可以通过 move
函数将左值转化为右值 C++11 中, std::move() 函数 位于 头文件中,该函数名字具有迷惑性,
并不搬移任何东西,唯一的功能就是将一个左值强制转化为右值引用,然后实现移动语义
int main()
{gx::string s1("hello world");// 这里s1是左值,调用的是拷贝构造gx::string s2(s1);// 这里我们把s1 move处理以后, 会被当成右值,调用移动构造// 但是这里要注意,一般是不要这样用的,因为我们会发现s1的// 资源被转移给了s3,s1被置空了。gx::string s3(std::move(s1));return 0;
}

STL容器插入接口函数也增加了右值引用版本:

以链表为例

 从上面运行结果来看,减少了深拷贝。那就意味着效率就提高了。当然move以后也有风险。s1就悬空了没有了。

一般来说我们写法不会用move而是下面这种 

 

那如果是C++98 那就是拷贝构造。而11之后就是移动构造。

总结:

左值引用减少拷贝,提高效率
右值引用也是减少拷贝,提高效率
但是他们的角度不同,左值引用是直接减少拷贝
右值引用是间接减少拷贝,识别出是左值还是右值,如果是右值,则不再深拷贝,直接移动拷贝!提高效率。

5.完美转化

5.1模板中的&& 万能引用(引用折叠)

 模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值。
 模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力,
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;
}

先看上面一段代码的 按照常理 PerfectForward(std::move(a));  PerfectForward(std::move(b));

是会调用void Fun(int&& x) { cout << "右值引用" << endl; }和void Fun(const int&& x) { cout << "const 右值引用" << endl; }函数

 

运行结果却都是左值引用?这是为什么?

因为右值本身就具有常性不可修改 PerfectForward这个函数参数是万能引用 当识别是右值时,编译器发现是右值时,会开辟一段空间,而这个开辟的空间会和传参的值进行资源转换。而在转换的过程中会出现属性丢失。也就是说原本的右值属性变成了左值属性。而这时在fun这函数进行传参t时,而这个t就变成左值。那调用时就会调用参数是左值的fun

那如何解决这个问题?

那就需要用到完美转发 forward<>()

 std::forward<T>(t)在传参的过程中保持了t的原生类型属性。

 5.2完美转发实际中的使用场景:

就比如list 我们使用push_back 时,我们后来又实现了insert这个插入函数

如果push_back函数套用insert这个函数就会出现上面的情况。传参时出现属性丢失。

原本是右值的移动构造就会变成拷贝构造。 效率就降低了。

 

template<class T>
struct ListNode
{ListNode* _next = nullptr;ListNode* _prev = nullptr;T _data;
};
template<class T>
class List
{typedef ListNode<T> Node;
public:List(){_head = new Node;_head->_next = _head;_head->_prev = _head;}void PushBack(T&& x){//Insert(_head, x);Insert(_head, std::forward<T>(x));}void PushFront(T&& x){//Insert(_head->_next, x);Insert(_head->_next, std::forward<T>(x));}void Insert(Node* pos, T&& x){Node* prev = pos->_prev;Node* newnode = new Node;newnode->_data = std::forward<T>(x); // 关键位置// prev newnode posprev->_next = newnode;newnode->_prev = prev;newnode->_next = pos;pos->_prev = newnode;}void Insert(Node* pos, const T& x){Node* prev = pos->_prev;Node* newnode = new Node;newnode->_data = x; // 关键位置// prev newnode posprev->_next = newnode;newnode->_prev = prev;newnode->_next = pos;pos->_prev = newnode;}
private:Node* _head;
};
int main()
{List<gx::string> lt;lt.PushBack("1111");lt.PushFront("2222");return 0;
}

如果我们把任意一个关键位置的forward的去掉,都会是拷贝构造。 

 右值引用非常的绕,希望大家下去可以多看几遍。有什么不懂的可以私信我。关注我带你学习更多C++知识。下节预告可变参数模板、lambda。也是非常重要!!!

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

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

相关文章

Vue3【四】使用Vue2的写法写一个新的组件子组件和根组件

Vue3【四】使用Vue2的写法写一个新的组件 Vue3【四】使用Vue2的写法写一个新的组件 Vue3是向下兼容的&#xff0c;所有可以使用Vue的选项式写法 运行截图 目录结构 文件源码 App.vue <template><div class"app"><h1>你好世界! 我是App根组件<…

数据分析常用模型合集(二)RARRA模型、RFM模型

随着互联网的发展&#xff0c;前期平台的砸钱拉新、抢占市场&#xff0c;大家都叫AARRR小甜甜&#xff1b; 现在市场基本抢占得差不多&#xff0c;形成了一个平衡&#xff0c;新人基本拉不到多少&#xff0c;用户都知道干什么事有哪些平台&#xff0c;比如买东西主流淘宝、京东…

动态IP基础解析:为什么我们需要它?

在深入探讨互联网世界的运作机制时&#xff0c;IP地址无疑是其核心要素之一。IP地址&#xff0c;作为网络设备的唯一标识&#xff0c;不仅确保了数据的准确传输&#xff0c;更是网络安全和管理的基石。本文将深入解析动态IP的基础知识&#xff0c;并探讨其重要性及在多种场景下…

大语言模型应用与传统程序的不同

大语言模型&#xff08;LLM&#xff09; 被描述的神乎其神&#xff0c;无所不能&#xff0c;其实&#xff0c;大语言模型只是一个模型&#xff0c;它能够理解和生成自然语言&#xff0c;唯有依靠应用程序才能够发挥作用。例如&#xff0c;基于大模型可以构建一个最简单的会话机…

神经网络 torch.nn---Non-Linear Activations (ReLU)

ReLU — PyTorch 2.3 documentation torch.nn - PyTorch中文文档 (pytorch-cn.readthedocs.io) 非线性变换的目的 非线性变换的目的是为神经网络引入一些非线性特征&#xff0c;使其训练出一些符合各种曲线或各种特征的模型。 换句话来说&#xff0c;如果模型都是直线特征的…

拼多多面试:Netty如何解决粘包问题?

粘包和拆包问题也叫做粘包和半包问题&#xff0c;它是指在数据传输时&#xff0c;接收方未能正常读取到一条完整数据的情况&#xff08;只读取了部分数据&#xff0c;或多读取到了另一条数据的情况&#xff09;就叫做粘包或拆包问题。 从严格意义上来说&#xff0c;粘包问题和…

全球AI新闻速递6.5

全球AI新闻速递 1.昆仑万维&#xff1a;开源2 千亿稀疏大模型天工Skywork-MoE&#xff0c;首个支持单台RTX 4090服务器。 2.字节豆包推出桌面客户端&#xff1a;支持 Windows / macOS&#xff0c;快捷启动、AI 划词、 AI 搜索。 3.东风柳汽与优必选科技签署人形机器人汽车制造…

FreeRTOS手表项目多级菜单的实现

一、首先介绍一下智能手表项目的背景&#xff1a; 如图&#xff0c;关注焦点是任务&#xff1a; 1、在一个确定时刻&#xff0c;在那一圈任务中&#xff08;写有只有一个任务解挂&#xff09;只有一个任务处在运行&#xff0c;界面显示的是该任务应该显示的内容&#xff1b; …

vscode运行Java utf-8文件中文乱码报错

问题现象 vscode 运行utf-8 java文,爆出如下错误 hello.java:5: &#xfffd;&#xfffd;&#xfffd;&#xfffd;: &#xfffd;&#xfffd;&#xfffd;&#xfffd;GBK&#xfffd;IJ&#xfffd;&#xfffd;&#xfffd;ӳ&#xfffd;&#xfffd;&#xfffd;ַ&a…

如何增加网站外链?

想增加网站外链&#xff0c;无非就是去其他别的网站不停去发带自己网站链接的内容&#xff0c;这取决于你去什么平台发&#xff0c;一般来说发外链无非就是几种方式 博客以及论坛&#xff0c;要能提供评论功能的&#xff0c;在这种平台积极发表评论&#xff0c;并在允许的情况…

DevOps在数字化转型中的作用——实现数字化可视性

DevOps 的出现是为了满足不断增长的市场和消费者对技术应用程序的需求。它旨在在不牺牲软件质量的情况下创建更快的开发环境。DevOps 还专注于在快速开发生命周期中提高软件的整体质量。它依赖于多种技术、平台和工具的组合来实现所有这些目标。 容器化是一项彻底改变了我们开发…

如何在桌面添加多个便签 怎样在桌面创建便签

每当我坐在电脑前&#xff0c;总会被各种琐事和灵感所困扰&#xff0c;需要一个随手可记、随时可查的工具。直到我发现了敬业签便签软件彻底改变了我的工作习惯。 她不仅可以在桌面上轻松创建多个便签&#xff0c;而且它的“云游”功能简直让我爱不释手。想象一下&#xff0c;…

http://account.battlenet.com.cn

http://account.battlenet.com.cn 魔兽战网 短信验证 查了下&#xff0c;我老早以前账号还在&#xff0c;纪念下&#xff0c;少玩游戏。

计算机网络学习记录 应用层 Day6

你好,我是Qiuner. 为记录自己编程学习过程和帮助别人少走弯路而写博客 这是我的 github https://github.com/Qiuner ⭐️ ​ gitee https://gitee.com/Qiuner &#x1f339; 如果本篇文章帮到了你 不妨点个赞吧~ 我会很高兴的 &#x1f604; (^ ~ ^) 想看更多 那就点个关注吧 我…

国产方案|轮胎气压胎压计方案

气压胎压计的基本原理是利用气压传感器将气体气压转换为电信号&#xff0c;再通过电子芯片电路进行处理传输&#xff0c;再将这些信息转发给显示屏显示。常见的传感器包括模拟气压传感器和数字气压传感器。其中&#xff0c;模拟气压传感器是目前应用最广泛的传感器之一&#xf…

2020 6.s081——Lab5:Lazy page allocation

再来是千年的千年 不变是眷恋的眷恋 飞越宇宙无极限 我们永不说再见 ——超兽武装 完整代码见&#xff1a;SnowLegend-star/6.s081 at lazy (github.com) Eliminate allocation from sbrk() (easy) 顾名思义&#xff0c;就是去掉sbrk()中调用growproc()的部分。1s完事儿。 Laz…

Mysql root用户远程连接失败解决方案

最近&#xff0c;踩坑云服务器通过root用户远程连接Mysql数据库失败&#xff0c;Mysql 版本为 5.7.44&#xff0c;原因如下&#xff0c;因为root用户权限过大&#xff0c;可能会有风险操作&#xff0c;可以新增其他用户来解决此问题&#xff0c;如果一定要用root用户&#xff0…

gitblit 环境搭建,服务器迁移记录

下载 Gitblit&#xff1a; http://www.gitblit.com/ JDK&#xff1a;gitblit网站显示需要jdk1.7&#xff0c;这里用的1.8。 Git&#xff1a;到官网下载最新版本安装 1). 分别安装JDK&#xff0c;Git&#xff0c;配置环境变量&#xff0c;下载并解压Gitblit 2). 创建代码仓库 …

阿里云镜像加速配置(工作中经常用到,写在此方便)

原因 由于运营商网络原因&#xff0c;会导致您拉取Docker Hub镜像变慢&#xff0c;甚至下载失败。为此&#xff0c;阿里云容器镜像服务ACR提供了官方的镜像加速器&#xff0c;从而加速官方镜像的下载。 获取镜像加速器地址 ACR会为每一个账号&#xff08;阿里云账号或RAM用户…

网络安全专用产品销售许可证查询的几种方式你知道吗?

随着网络技术的日益先进&#xff0c;网络安全事故也频发&#xff0c;因此购买网络安全专用产品&#xff0c;例如堡垒机是非常重要的。这里提醒大家要购买正规具有销售许可证的网络安全专用产品哦&#xff01;网络安全专用产品销售许可证查询的几种方式你知道吗&#xff1f; 网络…