【C++】C++11新特性:新的类功能、可变参数模板、STL容器中的empalce相关接口函数、lambda表达式、包装器(function、bind)

目录

一、新的类功能

1.1 移动构造函数和移动赋值运算符重载

1.2 强制生成默认函数的关键字default

1.3 禁止生成默认函数的关键字delete

1.4 其它的类功能

二、可变参数模板

三、STL容器中的empalce相关接口函数

四、lambda表达式

4.1 lambda的引入

4.2 lambda表达式语法

4.2.1 lambda表达式各部分说明

4.2.2 捕获列表说明

4.3 函数对象与lambda表达式

五、包装器

5.1 function包装器

5.2 bind包装器


一、新的类功能

1.1 移动构造函数和移动赋值运算符重载

在以前的C++时,我们知道C++的类有6个默认成员函数:
1. 构造函数
2. 析构函数
3. 拷贝构造函数
4. 拷贝赋值重载
5. 取地址重载
6. const 取地址重载
比较重要的是前4个,后两个用处不大。默认成员函数就是我们不写编译器会生成一个默认的。

C++11 新增了两个:移动构造函数移动赋值运算符重载

移动构造函数和移动赋值运算符重载有一些需要注意的点:

  • 如果你没有自己实现移动构造函数且没有实现析构函数、拷贝构造、赋值重载中的任意一个那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造。如果实现了就调用移动构造,没有实现就调用拷贝构造。
  • 如果你没有自己实现移动赋值重载函数,且没有实现析构函数、拷贝构造、赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值默认生成的移动赋值,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造完全类似)
  • 如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。

注:

  • 如果想要使用默认生成的移动构造/移动赋值重载,那么析构函数、拷贝构造、赋值重载都不能自己定义实现。但可以实现构造函数。
  • 一般析构函数、拷贝构造、赋值重载作为一个整体出现。比如一般出现析构函数,就说明类的内部实现了开辟空间请求资源,析构函数是为了释放资源。既然申请了资源,拷贝时就要进行深拷贝,于是就有了拷贝构造和赋值拷贝。

下面的示例中只实现了构造函数,mystring是上一篇中定义的类在此不作赘述。main函数要使用Person默认生成的拷贝构造和移动构造,之前讲过,默认生成的拷贝构造对内置类型浅拷贝,对自定义类型用它自己的拷贝构造(如果它有的话)。

class Person
{
public:
	Person(const char* name = "", int age = 0)
		:_name(name)
		, _age(age)
	{}

private:
	mystring::string _name;
	int _age;
};

int main()
{
	Person s1;
	Person s2 = s1;			   // 默认的拷贝构造
	Person s3 = std::move(s1); // 默认的移动构造

	return 0;
}

如果我们定义了析构函数、拷贝构造、赋值重载其中的任意一个,那么移动构造就不会默认生成,比如定义析构函数,就不会生成和使用默认的移动构造,而是使用默认的拷贝构造。

	~Person()
	{
		cout << "~Person()" << endl;
	}

 

1.2 强制生成默认函数的关键字default

如果我们既想生成析构函数、拷贝构造、赋值重载其中几个,又想生成默认的移动构造/移动赋值重载。可以使用default关键字强制生成默认的某些函数。

比如:我们提供了拷贝构造,就不会生成移动构造了,那么我们可以
使用default关键字显示指定移动构造生成。(也要强制生成默认的拷贝构造)

	// 强制编译器生成
	Person(Person&& p) = default;
    Person(const Person& p) = default;

如果不强制生成默认的拷贝构造函数会报错,因为强制生成移动构造后,就不会生成拷贝构造了。

编译器生成默认的拷贝构造函数和拷贝赋值运算符的条件是:

  • 类中没有定义拷贝构造函数或拷贝赋值运算符。
  • 类中没有定义移动构造函数或移动赋值运算符。
  • 类中没有定义任何构造函数(除了默认构造函数)。

1.3 禁止生成默认函数的关键字delete

在 C++98 中,如果想要阻止编译器生成某个函数的默认版本,可以将该函数声明为私有,并且只声明不实现。这样,除了类内部之外,外部无法访问这个函数,从而阻止了编译器生成默认版本,其他人想要调用就会报错。

