【C++高阶】:C++11的深度解析下

news2024/10/2 16:30:21

✨                                      彼方尚有荣光在,何须悲叹少年轻      🌏

📃个人主页:island1314

🔥个人专栏:C++学习

🚀 欢迎关注:👍点赞 👂🏽留言 😍收藏  💞 💞 💞


🚀前言

若没有了解C++11前面一部分知识的读者朋友们,可以看一下这篇文章:

【C++高阶】:C++11的深度解析上-CSDN博客

1、可变参数模板

1.1 概念

C++11的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板,相比C++98,类模版和函数模版中只能含固定数量的模版参数,可变模版参数无疑是一个巨大的改进。

  • 可变模版参数
  • 参数类型可变
  • 参数个数可变
  • 打印参数包内容

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

// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
//参数类型可变,参数个数可变
template <class ...Args>
void ShowList(Args... args)
{}

//以前的模板写法,固定了参数个数,类型不确定
template <class ...T>
void ShowList1(T... x)
{}

//对于Show调用
int main(){
	ShowList();
	ShowList(1);
	ShowList(1, "xxxxx");
}

其对sizeof的使用方法(不常见)

template <class ...Args>
void ShowList(Args... args){
	// 可变参数模版编译时解析
	// 下面是运行获取和解析,所以不支持这样用
	cout << sizeof...(args) << endl;
	for (size_t i = 0; i < sizeof...(args); i++){
		cout << args[i] << " ";//不支持这样打印参数包内容
	}
}

1.2 展开参数包的两种方式

递归函数方式展开参数包
//用一个无参的来进行匹配
void Print() //当是0个参数包的时候,就匹配到该函数
{
	cout << endl;
}

template <class T, class ...Args>
void Print(T&& x, Args&&... args) //三个参数包,先分析参数包类型
{
	cout << x << " "; //打印第一个参数
	Print(args...); //递归调用

//不能用这样来作0个参数包的截止
// 因为这里都是编译时推导的逻辑,而下面这个是运行时逻辑,在运行时判断,无法结束
	//if (sizeof...(args) == 0) return;
}

// 编译时递归推导解析参数
template <class ...Args>
void ShowList(Args&&... args){
	Print(args...);
}

int main(){
	ShowList();
	ShowList(1);
	ShowList(1, "xxxxx");
	ShowList(1, "xxxxx", 2.2);

	return 0;
}

对于递归函数解包其实质分析:

逗号表达式展开参数包
template <class T>
int PrintArg(T t){
	cout << t << " ";
	return 0;
}

template <class ...Args>
void ShowList(Args... args){
	int arr[] = { PrintArg(args)... };
	cout << endl;
}
 
// 编译推演生成下面的函数
void ShowList(int x, char y, std::string z) //参数包换算之后就是这样的
{
	int arr[] = { PrintArg(x),PrintArg(y),PrintArg(z) }; //编译器通过解包生成的就是这个
	cout << endl;
}

int main(){
	ShowList(1);
	ShowList(1, 'A');
	ShowList(1, 'A', std::string("sort"));

	return 0;
}

对于逗号表达式解包其实质分析:

//之前去掉逗号表达式通过返回值去掉的
template <class ...Args>
void ShowList(Args... args){
	//int arr[] = { cout << (args) << " "... };  //这样编译通过不了
	//ostream arr[] = { cout << (args) << " "... }; //ostream不支持拷贝,也通过不了
	int arr[] = { (cout<<(args)<<" ", 0)...}; //加个逗号表达式,0作那个初始化

	cout << endl;
}

void ShowList(int x, char y, std::string z) //相当于编译器生成了这个
{ //注意要有0
	int arr[] = { (cout<<(x)<<" ", 0), (cout << (y) << " ", 0), (cout << (z) << " ", 0) };

	cout << endl;
}

int main(){
	ShowList(1, 'A', std::string("sort"));
	return 0;
}

