C++ 左值引用与右值引用超详解

news2024/9/20 13:06:46

目录

一 左值与右值

        1.左值

        2.右值

        3.总结

二 左值引用与右值引用

1.左值引用

2.右值引用

3.总结与探究

  3.1右值引用可以修改么?取地址么?

   3.2左值引用与右值引用转化

左值引用 引用 右值

右值引用 引用 左值

    3.3左值引用与右值引用相同之处

    3.4左值引用与右值引用不同之处

  三 引用用处

    1.左值引用

     2.右值引用

        2.1移动构造

2.2移动赋值

2.3完美转发

     3.总结


一 左值与右值

        1.左值

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

        如下都c,p,b为左值。

int c = 0;
const int b = 2;
int* p = nullptr;

        2.右值

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

        以下都为右值

int fun()
{
	return 1;
}
void test()
{
	int a = 1, b = 2;
	//右值
	10;
	a + b;
	fun();
}

        如下图,将右值放在=左边就会报错,对其取地址也会报错

        3.总结

        左值一般为我们自己定义的变量,在定义时开辟了内存,我们可以对这块内存赋值,修改内存中的值,如果有const也仅从语法层面上不允许修改,这块内存在其生命周期结束的时候销毁。

        右值一般为临时变量,是程序运行时产生的中间产物,他不是我们用户自己定义开辟空间的,是由编译器帮我们开辟空间,并且在用完就立即销毁。右值的生命周期一般只在当前语句,当我们要对右值进行赋值时,他已经释放空间了,此时我们再进行访问就是野访问,(与野指针一样造成内存问题),所以我们不能对右值进行修改,编译器强制语法检查,遇到修改操作就报错。

        右值中特殊的就是字面常量,他们存储在内存的常量区,内存为只读属性,不可以修改,不可以取地址,他们的生命周期与程序的生命周期一样,当我们使用字面常量时,编译器帮我们开辟空间,并用字面常量初始化,这块临时空间用完即销。

        分辨左右值最常用方法就是判断他是否可以取地址。如果不可以就为右值。

二 左值引用与右值引用

       

1.左值引用

        左值引用就是给左值的引用,给左值取别名。

int c = 0;
const int b = 2;
int* p = nullptr;

//左值引用
int& lc = c;
const int& lb = b;
int*& lp = p;

        在int& lc = c;语句后,lc与c使用的是同一块空间,lc与c完全等效,一荣俱荣,一损俱损。

2.右值引用

        右值引用就是对右值的引用,给右值取别名。他的语法与左值引用十分相似,右值引用语法为对应类型加上两个& 。

int fun()
{
	return 1;
}
void test()
{
	int a = 1, b = 2;

	//右值引用
	int && r1=10;
	int && r2=(a + b);
	int && r3=fun();
}

        如果改为左值引用就会报错,如下图。

3.总结与探究

        首先明确一点就是不管是左值引用还是右值引用,都是给一段空间取别名。对于左值引用而言,他与绑定的变量共用同一块空间,是之前我们定义该变量(左值)开辟的空间,没有再开辟出新内存。       

        对于右值引用而言,他也没有开辟内存,他绑定的一块空间是编译器运行时为右值自动开辟的空间,没有为右值引用再开辟出新内存。

        在这里有个矛盾?右值引用引用干了什么?如果按照编译器对于普通右值的处理,当前语句结束就销毁,那么右值引用指向的空间就是野空间(参照指针叫法),我们对右值引用修改就会造成内存泄漏等重大内存问题,那么这样不就是脱裤子放屁了么!

        显然右值引用语法规定不是这样的,(以该句为例int && r3=fun();)当我们用r3引用fun()这个右值的时候,这块空间原本是由编译器释放销毁的,但被人为的抢走释放权限,加上右值引用这句话,就相当于说明我认为这块空间还有利用的价值,你编译器先别着急释放,交给我来释放,这里水很深,交给我来把握。我们就拥有了这块空间的生杀大权,编译器就不会立即释放该内存

  3.1右值引用可以修改么?取地址么?

        右值是一定不可以修改和取地址的,那么右值引用可以么?注意右值引用和右值是两个东西,只有左值引用和左值才一样,左值引用可以修改和取地址。

        实践是检验真理的唯一标准,上代码。