在C++11中更简单,只需在该函数声明加上=delete即可,这将告诉编译器不要生成该函数的默认版本,从而防止它被外部调用。称=delete修饰的函数为删除函数。 

Person(Person&& p) = delete;

1.4 其它的类功能

C++_好像有点东西的博客-CSDN博客

1.类成员变量初始化

C++11允许在类定义时给成员变量初始缺省值,默认生成构造函数会使用这些缺省值初始化,这
个我们在类和对象默认就讲了,这里就不再细讲了。

2. 继承和多态中的final与override关键字

这个我们在继承和多态部分已经进行了详细讲解这里就不再细讲。只是强调两点:

final 关键字
用途:final 关键字用于修饰类或者函数,表示不允许被继承或者重写。
类修饰:当 final 修饰一个类时,它表示该类不能被其他类继承。
函数修饰:当 final 修饰一个函数时,它表示该函数不能被派生类重写。

override 关键字
用途:override 关键字用于修饰虚函数,表示该虚函数是在派生类中重写的基类虚函数。
条件:override 修饰的虚函数必须在派生类中有一个基类虚函数与之匹配。
错误:如果 override 修饰的虚函数在派生类中没有重写基类虚函数,或者重写的函数不符合匹配条件,编译器会报错。

二、可变参数模板

C++11的新特性可变参数模板能够让我们创建可以接受可变参数的函数模板和类模板,相比C++98/03,类模版和函数模版中只能含固定数量的模版参数,可变模版参数无疑是一个巨大的改
进。然而由于可变模版参数比较抽象,使用起来需要一定的技巧,所以这块还是比较晦涩的。我们现阶段掌握一些基础的可变参数模板特性就够我们用了。

下面是一个可变参数的函数模板:

  • Args是一个模板参数包,args是一个函数形参参数包
  • 声明一个参数包Args... args,这个参数包中可以包含0到任意个模板参数。
  • sizeof...(args) 来获取参数包中参数的数量
template <class... Args>
void ShowList(Args... args)
{
	//cout << sizeof(args) << endl;//错误用法
	//sizeof...(args) 来获取参数包中参数的数量
	cout << sizeof...(args) << endl;
}

int main()
{
	ShowList(1);
	ShowList(1, 2, 3);
	return 0;
}

上面的参数args前面有省略号,所以它就是一个可变模版参数,我们把带省略号的参数称为“参数包”,它里面包含了0到N(N>=0)个模版参数。我们无法直接获取参数包args中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模版参数的一个主要特点,也是最大的难点,即如何展开可变模版参数。由于语法不支持使用args[i]这样方式获取可变参数,所以我们的用一些奇招来一一获取参数包的值。

void _ShowList()
{
	cout << endl;
}

// 编译时的递归推演
// 第一个模板参数依次解析获取参数值
template <class T, class ...Args>
void _ShowList(const T& val, Args... args)
{
	cout << val << " ";
	_ShowList(args...);
}

template <class ...Args>
void ShowList(Args... args)
{
	_ShowList(args...);
}

int main()
{
	ShowList(1);
	ShowList(1, 2);
	ShowList(1, 2, 3);
	ShowList(1, 2.2, 'x', 3.3);

	return 0;
}

简单解释过程:

  • 例如ShowList(1, 2, 3);函数把参数1,2,3传给_ShowList,在这个函数参数(const T& val, Args... args)里,1被看作第一个参数val,后面的所有参数2和3看作一个参数包。
  • 打印val,再递归调用_ShowList(val, args)。这个_ShowList函数里参数2被看作第一个参数val,后面的所有参数3看作一个参数包。
  • 打印val,再递归调用_ShowList(val,args)。这个_ShowList函数里参数3被看作第一个参数val,后面没有参数,args参数为空。
  • 打印val,因为args参数为空,递归调用_ShowList()。输出换行。

方法2:

template <class T>
int PrintArg(T&& t)
{
	cout << t << " ";
	return 0;
}

