C++11的更新介绍(初始化、声明、右值引用)

f62e50faa284456a8e3dd2569bed4a49.jpeg

🪐🪐🪐欢迎来到程序员餐厅💫💫💫

          主厨:邪王真眼

主厨的主页:Chef‘s blog  

所属专栏:c++大冒险

总有光环在陨落,总有新星在闪烁

C++11小故事:

       1998年是C++标准委员会成立的第一年,本来计划以后每5年视实际需要更新一次标准C++国际标准委员会在研究C++ 03的下一个版本的时候,一开始计划是2007年发布,所以最初这个标准叫 C++ 07。但是到06年的时候,官方觉得2007年肯定完不成C++ 07,而且官方觉得2008年可能也 完不成。最后干脆叫C++ 0x。x的意思是不知道到底能在07还是08还是09年完成。结果2010年的时候也没完成,最后在2011年终于完成了C++标准。最终定名为C++11。( 包含了约140个新特性,以及对C++03标准中约600个缺陷的修正

 统一的列表初始化

{}初始化

在C++98中,标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定。
struct Point
{
 int _x;
 int _y;
};
int main()
{
 int array1[] = { 1, 2, 3, 4, 5 };
 Point p = { 1, 2 };
 return 0;
}

         C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和自定义的类型,他的本意或许是想给所有变量一个统一的初始化方案使用初始化列表时,可添加“=”,也可不添加)

  • 对内置类型:


int x2{ 2 };
int x2={2};
 int array1[]{ 1, 2, 3, 4, 5 };
 double y={1.0};
double y{1.0};
 Point p{ 1, 2 };
 // C++11中列表初始化也可以适用于new表达式中
 int* pa = new int[4]{ 0 };
  • 对于结构体:

struct Point
{
 int _x;
 int _y;
};
int main()
{
struct Point p{1,0};
struct Point{1,0};
}
  •  创建对象时使用列表初始化调用构造函数初始化

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); // old style
 // C++11支持的列表初始化,这里会调用构造函数初始化
 Date d2{ 2022, 1, 2 };
 Date d3 = { 2022, 1, 3 };
 return 0;
}

 注意事项:

  1. d1是调用了构造函数
  2. d2是c++11的新初始化并且直接调用构造函数
  3. d3:{2022,1,3}先发生隐式类型转换,通过调用构造函数生成一个类,在以拷贝构造的方式生成d3,证明方法:就是用explicit修饰构造函数(会封锁他的隐类类型转换)就会导致报错(如下图所示)158b8c5e8f8c4e0a9920e7aacc66fd41.png

 std::initializer_list

typeid是操作符,不是函数。运行时获知变量类型名称,可以使用 typeid(变量).name()

  • 1.std::initializer_list是什么类型:

int main()
{
	// the type of il is an initializer_list 
	auto il = { 10, 20, 30 };
	cout << typeid(il).name() << endl;
	return 0;
}
此时的il就是initializer
ead0b569e8434154a2a1b521f3dbdfe3.png
  • 2.std::initializer_list使用场景

之前把给vector初始化多个值时有两种方法,用 造一个数组然后用迭代器或者 一直push_back
vector<int> v;
for (int i = 0; i < 5; i++)
{
    v.push_back(arr[i]);
}
C++11对STL中的不少容器就增加std::initializer_list作为参数的构造函数,这样初始化容器对象就更方便了。也可以作为operator=的参数,这样就可以用大括号赋值。

现在就可以直接用initize_list数据初始化了

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

其实这个新类型就是一个类模板, 

ed6532abdb9142d3af0501f35dfb752b.png

他的接口也是十分简单

91d80e9eac34459a9295b5d48e561cf7.png

  1. constructor:构造函数
  2. size:表示长度,
  3. begin,end:迭代器

initializer_list本质上是一个通过迭代器访问数组的容器。当其它容器(vector)通过initializer_list构造自己,其实就该容器遍历initize_list的迭代器,从而把里面的元素一个一个插入