int main()
{

	//右值引用
	int&& r1 = 10;
	
	cout << r1 << endl;
	cout << (void *) & r1 << endl;
	r1 = 20;
	cout << r1 << endl;

	return 0;
}

        运行结果如下。右值引用时可以修改的,也可以取地址。

        其实当执行完int&& r1 = 10;语句后他就与int c = 10;int& r1 = c;这两句的效果一样,到这里可能很多人被饶了进去,我们来捋一捋。

        为什么右值不可以修改?(取地址实际上也是为了后面修改操作)因为右值当当前语句结束就销毁,后面语句再访问就是访问野空间。

        为什么右值引用可以修改?我们从编译器手中拿走了右值销毁的权利,“右值”在引用后依旧存在,可以进行正常的访问修改操作。

        左值引用和左值使用的都是用户自定义开辟的空间,所以左值引用支持修改。

        我们可以通过一些汇编了解左右值引用

        通过上图我们可以更好的理解这句话。不管是左值引用还是右值引用,都是给一段数据取别名。

   3.2左值引用与右值引用转化

        左值引用 引用 右值

        首先如下代码是正常的定义右值引用,是没有任何问题的,那么我们可以使用左值引用么?

int&& ra = 10;

        如下代码,直接使用左值是错误的,编译器报错。通过上述的汇编图我们知道左值引用与右值引用最后都转化为指针,那么左值引用与右值引用的语法就是为了通过编译器的语法检测,编译器检测右值不支持修改操作,但一般的左值引用支持修改,我们就可以加上const,禁止修改,骗过编译器检测。

int& la = 10;

        如下图。直接使用左值引用是会报错的,加上const修饰就可以通过编译器语法检查

       将上述右值引用修改便可以通过编译

int fun()
{
	return 1;
}
void test()
{
	int a = 1, b = 2;

	//左值引用
	const int& r1 = 10;
	const int& r2 = (a + b);
	const int& r3 = fun();
}
右值引用 引用 左值

         首先如下代码是正常的定义左值引用,是没有任何问题的,那么我们可以使用右值引用么?

int c = 0;
//左值引用
int& lc = c;

        

        直接写如上图是肯定不可以的,编译器会进行类型检查,类型不匹配就报错。我们就可以通过强制类型转换骗过编译器。如下图,此时就不会有报错了。

        如下都是可以通过编译的。

int c = 0;
const int b = 2;
int* p = nullptr;

//右值引用
int&& lc = (int&&)c;
const int&& lb = (const int&&)b;
int*&& lp = (int*&&)p;

        并且在右值引用后,lc与c用的是同一块空间,此时仿佛lc是左值引用一样。我们可以通过如下代码检测。

int main()
{
	int c = 0;
	//右值引用
	int&& lc = (int&&)c;
	cout << c <<" " << lc << endl;

	c = 1;
	cout << c <<" " << lc << endl;

	lc = 112;
	cout << c <<" " << lc << endl;


	return 0;
}

        通过上述代码验证,我们就可以断定他们用的是同一块空间。此时的右值引用就可以看为左值引用,二者等效。

        看到大家这都会十分疑惑,C++11更新右值引用干什么?绕来绕去,闲的没事么?其实不尽然,C++这样设计是为了提高效率,减少深拷贝的消耗,在第三大模块再细谈。

    3.3左值引用与右值引用相同之处

       1.都是引用,都是再给一段空间取别名

        2.除了const修饰外,左值引用与右值引用都可以修改。

        3.底层都是指针,语法层面不同罢了

     3.4左值引用与右值引用不同之处

        1.左值引用引用左值,左值可以修改;右值引用引用右值,右值不可以修改

        2.左值是由用户自定义变量组成,右值一般为编译器运行时开辟临时变量。

  三 引用用处

        1.左值引用

               通过上图的汇编代码,我们可以知道引用有和指针同等的效率,那么在函数设计传递参数的时候,我们就可以传递引用来代替指针,如下代码。

void Print1(vector<int> & t)
{

}
void Print2(vector<int>* pt)
{

}

int main()
{
	vector<int> t1(10);
	Print1(t1);

	Print2(&t1);


	return 0;
}

        用左值引用代替指针后,不仅用起来更方便,在传参时不需要加上&,使用时不用加上*,并且引用比指针更加的安全。如果在 Print2中修改pt的值,就会造成野指针问题,而vector<int> & t就相对而言更加的安全了。

     2.右值引用