//展开函数
template <class... Args>
void ShowList(Args&&... args)
{
	// 要初始化arr,强行让编译器解析参数包,参数包有几个参数,PrintArg就依次推演生成几个
	int arr[] = { PrintArg(args)... };
	cout << endl;
}
  • 使用模板参数包推演初始化一个数组 arr,并且让编译器解析参数包,生成与参数包中参数数量相等的 PrintArg 调用。这是通过使用 ... 运算符在数组初始化表达式中实现的。
  • ... 运算符在 C++ 中被称为展开运算符,它用于将模板参数包中的每个参数依次传递给函数或模板。在这个例子中,PrintArg(args)... 表达式会依次调用 PrintArg 函数,每个参数调用一次。
  • 在 ShowList 函数中,使用 { PrintArg(args)... } 表达式来初始化数组 arr。这个表达式会依次调用 PrintArg 函数,每个参数调用一次。由于 PrintArg 函数返回 int 类型,所以数组 arr 中的每个元素都是 int 类型的,但它们的值是 PrintArg 函数的返回值,即 0。

三、STL容器中的empalce相关接口函数

list::emplace_back - C++ Reference (cplusplus.com)

vector::emplace_back - C++ Reference (cplusplus.com)

template <class... Args>
  void emplace_back (Args&&... args);

emplace 相关接口函数在 C++ 中提供了一种高效的方式来在容器中插入元素。

int main()
{
	std::list<mystring::string> lt;
	mystring::string s1("1111");
	lt.push_back(s1);
	lt.push_back(move(s1));
	cout << endl;

	mystring::string s2("1111");
	lt.emplace_back(s2);
	lt.emplace_back(move(s2));
	cout << endl;

	lt.push_back("xxxx");
	cout << endl;
	lt.emplace_back("xxxx");

	return 0;
}

 

empalce_back与push_back的对比:

  • emplace_back 和 push_back 是 C++ 中用于在容器末尾插入元素的两个方法。
  • 它们的效率在插入自定义对象时差不多,都可以使用相应的深拷贝和移动拷贝。
  • 但是在使用变量传参发生隐式类型时,push_back要进行构造加移动拷贝,而emplace_back直接将这个值一步步传给内部的构造函数构造。省略了移动拷贝的过程。
  • 所以emplace_back() 在插入大量元素时比 push_back() 更高效。但实际使用时差距也不是很大。大家可以根据自己情况选择使用。

四、lambda表达式

4.1 lambda的引入

在C++98中,如果想要对一个数据集合中的元素进行排序,可以使用std::sort方法。但是如果待排序元素为自定义类型,需要用户定义排序时的比较规则。例如一个类中有不同的值可以排序,就要写不同的排序方法。如商品的名称、价格、评分。

随着C++语法的发展,人们开始觉得上面的写法太复杂了,每次为了实现一个algorithm算法,都要重新去写一个类,如果每次比较的逻辑不一样,还要去实现多个类,特别是相同类的命名,这些都给编程者带来了极大的不便。因此,在C++11语法中出现了Lambda表达式

4.2 lambda表达式语法

lambda表达式书写格式:
[capture-list] (parameters) mutable -> return-type { statement }

4.2.1 lambda表达式各部分说明

  • [capture-list] : 捕捉列表。该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用。
  • (parameters):参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略。
  • mutable:取消常量性可以省略。默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。
  • ->returntype:返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。返回值类型明确情况下,一般可以省略,由编译器对返回类型进行推导。
  • {statement}:函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量。

注意:

  • 在lambda函数定义中,有参数列表、返回值类型还有函数体,与函数的不同就是多加了方括号[]和mutable。
  • lambda可以简单理解为匿名函数对象。该函数无法直接调用,如果想要直接调用,可借助auto将其赋值给一个变量。

4.2.2 捕获列表说明

捕捉列表描述了上下文中那些数据可以被lambda使用,以及使用的方式传值还是传引用。

  • [var]:表示值传递方式捕捉变量var
  • [=]:表示值传递方式捕获所有父作用域中的变量(包括this)
  • [&var]:表示引用传递捕捉变量var
  • [&]:表示引用传递捕捉所有父作用域中的变量(包括this)
  • [this]:表示值传递方式捕捉当前的this指针

