(学习总结20)C++11 可变参数模版、lambda表达式、包装器与部分新内容添加

C++11 可变参数模版、lambda表达式、包装器与部分新内容添加

  • 一、可变参数模版
    • 基本语法及原理
    • 包扩展
    • emplace系列接口
  • 二、lambda表达式
    • lambda表达式语法
    • 捕捉列表
    • lambda的原理
    • lambda的应用
  • 三、包装器
    • bind
    • function
  • 四、部分新内容添加
    • 新的类功能
      • 1.默认的移动构造和移动赋值
      • 2.声明时给缺省值
      • 3.defult 和 delete
      • 4.final 与 override
    • STL中一些变化

以下代码环境为 VS2022 C++

一、可变参数模版

基本语法及原理

C++11 支持可变参数模板,也就是说支持可变数量参数的函数模板和类模板,可变数目的参数被称为参数包。

存在两种参数包:模板参数包,表示零或多个模板参数;函数参数包,表示零或多个函数参数。

我们用省略号来指出一个模板参数或函数参数表示一个包。在模板参数列表中,class… 或 typename… 指出接下来的参数表示零或多个类型列表;在函数参数列表中,类型名后面跟 … 指出接下来表示零或多个形参对象列表。

函数参数包可以用左值引用或右值引用表示,跟上一篇文章讲的普通模板一样,每个参数实例化时遵循引用折叠规则

#include <iostream>
#include <vector>
using namespace std;template<class ...Args>
class one
{;
};template<class ...Args>
void get(Args... args)
{;
}template<class ...Args>
void set(const Args&... args)
{;
}template<class ...Args>
void print(Args&&... args)
{;
}int main()
{get(2, "111111", 5 + 6);set(5.1, 'h', 'e');print(1.5, 5.1f, "haha");print("hehe");one<int, double, long long, char, float> get1;one<string, vector<long long>> get2;one<long long> get3;return 0;
}

可变参数模板的原理跟模板类似,本质还是在编译时实例化对应类型和个数的多个函数

这里可以使用 sizeof… 运算符去计算参数包中参数的个数。

#include <iostream>
using namespace std;template<class ...Args>
void print(Args&&... args)
{cout << sizeof...(args) << endl;
}int main()
{print(1, 2, 3);				// 3个print(1.5, 5.1f, "haha");	// 3个print();					// 0个print("hehe");				// 1个return 0;
}

包扩展

对于一个参数包,除了能计算它的参数个数,我们能做的唯一的事情就是扩展它,当扩展一个包时,我们还要提供用于每个扩展元素的模式,扩展一个包就是将它分解为构成的元素,对每个元素应用模式,获得扩展后的列表。我们通过在模式的右边放一个省略号 … 来触发扩展操作。

#include <iostream>
using namespace std;void print()				// 当参数为 0 时自动匹配无参 print 递归终止
{cout << endl;
}template<class T, class ...Args>
void print(T&& x, Args&&... args)
{cout << x << " ";		// 递归调用 print ,将接收到的第一个参数进行打印print(args...);			// 用包扩展将剩下的参数传给下一个 print
}// 这里用 print(1.5, 5.1f, "haha"); 为例
// 首先会实例化 void print(double x, float y, string z) 函数
// 先打印 x,再通过包扩展将 y, z 传给下一个 print
//void print(double x, float y, string z)
//{
//	cout << x << " ";
//	print(y, z);
//}
// 下一个实例化为 void print(float y, string z) 函数
// 先打印 y,再通过包扩展将 z 传给下一个 print
//void print(float y, string z)
//{
//	cout << y << " ";
//	print(z);
//}
// 下一个实例化为 void print(string z) 函数
// 打印 z,此时包扩展里的参数为 0,会匹配 print() 函数结束包扩展
//void print(string z)
//{
//	cout << z << " ";
//	print();
//}int main()
{print(1, 2, 3);print(1.5, 5.1f, "haha");print();print("hehe");return 0;
}