让模拟实现的vector也支持{}初始化和赋值

template<class T>
class vector {
public:
     typedef T* iterator;
     vector(initializer_list<T> l)
     {
         _start = new T[l.size()];
         _finish = _start + l.size();
         _endofstorage = _start + l.size();
         iterator vit = _start;
         typename initializer_list<T>::iterator lit = l.begin();
         while (lit != l.end())
         {
             *vit++ = *lit++;
         }
     }
     vector<T>& operator=(initializer_list<T> l) {
         vector<T> tmp(l);
         std::swap(_start, tmp._start);
         std::swap(_finish, tmp._finish);
         std::swap(_endofstorage, tmp._endofstorage);
         return *this;
     }
private:
     iterator _start;
     iterator _finish;
     iterator _endofstorage;
 };

声明

auto

在C++98中auto是一个存储类型的说明符,表明变量是局部自动存储类型,但是局部域中定义局
部的变量默认就是自动存储类型,所以auto就没什么价值了。C++11中废弃auto原来的用法,将
其用于实现自动类型推断。
  • 此时的p就是int*
	int i = 10;
	auto p = &i;
	cout<<typeid(i).name()<<endl;
	cout << typeid(p).name();

738ea5e397e9453e9a7fcbf84ef54d80.png

  • auto帮我们省去了写冗长的变量类型名的麻烦
auto pf = strcpy;
map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };
//map<string, string>::iterator it = dict.begin();
auto it = dict.begin();

注意事项:

  1. auto 定义变量必须在定义时初始化
  2. 函数形参以及返回值不能用auto

    decltype

decltype可以检测一个变量或表达式的类型,并且拿这个类型去声明新的类型

	int a = 1;
	double b = 1.0;
	cout<<typeid(decltype( a * b)).name();

6e1ca2647f4348d7a24866be125a249d.png

  • decltype的一些使用使用场景
	int a = 1;
	double b = 1.0;
	decltype(a * b) c = 1;
	cout<<typeid(c).name();

836dd14bfb8c4693a08293171c4ca1e1.png


nullptr

在C/C++编程习惯中,如果一个指针没有合法的指向,我们基本都是按照如下 方式初始化:

void TestPtr()
{
int* p1 = NULL;
int* p2 = 0;
// ……
}

NULL实际是一个宏,在传统的C头文件(stddef.h)中,可以看到如下代码:

#ifndef NULL
#ifdef __cplusplus
#define NULL   0
#else
#define NULL   ((void *)0)
#endif
#endif

可以看到,NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量。不论采取何

种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦,比如

void f(int)
{
 cout<<"f(int)"<<endl;
}
void f(int*)
{
 cout<<"f(int*)"<<endl;
}
int main()
{
 f(0);
 f(NULL);
 f((int*)NULL);
 return 0;
}

f4d199be65b64b8489e3748f9126057d.png

程序本意是想通过f(NULL)调用指针版本的f(int*)函数,但是由于NULL被定义成0,因此与程序的

初衷相悖。

在C++98中,字面常量0既可以是一个整形数字,也可以是无类型的指针(void*)常量,但是编译器

默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void

*)0。

注意:

  • 1. 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptrC++11作为新关键字引入
  • 2. C++11中,sizeof(nullptr) sizeof((void*)0)所占的字节数相同。
  • 3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。 

    范围for循环

        这个我们在前面的课程中已经进行了非常详细的讲解,这里就不进行讲解了,请参考C++入门
+STL容器部分的博客。

 右值引用和移动语义

 

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

 左值和右值

  • 左值

左值是一个表示数据的表达式(如变量名或解引用的指针), 我们可以获取它的地址+可以对它赋
值,左值可以出现赋值符号=的左边,右值不能出现在赋值符号左边。定义时const修饰符后的左
值,不能给他赋值,但是可以取它的地址。左值引用就是给左值的引用。
// 以下的p、b、c、*p都是左值
int* p = new int(0);
int b = 1;
const int c = 2;