注意:

  • 传值方式捕捉的变量不能被修改,捕捉后就成了类仿函数内部的成员变量,但是这个变量是const修饰的。如果需要修改,就需要在表达式加上mutable。
  • 传值方式捕捉的变量和外部的变量不是同一个,即使内部可以修改,也不会影响外部的变量值。
  • [&var] 不是取地址,而是以引用的方式捕捉对象。不需要再加mutable。想加上也可以加。
  • 父作用域指包含lambda函数的语句块
  • 语法上捕捉列表可由多个捕捉项组成,并以逗号分割。比如:
    [=, &a, &b]:以引用传递的方式捕捉变量a和b,值传递方式捕捉其他所有变量
    [&,a, this]:值传递方式捕捉变量a和this,引用方式捕捉其他变量,捕捉this就可以访问成员变量。
  • 捕捉列表不允许变量重复传递,否则就会导致编译错误。 比如:[=, a]:=已经以值传递方式捕捉了所有变量,捕捉a重复。
  • lambda表达式之间不能相互赋值,即使看起来类型相同

在 C++ 中,当您创建一个 lambda 函数时,您可以选择捕捉外部作用域中的变量。如果您的 lambda 函数在块作用域(如循环、函数或模板参数包)之外定义,那么它不能捕捉任何变量。这被称为“封闭规则”或“封闭性规则”。

具体来说,这意味着:

  • 块作用域:如果 lambda 函数定义在一个块作用域内(如在循环中),它可以捕捉该块作用域内的变量。
  • 函数作用域:如果 lambda 函数定义在一个函数内,它可以捕捉该函数作用域内的变量。
  • 全局作用域:如果 lambda 函数定义在一个全局作用域内,或者在块作用域之外定义,它不能捕捉任何变量。

这主要是因为当 lambda 函数在全局作用域或块作用域之外定义时,它没有明确的“封闭”作用域。如果它捕捉了外部变量,那么这些变量必须被保持不变,以便 lambda 函数在它的生命周期内能够访问它们。然而,这通常是不可能的,因为这些变量可能在 lambda 函数创建后不久就被销毁或更改。

class AA
{
public:
	void func()
	{
		/*auto f1 = [this]() {
			cout << a1 << endl;
			cout << a2 << endl;
		};*/
		//lambda可以在成员函数内部
		//不可以使用a1,a2,因为捕捉列表是捕捉父作用域的变量,a1a2不是父作用域的变量
		auto f1 = [a1, a2]() {
			cout << a1 << endl;
			cout << a2 << endl;
		};
		//捕捉使用this,编译器做了优化处理,不用写this->a1,this->a2
		auto f1 = [=]() {
			cout << a1 << endl;
			cout << a2 << endl;
		};

		f1();
	}
private:
	int a1 = 1;
	int a2 = 1;
};

4.3 函数对象与lambda表达式

函数对象,又称为仿函数,即可以像函数一样使用的对象,就是在类中重载了operator()运算符的
类对象。

class Rate
{
public:
	Rate(double rate)
		:_rate(rate)
	{}

	double operator()(double money, int year)
	{
		return money * year * _rate;
	}
private:
	double _rate;
};

int main()
{
	double rate = 0.13;

	//使用函数对象
	Rate r1(rate);
	int tmp = r1(10000, 2);

	//使用lambda表达式
	auto r2 = [=](double money, int year) 
	{
		return money * year * rate;
	};
	int tmp2 = r2(10000, 2);
	return 0;
}

从使用方式上来看,函数对象与lambda表达式完全一样。

函数对象将rate作为其成员变量,在定义对象时给出初始值即可,lambda表达式通过捕获列表可
以直接将该变量捕获到。

实际在底层编译器对于lambda表达式的处理方式,完全就是按照函数对象的方式处理的。
即:如果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载了operator()。

名字是<lambda_uuid>,即lambda+uuid,随机生成 。

五、包装器

5.1 function包装器

function包装器 也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。

包装器包装的是可调用对象:函数指针、函数对象、仿函数、lambda表达式。

为什么要引入function包装器?

之前学习的函数指针、仿函数、lambda表达式在使用时都有各自的缺点,例如:

  • 函数指针的缺点:类型不好写。(void (*funcName)(int x, int y);)
  • 仿函数的缺点:类型好写,但是每次使用都需要写一个类,比较笨重.
  • lambda缺点:简洁,但是不好描述类型。不同机器的UUID不同,类型结果不同,即类型是相对匿名的。decltype可以获得类型。

function包装器的表达式语法:

std::function在头文件<functional>
// 类模板原型如下
template <class T> function;     // undefined


template <class Ret, class... Args>
        class function<Ret(Args...)>;