C++11 还支持更复杂的包扩展,直接将参数包依次展开作为实参给一个函数去处理。

#include <iostream>
using namespace std;template<class T>
int show(T&& x)
{cout << x << " ";return 0;
}template<class ...a>
void get(a&& ...x)
{;
}template<class ...Args>
class one
{
public:one() = default;
};template<class ...Args>
void print(Args&&... args)
{// 展开发生的位置一般在函数参数列表、成员初始化列表、属性列表、类模版参数列表等// show(args)... 表示每个参数都会调用一次 show()// 若有三个参数可表示为// show(one), show(two), show(three)// int arr[] = { 0, show(one), show(two), show(three) };// get(show(one), show(two), show(three));int arr[] = { 0, show(args)... };	// 数组执行顺序是从左到右//get(show(args)...);				// 函数参数执行是从右到左one<Args...> get;				// 类型可在类模板参数列表展开cout << endl;
}int main()
{print(1, 2, 3);print(1.5, 5.1f, "haha");print();print("hehe");return 0;
}

emplace系列接口

C++11 以后 STL 容器新增了 emplace 系列的接口,emplace 系列的接口均为模板可变参数,功能上兼容 push 和 insert 系列,但是 emplace 还支持新操作,假设容器为container<Type>,emplace 支持直接传入构造 Type 对象的参数,这样有些场景会更高效一些,可以直接在容器空间上构造 Type 对象。

emplace 系列相对于 insert 和 push 系列总体而言是更高效,可以平替后两者。

#include <iostream>
#include <vector>
using namespace std;int main()
{vector<pair<string, int>> arr;pair<string, int> get1 = { "1111111111", 1 };// 传左值一样走拷贝构造arr.push_back(get1);arr.emplace_back(get1);// 传右值一样走移动构造arr.push_back(pair<string, int>("22222222", 5));arr.emplace_back(pair<string, int>("22222222", 5));// push_back 会先 有参构造 再 移动构造arr.push_back({ "333333333", 10 });// emplace_back 会将参数直接传进去进行有参构造,没有移动构造,此时效率高一点arr.emplace_back("333333333", 10);return 0;
}

二、lambda表达式

lambda表达式语法

lambda 表达式本质是一个匿名函数对象,跟普通函数不同的是它可以定义在函数内部。

lambda 表达式语法使用层而言没有类型,所以一般是用 auto 或者 模板参数定义的对象 去接收 lambda 对象。

lambda表达式的格式: [capture-list](parameters)->return type { function body }

[capture-list] : 捕捉列表,该列表总是出现在 lambda 函数的开始位置,编译器根据 [] 来判断接下来的代码是否为 lambda 函数,捕捉列表能够捕捉上下文中的变量供 lambda 函数使用,捕捉列表可以传值和传引用捕捉。捕捉列表为空也不能省略。

(parameters) :参数列表,与普通函数的参数列表功能类似,但如果不需要参数传递,则可以将 () 省略。

->return type :返回值类型,用追踪返回类型形式去声明函数的返回值类型,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导。

{ function body } :函数体,函数体内的实现跟普通函数完全类似,在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量,函数体为空也不能省略。

#include <iostream>
using namespace std;int main()
{auto add = [](int x, int y)->int { return x + y; };cout << add(10, 20) << endl;// 1. [] 不能省略// 2. () 可以省略// 3. ->return type 可以省略// 4. {} 不能省略auto func1 = [] {};func1();auto swap = [](int& one, int& two)->void{int temp = one;one = two;two = temp;};int a = 100;int b = 1;cout << "a == " << a << endl << "b == " << b << endl << endl;swap(a, b);cout << "a == " << a << endl << "b == " << b << endl;return 0;
}

捕捉列表