2. lambda表达式

2.1 概念

     在C++98中,如果想要对一个数据集合中的元素进行排序,可以使用sort函数。如果待排序元素为自定义类型,需要用户使用仿函数定义排序时的比较规则。随着C++语法的发展,人们开始觉得上面的写法太复杂了,每次为了实现一个algorithm算法,都要重新去写一个类,如果每次比较的逻辑不一样,还要去实现多个类,特别是相同类的命名,这些都给编程者带来了极大的不便。因此,在C++11语法中出现了Lambda表达式

2.2 lambda表达式语法

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

  • [capture-list]:捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[ ]判断接下来的函数是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用。
  •  (parameters):参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以
    连同()一起省略。

  • mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)

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

  • {statement}:函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获
    到的变量。

注意: 在lambda函数定义中,参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为空。因此C++11中最简单的lambda函数为:[]{}; 该lambda函数不能做任何事情

捕获列表说明:

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

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

代码示例1:

// lambda 匿名函数的对象
int main(){
	auto add1 = [](int x, int y)->int {return x + y; };
	cout << add1(1, 2) << endl;

	auto func1 = []()->int{
		cout << "hello bit" << endl;
		cout << "hello world" << endl;
		return 0;
	};
	func1();

	// 返回值类型可自动推导类型,所以可以省略
	// 无参数可以省略
    // 返回值一般都不能省略
	auto func2 = []{
		cout << "hello bit" << endl;
		cout << "hello world" << endl;
		return 0;
	};
	cout << func2() << endl;

    //无返回值时可以省略返回值,但是建议最好还是有返回值
	auto func3 = []{
		cout << "hello bit" << endl;
		cout << "hello world" << endl;
	};
	func3();

	return 0;
}

  代码示例2:

int main(){
	int a = 0, b = 1;

	auto swap1 = [](int& x, int& y){ //参数列表
		// 只能用当前lambda局部域和捕捉的对象
		int tmp = x;
		x = y;
		y = tmp;
	};
	swap1(a, b);

	// 传值捕捉本质是一种拷贝,并且const修饰了
	// mutable相当于去掉const属性,可以修改了
	// 但是修改了不会影响外面被捕捉的值,因为是一种拷贝
	auto swap2 = [a, b]()mutable{ //捕捉列表
		int tmp = a;
		a = b;
		b = tmp;
	};
	swap2();

	auto swap3 = [&a, &b](){ //这里 & 是引用
		int tmp = a;
		a = b;
		b = tmp;
	};
	swap3();
     
    //...此处Goods的结构体构造三个参数分别为string _name,int _price,_evaluate
    vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, 
        { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } }; 
    //bool返回类型可以省略
    sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)->bool
	{
		return g1._price > g2._price;
	});
    
	return 0;
}

注意:

  • 父作用域指包含lambda函数的语句块
  • 语法上捕捉列表可由多个捕捉项组成,并以逗号分割。比如:[=, &a, &b]:以引用传递的方式捕捉变量a和b,值传递方式捕捉其他所有变量 [&,a,this]:值传递方式捕捉变量a和this,引用方式捕捉其他变量。
  • 捕捉列表不允许变量重复传递,否则就会导致编译错误。 比如:[=, a]= 已经以值传递方式捕捉了所有变量,捕捉a重复。
  • 块作用域以外的lambda函数捕捉列表必须为空。
  • 在块作用域中的lambda函数仅能捕捉父作用域中局部变量,捕捉任何非此作用域或者非局部变量都会导致编译报错。
  • lambda表达式之间不能相互赋值,即使看起来类型相同。

总的来说,lambda表达式就是一个仿函数,底层被处理成为一个lambda_uuid的一个仿函数类。

代码示例如下:

int x = 0;
int main(){
	// 只能用当前lambda局部域和捕捉的对象和全局对象
	int a = 0, b = 1, c = 2, d = 3;
	// 所有值传值捕捉
	auto func1 = [=]{
		int ret = a + b + c + d + x;
		return ret;
	};
	func1();
	cout << "auto func1 = [=]:"<< a << " " << b << " "  << c << " " << d << endl;

	// 所有值传引用捕捉
	auto func2 = [&]{
		a++, b++, c++, d++;
		int ret = a + b + c + d;
		return ret;
	};
	func2();
	cout <<"auto func2 = [&]:"<< a << " " << b << " " << c << " " << d << endl;

	// 混合捕捉
	auto func3 = [&a, b]{
		a++;
		//b++; //由于b是传值捕捉,b就不能修改
		int ret = a + b;
		return ret;
	};
	func3();
	cout<<"auto func3 = [&a, b]:" << a << " " << b << " " << c << " " << d << endl;
	

	// 混合捕捉
	// 所有值以引用方式捕捉,d用传值捕捉
	auto func4 = [&, d]{
		a++,b++,c++;
		//d++; //故d不能修改
		int ret = a + b + c + d;
	};
	func4();
	cout << "auto func4 = [&, d]:" << a << " " << b << " "  << c << " " << d << endl;


	auto func5 = [=, &d](){
		//a++, b++, c++; //a,b,c都不能修改
		d++; 
		int ret = a + b + c + d;
	};
	func5();
	cout << "auto func5 = [=, &d]():" << a << " " << b << " " << c << " " << d << endl;

	auto func6 = [=, &d]() mutable {
		a++, b++, c++, d++; //都可以修改,但是只有d对外面的值有影响
		int ret = a + b + c + d;
	};
	func6();
	cout << "auto func6 = [=, &d]() mutable:" << a << " " << b << " "<< c << " " << d << endl;
	
	return 0;
}

2.3 函数对象与lambda表达式

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

      lambda表达式可以很好的代替仿函数使用,如果使用仿函数,阅读代码的人想要知道其中的逻辑还需要去对应的地方去找,而lambda表达式可以很好的避免这个问题,增强了代码的可读性:

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

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

//捕捉列表的对象是成员变量存在lambad类对象中
//捕捉的本质是构造函数的初始化列表

int main(){
	// 函数对象
	double rate = 0.015;
	Rate r1(rate);
	cout << r1(10000, 2) << endl;

	// lambda
	auto r2 = [rate](double monty, int year)->double{
		return monty * rate * year;
	};
	cout << r2(10000, 2) << endl;

	int x = 1, y = 2;
	auto r3 = [=](double monty, int year)->double{
		return monty * rate * year;
	};
	cout << r3(10000, 2) << endl;

	return 0;
}

从使用方式上来看,函数对象与lambda表达式完全一样。
函数对象将rate作为其成员变量,在定义对象时给出初始值即可,lambda表达式通过捕获列表可
以直接将该变量捕获到。

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

3、包装器

3.1 function

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

ret = func(x);

上面func可能是什么呢?那么func可能是函数名?函数指针?函数对象(仿函数对象)?也有可能是lamber表达式对象?所以这些都是可调用的类型!如此丰富的类型,可能会导致模板的效率低下!

举个例子:

template<class F, class T>
T useF(F f, T x){
	static int count = 0;
	cout << "count:" << ++count << endl;
	cout << "count:" << &count << endl;
	return f(x);
}
double f(double i){
	return i / 2;
}
struct Functor{
	double operator()(double d){
		return d / 3;
	}
};
int main(){
	// 函数名
	cout << useF(f, 11.11) << endl;
	// 函数对象
	cout << useF(Functor(), 11.11) << endl;
	// lamber表达式
	cout << useF([](double d)->double { return d / 4; }, 11.11) << endl;
	return 0;
}

通过上面的程序验证,我们会发现useF函数模板实例化了三份。
但是包装器可以很好的解决上面的问题

std::function在头文件<functional>
// 类模板原型如下
template <class T> function;   // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;