模板参数说明:
Ret: 被调用函数的返回类型
Args…:被调用函数的形参

void swap_func(int& r1, int& r2)
{
	int tmp = r1;
	r1 = r2;
	r2 = tmp;
}

class Swap
{
public:
	void operator()(int& r1, int& r2)
	{
		int tmp = r1;
		r1 = r2;
		r2 = tmp;
	}
};

int main()
{
	int x = 1, y = 2;
	cout << x << "  " << y << endl;
	auto swap_lambda = [](int& r1, int& r2) {
		int tmp = r1;
		r1 = r2;
		r2 = tmp;
	};

	function<void(int&, int&)> f1 = swap_func;
	f1(x, y);
	cout << x << "  " << y << endl;

	function<void(int&, int&)> f2 = Swap();
	f2(x, y);
	cout << x << "  " << y << endl;

	function<void(int&, int&)> f3 = swap_lambda;
	f3(x, y);
	cout << x << "  " << y << endl;

	map<string, function<void(int&, int&)>> Option =
	{
		{"函数指针", swap_func},
		{"仿函数", Swap()},
		{"lambda表达式",swap_lambda}
	};
	Option["函数指针"](x, y);
	cout << x << "  " << y << endl;
	Option["仿函数"](x, y);
	cout << x << "  " << y << endl;
	Option["lambda表达式"](x, y);
	cout << x << "  " << y << endl;

	return 0;
}

如果function包装的是类的成员对象,

  1. 需要在对象前面加上类域和& ,&用来取地址,因为普通函数的函数名就是地址,静态的成员函数可以不加,但是推荐加上。
  2. 包装非静态成员函数,需要在Args…(被调用函数的形参)中指定类的实例化对象的地址。因为非静态成员函数的参数还有一个隐含的this指针。
  3. 编译器进行优化,可以在Args中实例化匿名对象,不用再实例化出对象传址。
class Plus
{
public:
	static int plusi(int a, int b)
	{
		return a + b;
	}

	double plusd(double a, double b)
	{
		return a + b;
	}
};

int main()
{
	// 成员函数取地址,比较特殊,要加一个类域和&
	function<int(int, int)> f1 = &Plus::plusi;
	cout << f1(1, 2) << endl;

	//function<double(double, double)> f2 = &Plus::plusd;//会报错,因为非静态成员函数的参数还有一个隐含的this指针。
	function<double(Plus*, double, double)> f2 = &Plus::plusd;
	Plus ps;
	cout << f2(&ps, 1.1, 2.2) << endl;

	//编译器进行优化,不用再实例化出对象传址
	function<double(Plus, double, double)> f3 = &Plus::plusd;
	cout << f3(Plus(), 1.1, 2.2) << endl;

	return 0;
}

5.2 bind包装器

function处理类的成员函数时,每次都要在参数上加上类名或者类的指针。为了减少操作,引入了bind,用来调节可调用对象的参数列表。

std::bind 是头文件functional的一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象和可选的参数绑定列表,生成一个新的可调用对象来“适应”原对象的参数列表。这个新的可调用对象可以被用来调用原始的可调用对象,并且可以改变参数的顺序或绑定一些参数的值。

原型如下:

bind的主要作用:

  1. 调整参数顺序
  2. 调整参数个数,有些参数可以绑定(bind)时写死。

调用bind的一般形式:auto newCallable = bind(callable,arg_list);
其中,newCallable本身是一个可调用对象,arg_list是一个逗号分隔的参数列表,对应给定的可调用对象callable的参数。当我们调用newCallable时,newCallable会调用callable,并传给它arg_list中的参数。
arg_list中的参数可能包含形如_n的名字,其中n是一个整数,这些参数是占位符(placeholders),表示newCallable的参数,它们占据了传递给newCallable的参数的“位置”。数值n表示生成的可调用对象中参数的位置:_1为newCallable的第一个参数,_2为第二个参数,以此类推。

int Sub(int a, int b)
{
	return a - b;
}