2.1移动构造

        为了下面举例现象明显,可以简单实现string类,注意关注构造函数,赋值重载函数,to_string函数。

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<assert.h>
using namespace std;

//MyString
namespace MS
{
	class string
	{
	public:
		const  size_t npos=-1;

		//构造函数
		string()
		{
			cout << "string():" << endl;
			_size = 0;
			_capacity = 0;
			_str = new char[1] {'\0' };
		}
		string(const char* str)
		{
			cout << "string(const char* str):" << endl;
			int sz = strlen(str);
			_str = new char[sz + 1];
			strcpy(_str, str);
			_size = sz;
			_capacity = sz;
		}

		string(const string& s)
		{
			cout << "string(const string& s):" << endl;
			_str = new char[s._size + 1];
			strcpy(_str, s._str);
			_size = s._size;
			_capacity = s._capacity;
		}

		

		//析构函数
		~string()
		{
			delete[] _str;
			_str = nullptr;
		}

		string& operator=(const string& s)
		{
			cout << "operator=(const string& s):" << endl;
			if (this != &s)
			{
				delete[] _str;
				_str = new char[s._size + 1];
				strcpy(_str, s._str);
				_size = s._size;
				_capacity = s._capacity;
			}

			return *this;
		}

		string& operator+= (const string& str)
		{
			int len = str._size;
			reserve(_size + len);

			strcpy(_str + _size, str.c_str());
			_size += len;
			return *this;
		}
		const char* c_str()const
		{
			return _str;
		}
		string& operator+= (const char* s)
		{
			int len = strlen(s);
			reserve(_size + len);

			strcpy(_str + _size, s);
			_size += len;
			return *this;
		}
		string& operator+= (char c)
		{
			reserve(_size + 1);
			_str[_size] = c;
			_str[_size + 1] = '\0';
			_size++;
			return *this;
		}
		void reserve(size_t n)
		{
			if (n <= _capacity)
				return;

			char* tmp = new char[n + 1];
			strcpy(tmp, _str);
			delete[]_str;
			_str = tmp;
			_capacity = n;
		}
		//容量
		size_t size()const
		{
			return _size;
		}
		size_t capacity()const
		{
			return _capacity;
		}

		void swap(string& str)
		{
			std::swap(_str, str._str);
			std::swap(_size, str._size);
			std::swap(_capacity, str._capacity);
		}

		friend string to_string(int value);
		friend ostream& operator<< (ostream& os, string& str);
	private:
		int _size=0;
		int _capacity=0;
		char* _str=nullptr;
	};

	string to_string(int value)
	{
		bool flag = true;
		if (value < 0)
		{
			flag = false;
			value = 0 - value;
		}
		string str;

		int d = 0;
		while (value > 0)
		{
			int x = value % 10;
			value /= 10;
			str += ('0' + x);
			d++;
		}

		if (flag == false)
		{
			str += '-';
		}

		for (int i = 0; i < d / 2; i++)
			swap(str._str[i], str._str[str._size - i - 1]);

		return str;
	}

	ostream& operator<< (ostream& os, string& str)
	{
		for (int i = 0; i < str.size(); i++)
		{
			os << str._str[i];
		}
		return os;
	}
}

        假如我们运行下述代码。

int main()
{

	MS::string s1 = MS::to_string(123);
	cout << s1<<endl;

	return 0;
}

         MS::to_string(123)返回的是string类型临时对象,然后s2拷贝构造这个临时对象完成初始化。如下图

        由上图可以知道我们为了初始化s2创建了两次str变量,一次是在to_string函数中,一次是为函数返回值创建,这是完全不优化的情况,事实上现在的编译器会有优化,认为临时变量可以省去,可以将为返回值创建str改为直接创建s2,减少依次拷贝消耗,如下图。

        上面这种还是比较可以理解的优化,但VS2022开的优化更大更叹为观止,即使上面优化后,我们任然要建立两次变量,消耗两次初始化的时间,但VS2022把s2与to_string中的str优化为一个变量,这样就只用建立一个变量如下图。

        这里调用的是str的构造函数,VS将s2与str优化成一个变量,故只有一次构造函数。

        VS2022的优化程度十分大,如果想运行完全不优化的编译器,可以在linux下添加额外指令运行,如下