模板参数说明:

  • Ret : 被调用函数的返回类型
  • Args… :被调用函数的形参

使用方法如下:

#include<functional>

int f(int a, int b){
	return a + b;
}

struct Functor{
public:
	int operator() (int a, int b){
		return a + b;
	}
};

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)> f1 = f; //类型不匹配会报错的
	function<int(int, int)> f1 = f; //实例化方式
	function<int(int, int)> f2 = Functor();
	function<int(int, int)> f3 = [](int a, int b) {return a + b; };

	cout << f1(1, 1) << endl;
	cout << f2(1, 1) << endl;
	cout << f3(1, 1) << endl;

	//由于成员函数的函数名受类域限制,因此下面我们需要指定函数名
	// 包装静态成员函数
	function<int(int, int)> f4 = &Plus::plusi;
	cout << f4(1, 1) << endl;

	// 包装非静态成员函数
	//非静态的成员函数要取函数指针的时候,需要加个&,静态可以不加
	//function<double(double, double)> f5 = &Plus::plusd;  
	
	//由于上面编译通过,因为其还有一个隐含的this参数,下面有两种解决方案
	//方法一:传指针
	function<double(Plus*, double, double)> f5 = &Plus::plusd;
	Plus pd;
	cout << f5(&pd, 1.1, 1.1) << endl;

	//方法二:不传指针,只传对象
	function<double(Plus, double, double)> f6 = &Plus::plusd;
	cout << f6(pd, 1.1, 1.1) << endl;
	cout << f6(Plus(), 1.1, 1.1) << endl;

	return 0;
}

有了包装器,如何解决模板的效率低下,实例化多份的问题呢?

template<class F, class T> 
T useF(F f, T x) {
	static int count = 0;
	cout << "count:" << ++count << endl;
	cout << "count:" << &count << endl;
	return f(x);
}

double f(double i) {
	return i / 2;
}
struct Functor{
	double operator()(double d){
		return d / 3;
	}
};
int main(){
	// 函数名
	std::function<double(double)> func1 = f;
	cout << useF(func1, 11.11) << endl;
	// 函数对象
	std::function<double(double)> func2 = Functor();
	cout << useF(func2, 11.11) << endl;
	// lamber表达式
	std::function<double(double)> func3 = [](double d)->double{ return d / 4; };
	cout << useF(func3, 11.11) << endl;
	return 0;
}
 

值得注意的是:对成员函数进行包装时,需要取地址

包装器的一些其他使用场景:

逆波兰表达式求值

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<int> st;
        map<string, function<int(int, int)>>opFuncMap = {
            {"+", [](int x, int y){return x + y;}},
            {"-", [](int x, int y){return x - y;}},
            {"*", [](int x, int y){return x * y;}},
            {"/", [](int x, int y){return x / y;}}
        };
        // //方法一:
        // for(auto &e : tokens){
        //     if(e == "+" || e == "-" || e == "*" || e == "/"){
        //         int r = st.top();st.pop();
        //         int l = st.top(); st.pop();
        //         switch(e[0]){
        //             case '+':
        //                 st.push(l + r);
        //                 break;
        //             case '-':
        //                 st.push(l - r);
        //                 break;
        //             case '*':
        //                 st.push(l * r);
        //                 break;
        //             case '/':
        //                 st.push(l / r);
        //                 break;
        //         }
        //     }
        //     else {
        //         st.push(stoi(e));
        //     }
        // }
        
        //方法二:
        for(auto &e : tokens)
        {
            //if(e == "+" || e == "-" || e == "*" || e == "/")
            if(opFuncMap.count(e)) {  // == 1就是操作符
                int r = st.top();st.pop();
                int l = st.top();st.pop();
                int ret = opFuncMap[e](l,r);
                st.push(ret);
            }
            else{
                st.push(stoi(e));
            }
        }

        return st.top();
    }
};