int main()
{
	function<int(int, int)> f1 = Sub;
	cout << f1(10, 5) << endl;

	// 调整参数顺序
	function<int(int, int)> f2 = bind(Sub, placeholders::_2, placeholders::_1);
	cout << f2(10, 5) << endl;

	// 调整参数个数,有些参数可以绑定(bind)时写死
	function<int(int)> f3 = bind(Sub, 20, placeholders::_1);
	cout << f3(5) << endl;

	return 0;
}
    function<double(double, double)> f4 = bind(&Plus::plusd, Plus(), placeholders::_1, placeholders::_2);
	cout << f4(1.11, 2.22) << endl;

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

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

相关文章

Ollama教程,本地部署大模型Ollama,docker安装方法,仅供学习使用

不可商用&#xff01;&#xff01;仅仅提供学习使用&#xff01; 先上视频教学&#xff1a; Ollama教程&#xff0c;本地部署大模型Ollama&#xff0c;docker安装方法&#xff0c;仅供学习使用&#xff01; 资料获取 &#xff1a; Ollama下载包和安装文档在这里&#xff1…

从零到一的程序猿-day2-yoloV4训练及免环境易语言调用

简介 本项目功能介绍&#xff1a;针对4位英文数字随机组合的验证码抽象图片进行分类识别&#xff0c;识别结果为验证码内容 训练 没有难度&#xff0c;手动标注&#xff0c;样本为150张&#xff0c;首先识别出图片中每个英数的位置&#xff0c;再由分类器进行标注识别&#x…

latex bib引参考文献

1.bib内容 2.sn-mathphys-num是官方的参考文献格式 3.不用导cite包&#xff0c;文中这么写 4.end document前ckwx是自己命名的bib的名字

C语言Linux进度条模拟

在Linux字符界面中&#xff0c;使用yum、apt下载东西时会有一个图形化的进度条&#xff0c;可以告诉我们任务的执行进度。 我们也可以通过C语言实现一个类似的进度条&#xff0c;并且可以做得更加美观。以后我们自己写的程序需要显示进度时就可以去调用我们自己实现的进度条。 …

【算法】贪心算法——柠檬水找零

题解&#xff1a;柠檬水找零(贪心算法) 目录 1.题目2.题解3.参考代码4.证明5.总结 1.题目 题目链接&#xff1a;LINK 2.题解 分情况讨论 贪心算法 当顾客为5元时&#xff0c;收下当顾客为10元时&#xff0c;收下10元并找回5元当顾客为20元时&#xff0c;收下20元并找回10…

图像交换部分区域或帧

生成一个boundingbox&#xff0c;或区间 给定矩形框占图像的面积比例&#xff0c;和图像的宽W高H&#xff0c;生成矩形框。根据给定的矩形框&#xff0c;交换两张图像的部分区域。 这里为了方便展示&#xff0c;简化问题&#xff0c;给定一个图像数组mels&#xff0c;对第 i …

基于SSM框架的垃圾分类系统的设计与实现(含源码+sql+开题报告+论文+论文答辩模板)

图1 前台首页截图 首页展示&#xff1a;首页展示法律法规、公示公告、用户交流论坛、分类指南、垃圾站点、以及个人中心&#xff1b; 法律法规&#xff1a;展示我国《城市生活垃圾分类及其评价标准》以及《生活垃圾分类标志》等最新法律法规&#xff1b; 公示公告&#xff1…

【第1章】SpringBoot实战篇之注册接口

文章目录 前言一、代码部分1. User2.UserMapper13. UserSerivce4. UserController15. Result 二、测试1.注册2.再次注册 总结 前言 下面介绍用户注册接口。 一、代码部分 1. User package org.example.springboot3.bigevent.entity;import com.baomidou.mybatisplus.annotat…

【Vulhub】Fastjson 1.2.24_rce复现

文章目录 一&#xff0c;Fastjson是什么&#xff1f;二&#xff0c;fastjson漏洞原理三&#xff0c;判断是否有fastjson反序列化四&#xff0c;复现Fastjson 1.2.24_rce(vulhub)环境配置1.判断是否存在Fastjson反序列化2.反弹shell3.启动RMI服务器4.构造恶意POST请求 一&#x…

FineBi导出Excel后台版实现

就是不通过浏览器,在后台运行的导出 参考文档在:仪表板查看接口- FineBI帮助文档 FineBI帮助文档 我这里是将这个帮助文档中导出的excel文件写到服务器某个地方后,对excel进行其他操作后再下载。由于原有接口耦合了HttpServletRequest req, HttpServletResponse res对象,…