命令为:其中a.cpp 为源文件名,-o 后面的aobj是你命名的文件名字

g++ -fno-elide-constructors Teacher.cpp -o aobj

        经管编译器优化做的很好,但是这不是语言本身控制的,不同的编译器对上述代码的优化不同,很少像VS2022优化这么大,C++11就引入了右值引用的语法用来优化上述的代码。

        回到最初的代码。MS::to_string(123);返回值按照我们之前讲的就是右值,在C++11中规定如果函数返回值是函数体内将要销毁的变量,那么这个将要销毁的变量是右值,叫做将亡值。 那么我们就可以根据右值类型增加个构造函数。

int main()
{

	MS::string s1 = MS::to_string(123);
	cout << s1<<endl;

	return 0;
}

        如下代码。我们就在构造函数中直接掠夺右值引用的资源,交换后就相当于初始化完了s2变量,我们称这种特殊的构造函数叫做移动构造。

string( string&& s)
{
	cout << "string( string&& s):移动构造" << endl;
	swap(s);
}

        加上移动构造后,上述代码运行结果如下。这看起来我们创建了两次string变量,实际上我们只消耗了创建一个str变量的时间,第二次创建只是交换资源,代价极小。这样不管编译器做不做优化,根据语法我们就可以优化上述的情况,提高效率

        在上述移动构造中的右值引用 s的使用与左值引用一模一样,我们称之为退化,即在string( string&& s)函数中相当于使用string& s左值引用。

2.2移动赋值

        显然我们不只会有构造时候有右值,=赋值的时候也会出现右值,这样我们就可以像上面重载构造函数一样,重载赋值函数,提高效率。

int main()
{

	MS::string s2;
	s2 = MS::to_string(123);
	cout << s2;

	return 0;
}

        上述代码运行结果如下

        经过编译器的优化后,我们任然需要消耗创建两个个str变量的时间,此时就可以重载右值版本的赋值重载函数如下。

string& operator=(string&& s)
{
	cout << "operator=(string&& s):移动赋值" << endl;
	swap(s);

	return *this;
}

        运行结果如下。这样看起来还是消耗创建两个个str变量的时间,但我们知道移动赋值只交换了资源,消耗极小。于是就相当于提高了效率。

2.3完美转发

        首先了解一个std中函数move,他的作用是将参数类型强制转换为右值,不管参数是左值还是右值。

        运行如下代码

int main()
{

	MS::string s1("1234");

	MS::string s2(s1);

	MS::string s3(move(s1));
	


	return 0;
}

        如下分析

        现有如下模板,C++11规定T&&可以接受右值引用也可以接受左值引用,也就是说t可以为右值也可以为左值。

template<typename T>
void PerfectForward(T&& t)
{

}

        如下代码是可以正常运行的。结果如下图。


template<typename T>
void PerfectForward(T&& t)
{
	cout << t << endl;
}

int main()
{
	PerfectForward(10);           // 右值
	int a = 1;
	PerfectForward(a);            // 左值
	
	return 0;
}

        经过之前我们看的汇编代码我们也可以更好的理解这种用法,不管左值引用还是右值引用最后都转化为指针处理,只要强制转换或者加上const就可以骗过编译器。

        接着运行下述代码

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=1;
	PerfectForward(a);            // 左值
	PerfectForward(std::move(a)); // 右值
	const int b = 8;
	PerfectForward(b);      // const 左值
	PerfectForward(std::move(b)); // const 右值
	return 0;
}

        运行结果如下。我们最后打印的都是左值,只是由于右值引用在绑定右值后就退化为左值引用,编译器将t都识别为左值引用。绑定后的右值引用和左值引用没什么区别,都可以赋值都可以取地址,编译器不会主动的区分他们。

        为了解决上述的情况,就引入了完美转发概念,即在传递过程中保持引用的特性,即右值引用不退化。引入forward函数,如果为左值什么都不办,为退化后的右值,进行一次move

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

    

        对于左值,模板相当于实例化出如下代码。

int a=1;
PerfectForward(a);            // 左值
void PerfectForward(T&& t)
{
	Fun((t));
}

        对于右值,模板相当于实例化出如下代码。在传递中保持右值属性。