3.2 bind

        bind是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。一般而言,我们用它可以把一个原本接收N个参数的函数fn,通过绑定一些参数,返回一个接收M个(M可以大于N,但这么做没什么意义)参数的新函数。同时,使用bind函数还可以实现参数顺序调整等操作。

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

使用如下:

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

int SubX(int a, int b, int c){return (a - b - c) * 10;}

//可以用命名空间展示出来
using placeholders::_1;
using placeholders::_2;
using placeholders::_3;

int main()
{
	//auto sub1 = bind(Sub,placeholders::_1,placeholders::_2);
	// bind 本质返回的一个仿函数对象
	// 调整参数顺序(不常用)
	// _1代表第一个实参
	// _2代表第二个实参

	auto sub1 = bind(Sub, _1, _2); //代表Sub(_1, _2)
	cout << sub1(10, 5) << endl;//10 相当于_1, 5相当于_2

	auto sub2 = bind(Sub, _2, _1); //代表Sub(_2, _1)
	cout << sub2(10, 5) << endl; //5 相当于_2, 10相当于_1

	//调整参数个数(常用)
	auto sub3 = bind(Sub, 10, _1); //代表Sub(100, _1)
	cout << sub3(5) << endl; //5 相当于_1

	auto sub4 = bind(Sub, _1, 10); //代表Sub(_1, 10)
	cout << sub4(5) << endl; //5 相当于_1

	// 分别绑死第123个参数
	auto sub5 = bind(SubX, 10, _1, _2);
	cout << sub5(5, 1) << endl;

	auto sub6 = bind(SubX, _1, 10, _2);
	cout << sub6(5, 1) << endl;

	auto sub7 = bind(SubX, _1, _2, 10);
	cout << sub7(5, 1) << endl;

	function<double(Plus, double, double)> f6 = &Plus::plusd;
	Plus pd;
	cout << f6(pd, 1.1, 1.1) << endl;
	cout << f6(Plus(), 1.1, 1.1) << endl;

	// bind一般用于,绑死一些固定参数
	function<double(double, double)> f7 = bind(&Plus::plusd, Plus(), _1, _2);
	cout << f7(1.1, 1.1) << endl;


	//auto func1 = [](double rate, double money, int year)->double {return monty * rate * year; }; //单利
	auto func1 = [](double rate, double money, int year)->double { //复利
		double ret = money;
		for (int i = 0; i < year; i++)
		{
			ret += ret * rate;
		}

		return ret - money;
	};

	function<double(double)> func3_1_5 = bind(func1, 0.015, _1, 3);
	function<double(double)> func5_1_5 = bind(func1, 0.015, _1, 5);
	function<double(double)> func10_2_5 = bind(func1, 0.025, _1, 10);
	function<double(double)> func20_3_5 = bind(func1, 0.035, _1, 30);

	cout << func3_1_5(1000000) << endl;
	cout << func5_1_5(1000000) << endl;
	cout << func10_2_5(1000000) << endl;
	cout << func20_3_5(1000000) << endl;

	return 0;
}

包装器的作用:统一可调用对象的类型,指明了参数和返回值类型

不包装前可能存在很多问题:

  • 函数指针太复杂,不方便理解。
  • 仿函数类型是一个类名,没有指明参数和返回值,需要去operator()才能看出来。
  • lambda表达式在语法层看不到类型。

📖总结

以上就是C++11的全部内容啦

本篇到此就结束,希望我的这篇博客可以给你提供有益的参考和启示,感谢大家支持!!!

祝大家天天顺心如意。

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

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

相关文章

浅谈 Spring AOP框架 (1)

文章目录 一、什么是 Spring AOP二、为什么要使用 Spring AOP三、AOP 的一些应用场景四、AOP 的组成五、如何使用 Spring AOP六、Spring AOP 的实现原理6.1、JDK 和 CGLIB 的区别 一、什么是 Spring AOP AOP (Aspect Oriented Programming) &#xff1a;面向切面编程&#xff…

