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.rhkb.cn/news/342751.html

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

相关文章

C语言小例程8/100

题目&#xff1a;输出特殊图案&#xff0c;请在c环境中运行&#xff0c;看一看 程序分析&#xff1a;字符共有256个。不同字符&#xff0c;图形不一样。 #include<stdio.h> int main() {char a176,b219;printf("%c%c%c%c%c\n",b,a,a,a,b);printf("%c%c%c…

Kafka 如何基于 KRaft 实现集群最终一致性协调

01 架构概览 Zookeeper 提供了配置服务、分布式同步、命名服务、Leader 选举和集群管理等功能&#xff0c;在大数据时代的开始很多开源产品都依赖 Zookeeper 来构建&#xff0c;Apache Kafka 也不例外。但是随着 Kafka 功能的演进和应用的场景越来越多&#xff1a; 基于 Zoo…

济南适宜地提取

题目: 网上下载中国的DEM、土地利用地图(1980、2000、2015年的)和一张最新济南市行政区划 图(要求:莱芜市并入济南后的区划图); 2.网上下载中国2015年年平均降水空间插值数据;3..网上下载中国2015年年平均气温空间插值数据; (注:以上数据可到资源环境科学与数据中心下载http…

QT快速下载

去QT官网之后&#xff0c;如下图所示 比如要下载qt-opensource-windows-x86-5.14.2.exe&#xff0c;进入5.14对应的文件夹&#xff0c;找到对应的版本 点击Details&#xff0c; 下载对应的种子&#xff0c;然后通过迅雷下载 个人实测&#xff0c;家庭网络平均18M的速率

【因果推断python】21_匹配2

目录 匹配估计器 匹配估计器 子分类估计器在实践中用得不多&#xff08;我们很快就会明白为什么&#xff0c;主要是因为维度诅咒这个原因&#xff09;&#xff0c;但它让我们很好地、直观地了解了因果推理估计器应该做什么&#xff0c;以及它应该如何控制混淆因素。这使我们能…

python--装饰器

[掌握]装饰器入门 语法糖 目标&#xff1a;掌握装饰器的快速使用。 装饰器本质上就是闭包&#xff0c;但装饰器有特殊作用&#xff0c;那就是&#xff1a;在不改变原有函数的基础上&#xff0c;给原有函数增加额外功能。 定义装饰器&#xff1a; def outer([外面参数列表]):…

kafka-消费者-消费异常处理(SpringBoot整合Kafka)

文章目录 1、消费异常处理1.1、application.yml配置1.2、注册异常处理器1.3、消费者使用异常处理器1.4、创建生产者发送消息1.5、创建SpringBoot启动类1.6、屏蔽 kafka debug 日志 logback.xml1.7、引入spring-kafka依赖1.8、消费者控制台&#xff1a;1.8.1、第一次启动SpringK…

Linux环境---在线安装jdk

Linux环境—在线安装jdk 一、使用步骤 1.安装环境 JDK版本&#xff1a;1.8 1.1 建立存放软件的目录 注意&#xff1a;此处本人是将需要按照的软件存放在directory目录下&#xff0c;可根据实际情况调整接收路径。 命令如下&#xff1a; mkdir directory2.安装jdk 2.1 建…

【YOLOv8改进[CONV]】SPDConv助力YOLOv8目标检测效果 + 含全部代码和详细修改方式 + 手撕结构图

本文将使用SPDConv助力YOLOv8目标检测效果的实践,文中含全部代码、详细修改方式以及手撕结构图。助您轻松理解改进的方法。 改进前和改进后的参数对比: 目录 一 SPDConv 二 SPDConv助力YOLOv8目标检测效果 1 整体修改 ① 添加SPDConv.py文件 ② 修改ultralytics/nn/tas…

Vue-插槽 Slots

文章目录 前言什么叫插槽简单插槽指定默认值多个插槽根据父级别名称指定区域显示(具名插槽)作用域插槽 前言 本篇文章不做过多的讲解与说明&#xff0c;只记录个人实验测试案例。 详见&#xff1a;vue 官方文档 插槽 slots 什么叫插槽 之前的博客中&#xff0c;父级组件可以…

【简单讲解下TalkingData】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

vite+ts设置别名

准备工作 安装 types/node 避免代码爆红 npm install types/node一、根目录下 vite.config.ts 文件中配置 import { resolve } from path;resolve: {// 设置文件./src路径为 alias: [{find: ,replacement: resolve(__dirname, ./src)}] }二、根目录下 tsconfig.json 文件中配…

【MySQL】数据库入门基础

文章目录 一、数据库的概念1. 什么是数据库2. 主流数据库3. mysql和mysqld的区别 二、MySQL基本使用1. 安装MySQL服务器在 CentOS 上安装 MySQL 服务器在 Ubuntu 上安装 MySQL 服务器验证安装 2. 服务器管理启动服务器查看服务器连接服务器停止服务器重启服务器 3. 服务器&…

美团发布2024年一季度财报:营收733亿元,同比增长25%

6月6日&#xff0c;美团(股票代码:3690.HK)发布2024年第一季度业绩报告。受益于经济持续回暖和消费复苏&#xff0c;公司各项业务继续取得稳健增长&#xff0c;营收733亿元(人民币&#xff0c;下同)&#xff0c;同比增长25%。 财报显示&#xff0c;一季度&#xff0c;美团继续…

$MPC 登录MEXC,加速Partisia Blockchain 生态市场进程

Partisia Blockchain是一个以MPC技术方案为基础&#xff0c;具备可审计特性的隐私Layer1生态&#xff0c;与此同时&#xff0c;该链通过系列创新的系统架构&#xff0c;能够兼顾高迸发、安全、可拓展性以及可互操作特性。基于系列技术特性&#xff0c;Partisia Blockchain正在构…

Mybatis 查询TypeHandler使用,转译查询数据(逗号分隔转List)

创建自定义的Hanndler /*** Package: com.datalyg.common.core.handler* ClassName: CommaSeparatedStringTypeHandler* Author: dujiayu* Description: 用于mybatis 解析逗号拼接字符串* Date: 2024/5/29 10:03* Version: 1.0*/ public class CommaSeparatedStringTypeHandle…

【重学C语言】十八、SDL2 图形编程介绍和环境配置

【重学C语言】十八、SDL2 图形编程介绍和环境配置 **SDL2介绍**SDL 2用途SDL 在哪些平台上运行&#xff1f;下载和安装 SDL2安装 SDL2 clion 配置 SDL2 SDL2介绍 SDL2&#xff08;Simple DirectMedia Layer 2&#xff09;是一个开源的跨平台多媒体开发库&#xff0c;主要用于游…

LabVIEW冲击响应谱分析系统

LabVIEW冲击响应谱分析系统 开发了一种基于LabVIEW开发的冲击响应谱分析系统&#xff0c;该系统主要用于分析在短时间内高量级输入力作用下装备的响应。通过改进的递归数字滤波法和样条函数法进行冲击响应谱的计算&#xff0c;实现了冲击有效持续时间的自动提取和响应谱的精准…

JVM学习-Arthas

Arthas Alibaba开源的Java诊断工具&#xff0c;在线排查问题&#xff0c;无需重启&#xff0c;动态跟踪Java代码&#xff0c;实时监控JVM状态Arthas支持JDK6&#xff0c;支持Linux/Mac/Windows&#xff0c;采用命令行交互模式&#xff0c;同时提供丰富的Tab自动补全功能&#…

数据资产入表-数据治理-指标建设标准

前情提要&#xff1a;数据价值管理是指通过一系列管理策略和技术手段&#xff0c;帮助企业把庞大的、无序的、低价值的数据资源转变为高价值密度的数据资产的过程&#xff0c;即数据治理和价值变现。上一讲介绍了标签标准设计的基本逻辑和思路。数据资产入表-数据治理-标签设计…