PerfectForward(std::move(a)); // 右值
void PerfectForward(T&& t)
{
	Fun(move(t));
}

        加上这句代码后就可以正常运行,如下图

3.总结

        C++11引入右值引用的概念就是为了提高效率解决一些特殊场景下的问题,不管是右值引用还是左值引用都是再给一段空间取别名,只不过这段空间分为用户主动开辟与运行时临时变量两种。

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

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

相关文章

MySQL基础:函数

&#x1f48e;所属专栏&#xff1a;MySQL 函数是指一段可以直接被另一段程序调用的程序或代码&#xff0c;在MySQL中也内置了许多函数供开发者去调用&#xff0c;例如之前提到的聚合函数&#xff0c;本节再去介绍一些其他常用的函数 字符串函数 函数功能CONCAT(S1,S2...Sn)字…

开源的量化交易领域平台vn.py(VeighNa)

一&#xff1a;vn.py&#xff08;VeighNa&#xff09;下的工具以及社区版和Elite版的区别 vn.py是一款广泛应用于量化交易领域的开源软件&#xff0c;它主要有以下用途和功能&#xff1a; 1. 交易系统开发框架&#xff1a;vn.py提供了一个完整的交易系统开发框架&#xff0c;可…

桶排序算法及优化(java)

目录 1.1 引言 1.2 桶排序的历史 1.3 桶排序的基本原理 1.3.1 工作流程 1.3.2 关键步骤 1.4 桶排序的Java实现 1.4.1 简单实现 1.4.2 优化实现 1.4.3 代码解释 1.5 桶排序的时间复杂度 1.5.1 分析 1.5.2 证明 1.6 桶排序的稳定性 1.7 著名案例 1.7.1 应用场景 …

基于GPT-SoVITS的API实现批量克隆声音

目标是将每一段声音通过GPT-SoVITS的API的API进行克隆,因为拼在一起的整个片段处理会造成内存或者缓存溢出。 将目录下的音频文件生成到指定目录下,然后再进行拼接。 通过AI工具箱生成的数据文件是这样的结构,temp目录下是没个片段生成的部分,connect_是正常拼接的音频文件…

笨鸟先飞(疯狂的小鸟)小游戏自制分享

《Flappy Bird》是一款由越南独立游戏开发者阮哈东&#xff08;Dong Nguyen&#xff09;制作并发布的移动端小游戏。该游戏最初于2013年上线&#xff0c;在2014年初迅速走红&#xff0c;成为全球范围内的热门现象。 游戏的玩法非常简单&#xff0c;玩家只需通过点击屏幕来控制…

Python | Leetcode Python题解之第355题设计推特

题目&#xff1a; 题解&#xff1a; class Twitter:class Node:def __init__(self):self.followee set()self.tweet list()def __init__(self):self.time 0self.recentMax 10self.tweetTime dict()self.user dict()def postTweet(self, userId: int, tweetId: int) ->…

基于人工智能、三维视觉、混合现实等技术的智慧能源开源了

一、简介 AI视频监控平台, 是一款功能强大且简单易用的实时算法视频监控系统。愿景在最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;减少企业级应用约 95%的开发成本&#xff0c;在强大视频算…

AI学习记录 - LSTM详细拆解

拒绝熬夜&#xff0c;一点点写&#xff0c;拆解LSTM计算过程和最后的总结 遗忘门的计算流程 拼接词向量&#xff0c;前面来的&#xff0c;现在输入的 然后进行计算&#xff1a;

浅谈移动端车牌识别技术的实现过程及应用场景

随着移动互联技术的飞速发展和智能设备的普及&#xff0c;Android、iOS平台上的车牌识别技术逐渐成熟并广泛应用于各个领域。该技术通过智能手机的摄像头捕捉车牌图像&#xff0c;利用先进的图像处理与机器学习算法&#xff0c;实现车牌号码的自动识别。相比传统的人工录入或固…

opencv中Core中的Norm函数解释

1. Norm的类型 NORM_L1&#xff1a; L1 范数&#xff08;曼哈顿范数&#xff09;。数组中所有元素绝对值之和。 NORM_L2&#xff1a; L2 范数&#xff08;欧几里得范数&#xff09;。数组中所有元素平方和的平方根。 NORM_INF&#xff1a;无穷范数&#xff08;最大绝对值范数&…