【代码随想录】【算法训练营】【第24天】 [77]组合

前言 思路及算法思维&#xff0c;指路 代码随想录。 题目来自 LeetCode。 day 23&#xff0c;愉快的周五~ 题目详情 [77] 组合 题目描述 77 组合 解题思路 前提&#xff1a;组合求子集问题 思路&#xff1a;回溯算法三部曲&#xff1a;递归函数的返回值以及参数、回溯…

MySQL 自定义函数(实验报告)

一、实验名称&#xff1a; 自定义函数 二、实验日期&#xff1a; 2024年 6 月 1 日 三、实验目的&#xff1a; 掌握MySQL自定义函数的创建及调用&#xff1b; 四、实验用的仪器和材料&#xff1a; 硬件&#xff1a;PC电脑一台&#xff1b; 配置&#xff1a;内存&#…

数据结构——图论详细笔记

一 图论基本概念 Directed Acyclic Graph &#xff08;DAG&#xff09; 二 图的存储 ①邻接矩阵(适用于稠密图) ②邻接表(适用于稀疏图) 三、图的遍历 ①深度优先搜索 //(基于邻接表实现&#xff0c;以有向图为例) //DFS:Depth First Search 深度优先搜索 //1、访问起始顶点 …

STM32学习和实践笔记(33):待机唤醒实验

1.STM32待机模式介绍 很多单片机具有低功耗模式&#xff0c;比如MSP430、STM8L等&#xff0c;我们的STM32也不例外。默认情况下&#xff0c;系统复位或上电复位后&#xff0c;微控制器进入运行模式。在运行模式下&#xff0c;HCLK 为CPU提供时钟&#xff0c;并执行程序代码。这…

【GPU原理】1.线程和缓存的关系

一、GPU如何做并行计算 1.简单的串行计算 对于如上的运算AXY&#xff0c;每次运算我们需要从内存读取两个数据&#xff0c;一个是x[i]&#xff0c;一个是y[i]&#xff0c;最后存回y[i]。这里面有一个FMA的操作&#xff08;融合乘加&#xff08;FMA&#xff09;指令是RISC处理器…

基于Qt GraphicView 解析 CIM/G 电力接线图文件

本文讲述了如何使用Qt的框架来渲染展示标准的CIM/G格式的图形文件&#xff0c;也就是公用信息模型&#xff08;common information model&#xff0c;CIM&#xff09;中的G文件部分的内容。这是一种电力系统图形的交换规则&#xff0c;用于电网图形交换。 [by amjieker] CIM/G …

Ai晚班车531

1.中央网信办等三部门&#xff1a;加快推进大模型、生成式人工智能标准研制。 2.中国石油与中国移动、华为、科大讯飞签署合作协议。 3.Opera浏览器与谷歌云合作&#xff0c;接入 Gemini 大模型。 4.谷歌 Gemini 加持Chromebook Plus。 5.英飞凌&#xff1a;开发 8kW和12kW…

《技术人求职之道》:从入职到离职,全方位解析求职艺术

一、引言二、内容&#xff1a;该求职专栏包含什么三、结果&#xff1a;通过该专栏你将收获什么四、说明&#xff1a;关于该专栏的一些问题解答五、后记 一、引言 求职&#xff0c;这是每个人职业生涯中必经的阶段&#xff0c;技术人亦不例外。上一个冬天的寒风已过&#xff0c…

获取 Bean 对象更加简单的方式

获取 bean 对象也叫做对象装配&#xff0c;是把对象取出来放到某个类中&#xff0c;有时候也叫对象注⼊。 对象装配&#xff08;对象注⼊&#xff09;即DI 实现依赖注入的方式有 3 种&#xff1a; 1. 属性注⼊ 2. 构造⽅法注⼊ 3. Setter 注⼊ 属性注入 属性注⼊是使⽤ Auto…

MySQL性能分析工具——EXPLAIN

性能分析工具——EXPLAIN 1、概述 定位了查询慢的SQL之后&#xff0c;我们就可以使用EXPLAIN或DESCRIBE工具做针对性的分析查询语句 。 DESCRIBE语句的使用方法与EXPLAIN语句是一样的&#xff0c;并且分析结果也是一样的。 MySQL中有专门负责优化SELECT语句的优化器模块&…