省流:左值显著的特征是可以取地址,但是不一定可以被修改。

  • 右值

右值也是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值(这个不能是左值引
用返回)等等, 右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能
取地址,给右值取别名后,会导致右值被存储到特定位置,且可以取到该位置的地址,例如:不能取字面量10的地址,但是rr1引用后,可以对rr1取地址,也可以修改rr1。如果不想rr1被修改,可以用const int&& rr1 去引用
int main()
{
double x = 1.1, y = 2.2;
// 以下几个都是常见的右值
10;
x + y;
fmin(x, y);
// 这里编译会报错:error C2106: “=”: 左操作数必须为左值
10 = 1;
x + y = 1;
fmin(x, y) = 1;
return 0;
}

 省流:右值显著的特征是不可以取地址,不可以被修改。

简单辨析了什么是左值,什么是右值,现在我们知道左值与右值的最大区别在于可不可以取地址


右值引用

对左值的引用就是左值引用

我们最开始学的引用就是左值引用,不记得的同学可以去看看我写的这篇博客


c++入门知识

  • 右值引用语法

ok,现在假定看到这的老铁都已经知晓了左值引用
右值引用的语法是:类型名&&
int x = 1, y = 1;
int&& rr1 = 10;
double&& rr2 = x + y;
double&& rr3 = fmin(x, y);
  •  左值引用可以引用右值吗

  1. 左值引用能引用左值,一般不能引用右值。
  2.  但是const左值引用既可引用左值,也可引用右值(常量具有常性,不能修改,如果我们直接把一个常量交给引用,就可能通过引用来修改这个常量,这违背了常性。因此不能直接引用一个右值常量,但是当我们使用const引用,就无法被修改,那么就可以引用了)
    // 左值引用只能引用左值,不能引用右值。
    int a = 10;
    int& ra1 = a;   // ra为a的别名
    //int& ra2 = 10;   // 编译失败,因为10是右值
    // const左值引用既可引用左值,也可引用右值。
    const int& ra3 = 10;
    const int& ra4 = a;
  •  右值引用可以引用左值吗

 原本右值引用是无法引用左值的,但是c++11提供了一个函数move可以把一个左值强转为右值

int a = 10;
// 右值引用可以引用move以后的左值
int&& r3 = std::move(a);

注意:move不会改变参数本身的左值属性,这一点可以参考强制类型转化: 

e125d424a99c4116854ffe6db364d76e.png 

r3右值引用一个被move的左值a,修改r3也会修改左值a,因为r3即是a的别名 

int a = 10;
int&& r3 = move(a);
r3 = 1;
cout << a;

25601c83185d4fbc82f44ab24841bada.png

8ab31c2eeb6c41f5a6e1f17c318b0afb.png

  • 关于对常量的右值引用:

先说结论 

当右值引用了常量,引用会把常量区中的数据拷贝一份到栈区,然后该引用指向栈区中拷贝后的数据

int&& r3 = 10;
r3 = 1;

理由一:

如果r3拿到的就是常量10本身,那就可以对其进行修改,那以后我们在用10给别的变量赋值时,他就不再是10了,这显然是十分荒谬的,因此得证

理由二:

看地址

如果r3拿到的就是常量10本身,那他的地址因该是常量区,与10地址相同,但显然r3与r4地址相近,是在栈区。

410144d450c94d988ca98bd6e1bef98a.png


 右值引用使用场景和意义

前面我们可以看到左值引用既可以引用左值和又可以引用右值,那为什么C++11还要提出右值引
用呢?下面我们来看看左值引用的短板,右值引用是如何补齐这个短板的!
  • 左值引用的使用场景:

做参数和做返回值都可以提高效率
void func1(string& s)
{
	;
}
void func2(string s)
{
	;
}
int main()
{
string s1("hello world");
// func1和func2的调用我们可以看到左值引用做参数减少了拷贝,提高效率的使用场景和价值
int a = 0, b = 0;
size_t begin = clock();
while(a++<100000000)
	func2(s1);

size_t end = clock();
cout << end - begin;
while(b++<100000000)
	func1(s1);
begin = clock();
cout << endl << begin - end;
}

 (realse版本)941d0e16be89423fa60c68f2ea009712.png