Nginx的7大调度算法详解

Nginx的7大调度算法详解 一、Sticky二、Round-Robin&#xff08;RR&#xff09;三、Weight四、Least_conn五、IP_hash六、Fair七、URL_hash总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; Nginx作为一款高性能的HTTP和反向代理服务器&a…

Linux虚拟机磁盘管理-添加磁盘

添加磁盘--添加前请选关闭虚拟机 添加步骤&#xff1a; 1.编辑虚拟机设置 2.选择硬盘 3.选择SCSI 4.创建新虚拟磁盘 5.设置磁盘大小 6.点击完成 开机的时候会去读取有几块硬盘&#xff0c;总共我们是有4块硬盘&#xff0c;sda\sdb\sdc\sdd 注意&#xff1a;新加的硬盘实际我们…

VScode相关使用、配置

VScode 拉取新分支 点击左下角分支会出现这个 选择创建新分支依据… 选择一个分支为从这个分支拉新分支 输入新分支的名称即可 VScode 合并分支 切到最终要合并到的分支&#xff0c;通过快捷键 shiftctrlp 出现框中 &#xff0c;选择 git 合并分支 选择要合并过来的分…

【Docker】Docker Consul

docker consul Docker Consul 是一个用于服务发现和配置的开源工具&#xff0c;它是 HashiCorp 公司推出的一个项目。Consul 提供了一个中心化的服务注册和发现系统&#xff0c;可以帮助开发人员轻松地在 Docker 容器和集群之间进行服务发现和配置管理。 Consul 使用基于 HTT…

位运算使用

在写代码过程中&#xff0c;适当的位运算是一种提高代码质量的有效手段。 0 位运算 常用的运算符共 6 种&#xff0c;分别为按位与&、按位或|、按位异或^、按位取反~、左移位<<、右移位>>。 0.1 按位与&、按位或|、按位异或^ 按位与&、按位或|、按…

MySQL中处理JSON数据:大数据分析的新方向,MYSQL如何处理JSON数据,参数讲解+实战案例+全网最全

1-3章理论为主&#xff0c;如果想直接看实战和MySQL如何操作JSON可以直接看第4章。 感谢您的观看&#xff0c;如果您喜欢这篇文章或者对您有所帮助的话&#xff0c;动动发财的小手点点关注&#xff0c;一起学习一起进步 第一章 引言 1.1 研究背景与意义 随着大数据技术的迅猛…

回归预测|基于北方苍鹰优化支持向量机的数据回归预测Matlab程序NGO-SVM 多特征输入单输出 高引用先用先创新

回归预测|基于北方苍鹰优化支持向量机的数据回归预测Matlab程序NGO-SVM 多特征输入单输出 高引用先用先创新 文章目录 前言回归预测|基于北方苍鹰优化支持向量机的数据回归预测Matlab程序NGO-SVM 多特征输入单输出 高引用先用先创新 一、NGO-SVM 模型1. 北方苍鹰优化算法&#…

vue3+ts封装axios以及解决跨域问题

目录 一、前言二、封装axios三、 解决跨域四、调用接口五、运行结果 一、前言 前端请求后端数据时&#xff0c;会用到axios&#xff0c;但是如果不将axios封装好&#xff0c;会导致代码冗余 二次封装的好处如下&#xff1a; 求头能统一处理便于接口的统一管理解决回调地狱配置…

rust api接口开发(以登陆和中间件鉴权为例)

rust rest api接口开发 所需依赖 axumtokioredis cargo add axum redis cargo add tokio --featuresfull路由服务创建和运行 //子路由 let v1router axum::Router::new(); //主路由,并将子路由绑定到主路由 let routeraxum::Router::new().nest("/v1",v1router)…

Zabbix6.4监控Windows上的GPU使用率

背景&#xff1a;一台Windows物理机上装了英伟达的GPU显卡&#xff0c;业务需要实时监控它的使用率。 1、确认nvidia-smi命令可用 2、命令查询相关使用情况 3、服务器上部署zabbix-agent 提前下载好包上传&#xff0c;路径自行修改 C:\Users\Administrator>C:\zabbix_age…