lambda 表达式中默认只能用 lambda 函数体和参数中的变量,如果想用外层作用域中的变量就需要进行捕捉。

  1. 第一种捕捉方式是在捕捉列表中显示的传值捕捉和传引用捕捉,捕捉的多个变量用逗号分割。[ x, y, &z ] 表示 x 和 y 值捕捉,z 引用捕捉。
#include <iostream>
using namespace std;int main()
{int x = 0;int y = 10;int z = 100;int t = 1000;auto func1 = [x, y, &z]{cout << x << " " << y << " " << z << endl;};func1();return 0;
}
  1. 第二种捕捉方式是在捕捉列表中隐式捕捉,在捕捉列表写一个 “ = ” 表示隐式值捕捉,在捕捉列表写一个 “ & ” 表示隐式引用捕捉,这样 lambda 表达式中用了哪些变量,编译器就会自动捕捉哪些变量。
#include <iostream>
using namespace std;int main()
{int x = 0;int y = 10;int z = 100;int t = 1000;auto func1 = [=]{cout << "func1 " << x << " " << y << " " << z << endl;};auto func2 = [&]{cout << "func2 " << x << " " << y << " " << z << endl;};func1();func2();return 0;
}
  1. 第三种捕捉方式是在捕捉列表中混合使用隐式捕捉和显示捕捉。当使用混合捕捉时,第一个元素必须是 “ & ” 或 “ = ”,并且 “ & ” 混合捕捉时,后面的捕捉变量必须是值捕捉,同理 “ = ” 混合捕捉时,后面的捕捉变量必须是引用捕捉。[ =, &x ] 表示其他变量隐式值捕捉,x 引用捕捉;[ &, z, y ] 表示其他变量引用捕捉,z 和 y 值捕捉。
#include <iostream>
using namespace std;int main()
{int x = 0;int y = 10;int z = 100;auto func1 = [=, &x]{cout << "func1 " << x << " " << y << " " << z << endl;};auto func2 = [&, z, y]{cout << "func2 " << x << " " << y << " " << z << endl;};func1();func2();return 0;
}

lambda 表达式如果在函数局部域中,它可以捕捉 lambda 位置之前定义的变量,不能捕捉静态局部变量和全局变量,静态局部变量和全局变量也不需要捕捉,在 lambda 表达式中可以直接使用。这也意味着 lambda 表达式如果定义在全局位置,捕捉列表必须为空。

默认情况下, lambda 捕捉列表是被 const 修饰的,则传值捕捉的过来的对象不能修改,mutable 加在参数列表的后面可以取消其常量性,使用该修饰符后,传值捕捉的对象就可以修改了,但是修改的是形参对象,不会影响实参。使用该修饰符后,参数列表即使为空也不可省略