[CTF]-PWN:格式化字符串漏洞题综合解析

printf型格式化字符串漏洞&#xff1a; 任意地址写&#xff1a; 32位&#xff1a; 例题&#xff08;inndy_echo&#xff09;&#xff1a; 有格式化字符串漏洞&#xff0c;可以修改printf的got表内地址为system&#xff0c;传参getshell 解法一&#xff1a; 在32位中可以使…

C语言 操作符

操作符分多种&#xff1a;算术操作符&#xff0c;移位操作符&#xff0c;位操作符&#xff0c;赋值操作符&#xff0c;单目操作符&#xff0c;关系操作符&#xff0c;逻辑操作符&#xff0c;条件操作符&#xff0c;逗号表达式&#xff0c;下标引用&#xff0c;函数调用和结构成…

【Web开发手礼】探索Web开发的秘密(十四)-Vue2(1)Node.js的安装、Vue入门

主要介绍了Node.js的安装教程、Vue2常用的一些指令、声明周期&#xff01;&#xff01;&#xff01; 文章目录 前言 Node.js安装 选择安装目录 验证NodeJS环境变量 配置npm的全局安装路径 切换npm的淘宝镜像 安装Vue-cli ​编辑 Vue2入门 引入vue.js文件 入门代码 常用指令 生…

眼在手外-机器人坐标系与相机坐标系标定方法

1 眼在手外坐标系概述 实现机械臂和相机的手眼标定&#xff0c;就是要通过双目相机坐标系、机械臂坐标系和机械臂 末端执行器三者的坐标系转换&#xff0c;求出手眼转换矩阵。设双目相机坐标系为 Oc&#xff0c;标定板坐标 系为 Ow&#xff0c;末端执行器坐标系为 Oe&#xff0…

【学习笔记】Day 3

一、进度概述 1、作业1 2、组会会议纪要——没太听懂&#xff0c;得再看 二、详情 1、作业1 &#xff08;1&#xff09;在python中&#xff0c;想要使output为图片&#xff0c;需要用的matplotlib库&#xff0c;这里做简单的整理&#xff0c;以便更好的理解代码。 …

ADC的介绍和工作原理

一&#xff0c;什么是ADC&#xff1f; Analog-to-Digital Converter&#xff0c;指模拟/数字转换器 什么是ADC&#xff1a; ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量&#xff0c;建立模拟电路到数字电路的桥梁 SUCH AS: 12 位 ADC 是一种逐次逼近…

免费【2024】springboot 导师选择管理系统的管理设计与实现

博主介绍&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围&#xff1a;SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化…

从零入门 AI for Science(AI+药物) 笔记 #Datawhale AI 夏令营

&#x1f496;使用平台 我的Notebook 魔搭社区 https://modelscope.cn/my/mynotebook/preset . 魔搭高峰期打不开Task3又换回飞桨了 吧torch 架构换成了 飞桨的paddle 飞桨AI Studio星河社区-人工智能学习与实训社区 https://aistudio.baidu.com/projectdetail/8191835?cont…

解决电脑数字小键盘经常自动关闭的问题

本文解决了电脑数字小键盘经常自动关闭的问题&#xff0c;可供大家参考。 winR&#xff0c;输入regedit打开注册表 依次选择HKEY_USERS DEFAULT Control Panel---Keyboard”&#xff0c;将InitialKeyboardIndicators的值由2147483648改为80000002&#xff0c;即可解决。

xss漏洞(四,xss常见类型)

本文仅作为学习参考使用&#xff0c;本文作者对任何使用本文进行渗透攻击破坏不负任何责任。 前言&#xff1a; 1&#xff0c;本文基于dvwa靶场以及PHP study进行操作&#xff0c;靶场具体搭建参考上一篇&#xff1a; xss漏洞&#xff08;二&#xff0c;xss靶场搭建以及简单…