string& func1(string& s)
{
	return s;
}
string func2(string& s)
{
	return s;
}
int main()
{
string s1("hello world");
// func1和func2的调用我们可以看到左值引用做参数减少了拷贝,提高效率的使用场景和价值
int a = 0, b = 0;
size_t begin = clock();
while(a++<1000000)
	func2(s1);

size_t end = clock();
cout << end - begin;
while(b++<1000000)
	func1(s1);
begin = clock();
cout << endl << begin - end;
}

 

(debug版本)
d6ed96129d604e04a59a2a0c75649ac5.png
  • 左值引用的短板:

但是当函数返回对象是一个局部变量,出了函数作用域就不存在了,就不能使用左值引用返回,
只能传值返回。例如:下面的函数只能使用传值返回, 由于返回值会随着函数结束而被销毁,所以要先拷贝构造一个临时变量,接着再由临时变量去个ret2进行拷贝构造。结果就是旧一点的编译器是两次拷贝构造,新的编译器自带优化是1次拷贝构造。(都是深拷贝)
string func()
{
string s("aaa");
	return s;
}
int main()
{
	string s =func();
}

因此,右值引用闪亮登场。 

 我们之前说了被move的左值可以被右值引用,除此之外:

C++会把即将离开作用域的非引用类型的返回值当成右值,这种类型的右值也称将亡值 

产生思想:假设b是将亡值,用它去构造a,那我干嘛还要进行深拷贝,我直接把b的数据转移到a身上不就行了(因为b马上自己也要释放空间,那这些数据也会消失,不如直接给a)

  • 移动构造、移动赋值:

在string中增加移动构造, 移动构造本质是将参数右值的资源窃取过来,占位已有,那么就不

用做深拷贝了,所以它叫做移动构造,就是窃取别人的资源来构造自己

还有移动赋值原理相同

class _string
{
public:
	_string(const char* str = "")//"\0"=="",因为结尾没有'\0'会补
		: _size(strlen(str))
		, _capacity(_size == 0 ? 3 : _size)
	{
		_str = new char[_capacity + 1];//_capacity不包括最后的'\0'
		strcpy(_str, str);
	}
	_string(_string&& s)
		:_str(nullptr)
		, _size(s._size)
		, _capacity(s._capacity)
	{
		cout << "移动构造" << endl;
		std::swap(_str, s._str);
	}
	_string& operator=(_string&&s)
	{
		cout << "移动赋值" << endl;
		std::swap(_str, s._str);
	}
	char* _str;
	size_t _size;
	size_t _capacity;
};
_string get_string()
{
	_string str("");

	return str;
}
int main()
{
	_string s2 = get_string();

	return 0;
}

 相较于之前的两次或一次拷贝构造,因为get_string的返回值是右值,所以直接调用一次或两次移动构造,而移动构造也不是深拷贝,而是直接用swap交换来进行拷贝,效率自然提高


移动构造之所以这么叫,就是因为移走了别人的资源。这部分资源之所以会被移走,就是因为它有右值属性。而它之所以有右值属性,要么就是这个变量是个将亡值,资源不转移就浪费了;要么就是被程序员亲自move了,程序员许可把这个对象的资源转移走,这就保证了资源移动的安全性。

移动语义(Move Semantics)是 C++11 引入的一项重要特性,它允许对象的资源(如堆上分配的内存)在不进行深度复制的情况下进行转移。通过移动语义,可以将对象的资源从一个对象转移到另一个对象,从而避免不必要的内存拷贝,提高程序性能和效率。


万能引用

  • 引用折叠

在下面的函数模板中,T&&并不是表示右值引用,他会识别传参类型,传参为左值就是左值引用,传参是右值就是右值引用