#include <iostream>
using namespace std;string str1 = "全局变量";auto func1 = []			// 定义在全局位置,捕捉列表必须为空{cout << str1 << endl;cout << "hello world!" << endl;};void test()
{string str4 = "另一个函数中的局部变量";static string str5 = "另一个函数中的静态局部变量";
}int main()
{string str2 = "局部变量";static string str3 = "静态局部变量";auto func2 = [&str2]{cout << str1 << endl;cout << str2 << endl;cout << str3 << endl;//cout << str4 << endl;		// 无法使用其他局部域变量//cout << str5 << endl;		// 无法使用其他局部域静态变量};func1();func2();int a = 10;int b = 20;int c = 30;int d = 40;cout << endl << a << " " << b << " " << c << " " << d << endl;auto func3 = [a, b, &c, &d]{//a = 0;	// 不可修改//b = 0;d = c = 1000;};func3();cout << a << " " << b << " " << c << " " << d << endl;// 传值捕捉是拷贝,并被 const 修饰,// mutable 可以去掉 const 属性,使得值捕捉变量可修改auto func4 = [a, b, &c, &d]()mutable ->void{a = 0;		// mutable 修饰后可修改,但不会影响外面被值捕捉的值b = 0;d = c = -99;};func4();cout << a << " " << b << " " << c << " " << d << endl;return 0;
}

lambda的原理

lambda 的原理和 范围for 很像,编译后从汇编指令层的角度看,压根就没有 lambda 和 范围for 这样的东西。范围for 底层是迭代器,而 lambda 底层是仿函数对象,也就说如果我们写了一个 lambda 以后,编译器会生成一个对应的仿函数的类。

仿函数的类名是编译器按一定规则生成的,保证不同的 lambda 生成的类名不同。

lambda 参数 / 返回类型 / 函数体 就是仿函数 operator() 的 参数 / 返回类型 / 函数体, lambda 的捕捉列表本质是生成的仿函数类的成员变量,也就是说捕捉列表的变量都是 lambda 类构造函数的实参,如果隐式捕捉,使用哪些就传哪些对象。

lambda的应用

一般使用的可调用对象只有函数指针和仿函数对象,函数指针的类型定义起来比较麻烦,仿函数要定义一个类,相对也会比较麻烦。使用 lambda 去定义可调用对象,既简单又方便。

lambda 在很多地方用起来也很好用。比如线程中定义线程的执行函数逻辑,智能指针中定制删除器等。

三、包装器

bind

std::bind 是一个函数模板,它是一个可调用对象的包装器,可以把它看做一个函数适配器,对接收的可调用对象进行处理后返回另一个可调用对象。 std::bind 可以用来调整参数个数和参数顺序

std::bind 在 <functional> 这个头文件中。

调用 std::bind 的一般形式: auto newCallable = std::bind(callable, arg_list);其中 newCallable 本身是一个可调用对象,arg_list 是一个逗号分隔的参数列表,对应给定的 callable 的参数。当调用 newCallable 时,newCallable 会调用 callable,并传给它 arg_list 中的参数。

arg_list 中的参数可能包含形如 _n 的名字,其中 n 是一个整数,这些参数是占位符,表示 newCallable 的参数,它们占据了传递给 newCallable 的参数的位置。数值 n 表示生成的可调用对象中参数的位置:_1 为 newCallable 的第一个参数,_2 为第二个参数,以此类推。_1 / _2 / _3… 这些占位符放在 placeholders 的一个命名空间中。

#include <iostream>
#include <iostream>
#include <functional>
using namespace std;using placeholders::_1;
using placeholders::_2;int add(int a, int b)
{return a + b;
}struct sub
{int operator()(int a, int b){return a - b;}
};int main()
{// bind 本身返回一个仿函数对象auto tadd = bind(add, _1, _2);auto tsub = bind(sub(), _1, _2);auto tmul = bind([](int a, int b) {return a * b; }, _1, _2);int a = 10;int b = 5;cout << tadd(a, b) << endl;cout << tsub(a, b) << endl;cout << tmul(a, b) << endl << endl;// bind 可以调整对象的参数顺序(但不常用)。// _1 代表第一个实参传入对象参数的位置// _2 代表第二个实参传入对象参数的位置// ... 其它同理auto radd = bind(add, _2, _1);auto rsub = bind(sub(), _2, _1);auto rmul = bind([](int a, int b) {return a * b; }, _2, _1);cout << radd(a, b) << endl;cout << rsub(a, b) << endl;cout << rmul(a, b) << endl << endl;// bind 调整对象参数个数(常用)。auto sub_10 = bind(sub(), _1, 10);auto _100_sub = bind(sub(), 100, _1);cout << sub_10(50) << endl;cout << _100_sub(-10) << endl;return 0;
}

function

std::function 是一个类模板,也是一个包装器。 std::function 的实例对象可以包装存储其他的可调用对象,包括函数指针、仿函数、 lambda 、 bind 表达式等,存储的可调用对象被称为 std::function 的目标。若 std::function 不含目标,则称它为空。调用空 std::function 的目标会导致抛出 std::bad_function_call 异常。

function 被定义 <functional> 头文件中。std::function - cppreference.com 是 std::function 的官方文件链接。

函数指针、仿函数、 lambda 等可调用对象的类型各不相同, std::function 的优势就是统一类型,对它们都可以进行包装,这样在很多地方就方便声明可调用对象的类型。

#include <iostream>
#include <functional>
using namespace std;int add(int a, int b)
{return a + b;
}struct sub
{int operator()(int a, int b){return a - b;}
};struct func
{static string getString(){return "得到字符串";}int getNumber(){return 10000;}
};int main()
{//function 的 <> 中参数放在 () 里,返回值在 () 的左边:function<string()> print = [] { return "hello world!"; };function<int(int, int)> tsub = sub();function<int(int, int)> tadd = add;cout << print() << endl;cout << tsub(10, 2) << endl;cout << tadd(10, 2) << endl;// 对于静态类成员函数function<string()> getstr1 = func::getString;function<string()> getstr2 = &func::getString;cout << getstr1() << endl;cout << getstr2() << endl;// 对于普通成员函数有麻烦的地方,因为有隐式的 this 指针,调用 function 时需要传入对象function<int(func&)> getnum1 = &func::getNumber;//function<int(func&)> getnum1 = func::getNumber;	普通成员函数需要取地址符号 '&'func one;cout << getnum1(one) << endl;// 解决方法:使用 bind 将对象的 this 指针进行绑定,方便调用function<int()> getnum2 = bind(&func::getNumber, func());cout << getnum2() << endl;return 0;
}

四、部分新内容添加

新的类功能

1.默认的移动构造和移动赋值

原来 C++ 类中,有 6 个默认成员函数:构造函数 / 析构函数 / 拷贝构造函数 / 拷贝赋值重载 / 取地址重载 / const 取地址重载,最后重要的是前 4 个,后两个用处不大,默认成员函数就是我们不写编译器会生成一个默认的。C++11 新增了两个默认成员函数,移动构造函数 和 移动赋值运算符重载。

如果没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个。则编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。

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

如果自己提供了移动构造或者移动赋值,编译器就不会自动提供拷贝构造和拷贝赋值。

2.声明时给缺省值

这部分内容可参考:(学习总结9)C++学习的初步总结 —— 三、缺省参数

3.defult 和 delete

C++11 可以更好的控制要使用的默认函数。假设要使用某个默认的函数,但是因为一些原因这个函数没有默认生成。如果提供了拷贝构造,就不会生成移动构造,则可以使用 default 关键字显示指定移动构造生成。

如果能想要限制某些默认函数的生成,在 C++98 中,是该函数设置成 private,并且只声明,这样只要其他人想要调用就会报错。在 C++11 中更简单,只需在该函数声明加上 = delete 即可,该语法指示编译器不生成对应函数的默认版本,称 = delete 修饰的函数为删除函数。

#include <iostream>
using namespace std;class One
{
public:One() = default;~One() = default;One(const One&) = default;One& operator=(const One&) = default;One(One&&) = default;One& operator=(One&&) = default;
};class Two
{
public:Two() = delete;
};int main()
{One get1;//Two get2;		// 默认构造已经删除,会报错return 0;
}

4.final 与 override

这部分内容可参考:
(学习总结17)C++继承 —— 四、派生类的默认成员函数 —— 实现一个不能被继承的类
(学习总结18)C++多态 —— 二、多态的定义及实现 —— 6. override 和 final 关键字

STL中一些变化

下图圈起来的就是 STL 中的新容器,但是实际上常用的是 unordered_map 和unordered_set。

在这里插入图片描述

STL 中容器的新接口也不少,最重要的就是右值引用和移动语义相关的 push / insert / emplace 系列接口 和 移动构造与移动赋值,还有 initializer_list 版本的构造等,容器的 范围for 遍历支持。

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

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

相关文章

东芝e-STUDIO2829A复印机提示“维护”该如何操作

东芝e-STUDIO2829A复印机基本参数: 产品类型 数码复合机 颜色类型 黑白 涵盖功能 复印/打印/扫描 最大原稿尺寸 A3 处 理 器 500MHz 内存容量 标配:512MB,选配:1GB 供纸容量 标配纸盒:350页(A4),最大容…

春秋杯-WEB

SSTI 可以看到主页那里有个登录测试之后为ssti {{4*4}} fenjing梭哈即可得到payload {{((g.pop.__globals__.__builtins__.__import__(os)).popen(cat flag)).read()}}file_copy 看到题目名字为file_copy&#xff0c; 当输入路径时会返回目标文件的大小&#xff0c; 通…

警惕IDEA 2024版重大Bug问题:LomBok失效、Gradle冲突、Spring Boot启动错误

一直以来我认为工具类的软件是越新越好&#xff0c;因为工具代表着一定的先进性&#xff1b;但是IDEA 2024好好的给我上了一课&#xff0c;比如lombok 不起作用、比如Spring Boot 3.4.x 启动报错、再比如MyBatis log plus冲突、再比如Gradle插件冲突. 一、Lombok 失效问题 请不…

GPT-5 传言:一场正在幕后发生的 AI 变革

新的一年&#xff0c;让我们从一个引人入胜的话题开始&#xff1a;如果我告诉你&#xff0c;GPT-5 并非虚构&#xff0c;而是真实存在呢&#xff1f;它不仅真实存在&#xff0c;而且正在你看不见的地方悄然塑造着世界。我的基本假设是&#xff1a;OpenAI 已经秘密开发出 GPT-5&…

【Unity3D】利用Hinge Joint 2D组件制作绳索效果

目录 一、动态绳索 &#xff08;可移动根节点&#xff09; 二、静态绳索 三、利用Skinning Editor(Unity2022.3.15f1正常使用) 四、注意事项 一、动态绳索 &#xff08;可移动根节点&#xff09; 动态绳索 DynamicRope空物体 Anchor和whitecircle是相同位置的物体&#xff…

【12】Word:张老师学术论文❗

目录 题目 ​NO2 NO3 NO4 NO5 NO6 NO7.8 题目 NO2 布局→页面设置→纸张&#xff1a;A4→页边距&#xff1a;上下左右边距→文档网格&#xff1a;只指定行网格→版式&#xff1a;页眉和页脚&#xff1a;页脚距边界&#xff1a;1.4cm居中设置论文页码&#xff1a;插入…

每日进步一点点(网安)

今日练习题目是PHP反序列化&#xff0c;也学习一下说明是序列化和反序列化 1.PHP序列化 序列化是指将数据结构或对象转换为可传输或可储存的格式的过程。这通常需要将数据转换为字节流或者其他编码格式&#xff0c;以便在不同系统和应用程序之间进行传输或存储 在PHP中&…

【专题一 递归】21. 合并两个有序链表

1.题目解析 2.讲解算法原理 解法:递归-> 重复的子问题 重复子问题 ->函数头的设计 合并两个有序链表--->Node dfs(l1&#xff0c;l2) 只关心某一个子问题在做什么事情 ->函数体的设计 比大小l1→next dfs( l1.next, l2)return l1 递归的出口 if(l1null)return l2…

OpenCV基础:获取子矩阵的几种方式

目录 相关阅读 方法一&#xff1a;使用切片操作 方法二&#xff1a;使用高级索引 方法三&#xff1a;使用条件筛选 方法四&#xff1a;使用 numpy 的 take 函数 相关阅读 OpenCV基础&#xff1a;矩阵的创建、检索与赋值-CSDN博客 OpenCV基础&#xff1a;图像运算-CSDN博客…

Visual Studio Community 2022(VS2022)安装方法

废话不多说直接上图&#xff1a; 直接上步骤&#xff1a; 1&#xff0c;首先可以下载安装一个Visual Studio安装器&#xff0c;叫做Visual Studio installer。这个安装文件很小&#xff0c;很快就安装完成了。 2&#xff0c;打开Visual Studio installer 小软件 3&#xff0c…

学习threejs,使用OrbitControls相机控制器

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.OrbitControls 相机控…

【2024年华为OD机试】 (B卷,100分)- 流水线(Java JS PythonC/C++)

一、问题描述 题目描述 一个工厂有 m 条流水线&#xff0c;来并行完成 n 个独立的作业&#xff0c;该工厂设置了一个调度系统&#xff0c;在安排作业时&#xff0c;总是优先执行处理时间最短的作业。 现给定流水线个数 m&#xff0c;需要完成的作业数 n&#xff0c;每个作业…

opencv图像基础学习

2.3图像的加密解密 源码如下&#xff1a; import cv2 import numpy as np import matplotlib.pyplot as plt def passImg():imgcv2.imread(./image/cat.jpg,0)h,wimg.shape#生成一个密码&#xff0c;加密key_imgnp.random.randint(0,256,size(h,w),dtypenp.uint8)img_addmcv2…

【机器学习】零售行业的智慧升级:机器学习驱动的精准营销与库存管理

我的个人主页 我的领域&#xff1a;人工智能篇&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01;&#x1f44d;点赞 收藏❤ 在当今数字化浪潮汹涌澎湃的时代&#xff0c;零售行业正站在转型升级的十字路口。市场竞争的白热化使得企业必须另辟蹊径&#xff0…

Navicat Premium 原生支持阿里云 PolarDB 数据库

近日&#xff0c;我司旗下的 Navicat Premium 软件通过了阿里云 PolarDB 数据库产品生态集成认证&#xff0c;这标志着 Navicat 通过原生技术全面实现了对秒级弹性、高性价比、稳定可靠的PolarDB 数据库三大引擎&#xff08;PolarDB MySQL版、PolarDB PostgreSQL版和 PolarDB f…

【SpringBoot应用篇】SpringBoot+MDC+自定义Filter操作traceId实现日志链路追踪

【SpringBoot应用篇】SpringBootMDC自定义Filter操作traceId实现日志链路追踪 解决的问题解决方案MDC具体逻辑ymllogback-spring.xmlTraceIdUtil操作工具类TraceIdFilter自定义过滤器GlobalExceptionHandler全局异常处理类TraceIdAspect切面UserController测试验证 多线程处理M…

python如何解析word文件格式(.docx)

python如何解析word文件格式&#xff08;.docx&#xff09; .docx文件遵从开源的“Office Open XML标准”&#xff0c;这意味着我们能用python的文本操作对它进行操作&#xff08;实际上PPT和Excel也是&#xff09;。而且这并不是重复造轮子&#xff0c;因为市面上操作.docx的…

python爬虫报错日记

python爬虫报错日记 类未定义 原因&#xff1a;代码检查没有问题**&#xff0c;位置错了**&#xff0c;测试代码包含在类里…… UnicodedecodeError错误 原因&#xff1a;字符没有自动转换成utf-8格式 KeyError&#xff1a;“href” 原因&#xff1a;前面运行正常&#x…

第34天:Web开发-PHP应用鉴别修复AI算法流量检测PHP.INI通用过滤内置函数

#知识点 1、安全开发-原生PHP-PHP.INI安全 2、安全开发-原生PHP-全局文件&单函数 3、安全开发-原生PHP-流量检测&AI算法 一、通用-PHP.INI设置 参考&#xff1a; https://www.yisu.com/ask/28100386.html https://blog.csdn.net/u014265398/article/details/109700309 …

基于PHP的校园新闻发布管理

摘要 近年来&#xff0c;随着互联网技术的迅速发展&#xff0c;人们获取新闻的渠道也变得越来越多样化&#xff0c;已经不再拘束于传统的报纸、期刊、杂志等纸质化的方式&#xff0c;而是通过网络满足了人们获得第一手新闻的愿望&#xff0c;这样更加有助于实现新闻的规范化管…