[240804] OpenTofu 1.8.0 发布,带来更友好的编码体验 | 生成式 AI 滥用现象分析

目录 OpenTofu 1.8.0 发布&#xff0c;带来更友好的编码体验生成式 AI 滥用现象分析 OpenTofu 1.8.0 发布&#xff0c;带来更友好的编码体验 OpenTofu 1.8.0 现已发布&#xff0c;主要功能包括&#xff1a; 变量和局部值的早期求值: 现在可以在模块源、后端配置和状态加密等更…

西部数据HDD和闪存业务均在复苏加速

财务概览 西部数据&#xff08;Western Digital&#xff09;截至2024年7月341日的第四财季营收达到37.6亿美元&#xff0c;同比增长41%&#xff0c;超出预期的33亿美元&#xff0c;净利润达到3.3亿美元&#xff0c;与去年同期亏损7.15亿美元形成鲜明对比。整个2024财年的营收增…

C语言初阶(12)

1.调试的基本 调试是发现并解决C语言编译的bug的方法。 调试基本步骤是1.发现程序错误的存在 2.以隔离、消除等方式对错误进行定位 3.确定错误产生的原因 4.提出纠正错误的解决办法 5.对程序错误予以改正&#xff0c;重新测试 编译版本release和debug版本&#xff0c;releas…

一例AutoHotkey语言生成的文件夹病毒分析

概述 这是一个使用AutoHotkey语言编写的文件夹病毒&#xff0c;使用ftp服务器来当作C2&#xff0c;通过U盘传播&#xff0c;样本很古老&#xff0c;原理也很简单&#xff0c;这种语言的样本还是第一次见到&#xff0c;记录一下。 样本的基本信息 PE32库: AutoIt(3.XX)[-]编译…

Cocos Creator2D游戏开发(11)-飞机大战(9)-cocos发布微信小游戏

准备工作: ① cocosCreator ②微信小游戏开发者工具 第一步: cocosCreator 打包编译,设置发布平台,默认场景,设备方向,AppId 最后点击构建 等待构建完成 第二步: 导入微信开发者工具 就是cocos Creator 中构建发布里面的发布路径 然后编译: 剩下的就是微信开发者工具…

Bootstrap框架介绍

1、Bootstrap框架的下载和使用 Bootstrap框架是基于HTML、CSS、JavaScript的CCS/HTML框架,是一种封装好的前端框架。它包括js、css、front字体样式库。该框架下载链接:https://v3.bootcss.com/getting-started/#download,并选择下载源码。 建一个BootstrapDemo文件夹,将js…

反激式电源为什么上电最容易烧MOS管?

大家好,这里是大话硬件。 这篇文章总结一下最近在研究的反激电源RCD吸收回路和VDS尖峰问题。这也是为什么MOS管在开机容易被电压应力击穿的原因。 下图是反激电源变压器部分的拓扑。 在MOS开通时,VDS上电压: 由于Rdson比较小,MOS开通时,VDS电压也较小。此时,MOS漏极电…

对象存储及其相关概念介绍

对象存储是一种用来描述解决和处理离散单元&#xff08;这些离散单元被称作为对象&#xff09;的方法的通用术语。以下是关于对象存储的详细解析&#xff1a; 一、基本概念 定义&#xff1a;对象存储&#xff0c;也叫做基于对象的存储&#xff0c;是一种将数据以对象的形式进…

【过题记录】8.4(robocom补题,网络流)

今天robocom国赛&#xff0c;因为一个bool函数忘记return 1而裂开(错失21分) 以此为戒 贪心消消乐 其实就是一个求最大子矩阵和的板子题 利用最大子段和的思想 枚举矩阵中的上下界 压成一维后利用最大子段和 O ( n ) O(n) O(n)处理 复杂度 O ( n 3 ∗ k ) O(n^3*k) O(n3∗k) k为…