template<class T>
void  PerfectForward(T&& t)
{
	cout << "右值引用" << endl;
}

证明如下 

template<class T>
void  PerfectForward(T&& t)
{
	cout << "右值引用" << endl;
}
template<class T>
void PerfectForward(const T& t)
{
	cout << "左值引用" << endl;
}
int main()
{
	int a = 10;
	PerfectForward(move(a));
	PerfectForward(a);
	return 0;
}

4874cf214248476b81d85364e4da8760.png

发现第二个函数也是调用的第一个函数模板,则说明第一个函数模板所生成的函数比第二个更适合所传参数,我们知道第二个生成了const int&,最适合的是int&,得证。

可以看作是&&折叠成了一个&,所以万能引用又称为引用折叠
 

请看以下的代码

void  func(int&& t)
{
	cout << "右值引用" << endl;
}
void  func(const int&& t)
{
	cout << "const右值引用" << endl;
}
void func(const int&t)
{
	cout << "const左值引用" << endl;
}
void  func(int& t)
{
	cout << "左值引用" << endl;
}
template<class T>
void  PerfectForward(T&& t)
{
	func(t);
}
int main()
{
	const int a = 10;
	PerfectForward(move(a));
	PerfectForward(a);
	int b = 10;
	PerfectForward(move(b));
	PerfectForward(b);
	PerfectForward(10);
	return 0;
}

543669f182f241f1b1df882a75845469.png

所有都是左值,why???
解答:

a右值引用后b,右值引用指向的对象b是右值属性,但是引用本身a是左值属性(对象b有无const属性会被a继承)

有的老铁要说了在把func变成func(move(T))

万万不可啊,假如我传了一个右值那还好,你这样保证了它的属性不改变,可假如人家本来就是左值呢?那你就把它改成了右值了,这不就出问题了

为什么c++11要这么设计呢?

我们右值引用是为了移动语义,说白了就是转移资源,假如引用后依旧是右值,因为右值是不能被修改的,那就实现不了转移资源,但这样也有bug,即是我们可能会多个函数嵌套,最里面才是转移资源,但它在第一层就把值从左值变成了右值,后面面对函数重载就可能调用错,那怎莫让它类型不改变呢

  • 完美转发闪亮登场:

C++提供了一个函数模板forward,称为完美转发,其可以自动识别到参数的左右值类型,从而将其转化为原来的值的类型。

void  func(int&& t)
{
	cout << "右值引用" << endl;
}
void  func(const int&& t)
{
	cout << "const右值引用" << endl;
}
void func(const int&t)
{
	cout << "const左值引用" << endl;
}
void  func(int& t)
{
	cout << "左值引用" << endl;
}
template<class T>
void  PerfectForward(T&& t)
{
	func(forward<T>(t));
}
int main()
{
	const int a = 10;
	PerfectForward(move(a));
	PerfectForward(a);
	int b = 10;
	PerfectForward(move(b));
	PerfectForward(b);
	PerfectForward(10);
	return 0;
}

943875947f5340a9a73f8cb4393e26fb.png 

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

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 pos
 prev->_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 pos
 prev->_next = newnode;
newnode->_prev = prev;
 newnode->_next = pos;
 pos->_prev = newnode;
 }
private:
 Node* _head;
};
int main()
{
 List<bit::string> lt;
 lt.PushBack("1111");
 lt.PushFront("2222");
 return 0;
}

 总结:

我们今天学习了初始化、声明以及右值引用三个点,大概是学完了一半,剩下的就下次再讲啦。

🥰创作不易,你的支持对我最大的鼓励🥰

🪐~ 点赞收藏+关注 ~🪐

e3ff0dedf2ee4b4c89ba24e961db3cf4.gif

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

酒厂废水总氮超标解决方法,除总氮树脂A-62

首先生化处理通过微生物的作用&#xff0c;将废水中的有机物质降解为无机物质&#xff1b;接着高级氧化&#xff0c;对剩余难以生物降解的有机物进行深度氧化&#xff0c;进一步削减总氮含量&#xff1b;最后&#xff0c;通过TulsimerA-62MP除硝酸盐特种树脂进行深度去除残余的…

FireProx:一款功能强大的AWS API网关管理与IP地址轮换代理工具

关于FireProx FireProx是一款功能强大的AWS API网关安全管理工具&#xff0c;该工具可以帮助广大研究人员创建实现唯一IP地址轮换的实时HTTP转发代理。 在发送网络请求或进行网络交互时&#xff0c;实现源IP地址轮换是一个非常复杂的过程&#xff0c;虽然社区中也有相关的工具…

Ubuntu 22.04进行远程桌面连接

文心一言 Ubuntu 22.04进行远程桌面连接&#xff0c;无论是连接到Windows 10还是另一个Ubuntu 22.04&#xff0c;都可以通过不同的方式实现。以下是具体的步骤&#xff1a; 连接到Windows 10 在Windows 10上开启远程桌面功能&#xff1a;首先&#xff0c;需要在Windows 10上…

Debian 安装 Docker

Debian 安装 Docker。 这是官方安装文档 Install Docker Engine on Debian | Docker DocsLearn how to install Docker Engine on Debian. These instructions cover the different installation methods, how to uninstall, and next steps.https://docs.docker.com/engine/i…

redis消息队列

redis消息队列 redis可以直接实现消息队列&#xff0c;无需学习别的技术 list——本质是链表&#xff0c;数据存储 启动同一个IP和端口的2台客户端&#xff0c;一边阻塞弹出&#xff0c;一边添加元素 在20s内&#xff0c;如果有元素就弹出&#xff0c;没有元素就等待&#xff…

泛域名SSL证书有什么优势?

泛域名SSL证书&#xff0c;又称通配符证书&#xff0c;是一种特殊的数字证书类型&#xff0c;设计用于同时保护一个主域名及其所有同级子域名。具体而言&#xff0c;如果您为某个域名&#xff08;如 example.com&#xff09;申请了泛域名SSL证书&#xff0c;该证书将不仅适用于…

Thingsboard PE 白标的使用

只有专业版支持白标功能。 使用 ThingsBoard Cloud 或安装您自己的平台实例。 一、介绍 ThingsBoard Web 界面提供了简便的操作,让您能够轻松配置您的公司或产品标识和配色方案,无需进行编码工作或重新启动服务。 系统管理员、租户和客户管理员可以根据需要自定义配色方案、…

2024年 Mathorcup高校数学建模竞赛(A题)PCI 规划问题 | 多目标规划解析,小鹿学长带队指引全代码文章与思路

我是鹿鹿学长&#xff0c;就读于上海交通大学&#xff0c;截至目前已经帮200人完成了建模与思路的构建的处理了&#xff5e; 本篇文章是鹿鹿学长经过深度思考&#xff0c;独辟蹊径&#xff0c;通过多目标规划解析解决非法野生动植物贸易问题。结合神经网络、集成学习、贝叶斯网…

Web程序设计-实验02 CSS页面布局

【实验主题】 影视网站前台模板页设计 【实验任务】 1、浏览并分析多个影视网站&#xff08;详见参考资源&#xff0c;建议自行搜索更多影视网站&#xff09;的整体版面布局&#xff0c;对比同一网站不同页面&#xff08;主页、列表页、详情页&#xff09;的元素异同——剔除…

故障诊断 | 基于LSTM的滚动轴承故障诊断

效果 概述 基于LSTM(长短期记忆网络)的滚动轴承故障诊断是一种利用深度学习技术来预测滚动轴承是否存在故障的方法。下面是一个基本的滚动轴承故障诊断的流程: 数据收集:首先,需要收集与滚动轴承相关的振动信号数据。这些数据可以通过传感器或振动监测系统获取。收集的数…

如何对输入信号产生一个固定的时移(CODESYS信号时移FB)

1、同步性问题(跟随给定和跟随反馈的区别) 随动系统同步性问题(跟随给定和跟随反馈的区别)-CSDN博客文章浏览阅读39次。1、运动控制比例随动运动控制比例随动系统_正运动随动系统-CSDN博客PLC如何测量采集编码器的位置数据,不清楚的可以参看我的另一篇博文:三菱FX3U PLC高速…

【绩效管理】建立员工绩效考核机制,提升企业绩效管理水平

随着企业的迅猛发展&#xff0c;其内部管理问题日益突出&#xff0c;已经制约了企业的进一步发展。一方面&#xff0c;员工工作懒散、积极性不高&#xff0c;出错的次数也逐步上升&#xff0c;另一方面&#xff0c;管理者也无法有效评价员工的工作好坏。面对这些问题&#xff0…

计算机网络常见面试总结

文章目录 1. 计算机网络基础1.1 网络分层模型1. OSI 七层模型是什么&#xff1f;每一层的作用是什么&#xff1f;2.TCP/IP 四层模型是什么&#xff1f;每一层的作用是什么&#xff1f;3. 为什么网络要分层&#xff1f; 1.2 常见网络协议1. 应用层有哪些常见的协议&#xff1f;2…

02—js数据类型及相互转换

一、数据类型 js把数据分为两类 基本类型&#xff1a;string number boolean undefined null 引用类型&#xff1a;object(fuction(可以执行) array&#xff08;数值下标&#xff0c;内部数据是有序的&#xff09;) 1.Number:数值类型&#xff0c;整数和小数 &#xff08…

SpringMVC原理及工作流程

组件 SpringMVC的原理主要基于它的各个组件之间的相互协作交互&#xff0c;从而实现了Web请求的接收&#xff0c;处理和响应。 它的组件有如下几个&#xff1a; DispatcherServlet前端控制器 HandlerMapping处理器映射器 Controller处理器 ModelAndView ViewResolver视图…

Mysql内存表及使用场景(12/16)

内存表&#xff08;Memory引擎&#xff09; InnoDB引擎使用B树作为主键索引&#xff0c;数据按照索引顺序存储&#xff0c;称为索引组织表&#xff08;Index Organized Table&#xff09;。 Memory引擎的数据和索引分开存储&#xff0c;数据以数组形式存放&#xff0c;主键索…

前端CSS讲义1

什么是 CSS? CSS 指层叠样式表 样式定义如何显示 HTML 元素 样式通常存储在样式表中 把样式添加到 HTML 4.0 中&#xff0c;是为了解决内容与表现分离的问题 外部样式表可以极大提高工作效率 外部样式表通常存储在 CSS 文件中 多个样式定义可层叠为一 样式对网页中元素…

DAY03|203.移除链表元素、707.设计链表、206.反转链表

203.移除链表元素、707.设计链表、206.反转链表 LeetCode 203.移除链表元素LeetCode 707.设计链表LeetCode 206.反转链表双指针法递归法 LeetCode 203.移除链表元素 注意&#xff0c;在dummy上操作&#xff0c;返回也返回dummy->next 如果头铁想返回head&#xff0c;那样会…

2024高交会-2024深圳高新技术展-高新技术成果交易会

2024高交会-2024深圳高新技术展-2024高新技术成果展-中国高校技术交易会-第26届高交会-深圳高交会-深圳高科技展-深圳新科技展-深圳高新技术成果展 第二十六届中国国际高新技术成果交易会&#xff08;简称高交会&#xff09; 时间&#xff1a;2024年11月15日-19日 地址&#…

点击按钮(文字)调起elementUI大图预览

时隔一年&#xff0c;我又回来了 ~ 最近在做后台&#xff0c;遇到一个需求&#xff0c;就是点击“查看详情”按钮&#xff0c;调起elementUI的大图预览功能&#xff0c;预览多张图片&#xff0c;如下图&#xff1a; 首先想到的是使用element-ui的el-image组件&#xff0c;但它是…