C++之模板和可变模板参数

目录

一、为什么要定义模板

模板的优点:

二、模板的定义

三、模板的类型

3.1、函数模板

3.1.1、实例化:隐式实例化与显示实例化

3.1.2、函数模板、普通函数间的关系

3.1.2.1易错点:

3.1.2.2重载例子:

3.1.2.3优先级与执行顺序:

3.1.3、模板头文件与实现文件

3.1.4、模板的特化:偏特化与全特化

3.1.5、函数模板的参数类型

3.1.6、成员函数模板

3.2、类模板

注意:

模板的嵌套:

模板做参数:

四、可变模板参数

4.1、模板参数包

4.2、函数参数包

4.3、可变模板参数的优势(有两条)


一、为什么要定义模板

现在的 C++ 编译器实现了一项新的特性:模板( Template ),简单地说, 模板 是一种通用的描述机制,也就是说,使用模板允许使用 通用类型 来定义函数或类等,在使用时,通用类型可被具体的类型,如 int double 甚至是用户自定义的类型来代替。模板引入一种全新的编程思维方式,称为 泛型编程 通用编程
泛型编程:不是针对某一种具体的类型进行编程,而是针对一类类型进行编程,将类型抽象成T(类型参数化)
#形象地说,把函数比喻为一个游戏过程,函数的流程就相当于 游戏规则。
#在以往的函数定义中,总是指明参数是 int 型还是 double 型等等,这就像是为张三(好比 int 型)和李四(好比 double 型)比赛制定规则。可如果王五( char* 型)和赵六( bool 型)要比赛,还得提供一套函数的定义,这相当于又制定了一次规则,显然这是很麻烦的。
#模板的的引入解决了这一问题 ,不管是谁和谁比赛,都把他们定义成 A B 比赛,制定好了 A B 比赛的规则(定义了关于 A B 的函数)后,比赛时只要把 A 替换成张三,把 B 替换成李四就可以了, 大大简化了程序代码量 维持了结构的清晰 大大提高了 程序设计 的效率 。该过程称为 类型参数化
# 强类型程序设计中,参与运算的所有对象的类型在编译时即确定下来,并且编译程序将进行严格的类型检查。为了解决 强类型的严格性和灵活性的冲突。有以下3中方式解决:
#带参数宏定义 (原样替换)
#重载函数 (函数名相同,函数参数不同)
#模板 (将数据类型作为参数)
#include <iostream>
using namespace std;

int add(int x, int y)			//定义两个int类型相加的函数
{
	return x + y;
}

double add(double x, double y) //重载两个double类型相加的函数
{
	return x + y;
}

char* add(char* px, char* py)			//重载两个字符数组相加的函数
{
	return strcat(px, py);				//调用库函数strcat
}

int main()
{
	cout << add(1, 2) << endl;			//调用add(const int,const int)

	cout << add(3.0, 4.0) << endl;		//调用add(const double,const double)

	char x[10] = "Hello ";				//创建字符数组,注意要留够大小
	char y[] = "C++";
	cout << add(x, y) << endl;			//调用add(char*,char*)

	return 0;
}

模板的优点:

1、简化程序,少写代码,维持结构的清晰,大大提高程序的效率。
2、解决强类型语言的严格性和灵活性之间的冲突。
2.1、带参数的宏定义(原样替换)
2.2、函数重载(函数名字相同,参数不同)
2.3、模板(将数据类型作为参数)
3、强类型语言程序设计:C/C++/Java等,有严格的类型检查,如int a = 10,在编译时候明确变量的类型,如果有 问题就可以在编译时发现错误,安全,但是不够灵活,C++引进auto其实就是借鉴弱类型语言的特征。
弱类型程序语言设计:js/python等,虽然也有类型,但是在使用的时候直接使用let/var number,不知道变量具体类型,由编译器解释变量类型,属于解释型语言。如果有错,到运行时才发现,虽然灵活,但是不安全。

二、模板的定义

#模板的引入使得函数定义摆脱了类型的束缚,代码更为高效灵活。 C ++中,通过下述形式定义一个模板:

template <class T...>

template<typename T,....>

#早期模板定义使用的是 class ,关键字 typename 是最近才加入到标准中的,相比 class typename 更容易体现 类型 的观点,虽然两个关键字在模板定义时是等价的,但从代码兼容的角度讲,使用 class 较好一些。
#模板有 函数模板 类模板 之分。通过 参数实例化 构造出具体的函数或类,称为 模板函数 模板类
//模板
#include <iostream>
#include <string>					//使用标准类库中提供的string类时必须包含此头文件
using namespace std;

template <typename T>
T add(const T &a, const T &b)
{
	return a + b;
}

int main()
{
	cout << add(10, 20) << endl;	//调用add(const int,const int)
	cout << add(1.0, 2.0) << endl;	//调用add(const double,const double)
	string x("Hello,"), y("world");
	cout << add(x, y) << endl;		//调用add(string,string)

	return 0;
}
#代码中的 add 函数便是一个函数模板, 编译器 根据函数模板的定义,检查传入的参数类型,生成相应的函数,并调用之。 函数模板 的定义形式如下:

  template <模板参数表>

  返回类型 函数名(参数列表)

  {  //函数体  }

#关键字 template 放在模板的定义与声明的最前面,其后是用逗号分隔的 模板参数表 ,用尖括号( <> )括起来。 模板参数表不能为空 ,模板参数有两种类型:
#class typename 修饰的类型参数,代表一种类型
# 非类型参数 表达式 , 必须是 整型类型 ,使用已知类型符,代表一个常量
//带非类型参数
template <typename T, int NUM>
T fun(T a)
{
	return a * NUM;
}

//调用
cout << fun<int, 4>(3) << endl;
#返回类型和函数的参数列表中可以包含类型参数,在函数中可以使用模板参数表中的常量表达式,如:
template <class T1,class T2,int number>

double fun(T1 a,int b,T2 c)

{

  return a * (number + b) * c;

  //函数体,其中number可以作为一个int型常量来使用

}

三、模板的类型

函数模板与类模板。通过参数实例化构造出具体的函数或者类,称为模板函数或者模板类。

3.1、函数模板

template <typename T>//模板参数列表
T add(T x, T y)
{
  cout << "T add(T, T)" << endl;
  return x + y;
}

                        模板参数推导(在实参传递的时候进行推导)
函数模板------------------------------------------------------------------------模板函数
                                            实例化

3.1.1、实例化:隐式实例化与显示实例化

#函数模板实际上不是个完整的函数定义,因为其中的类型参数还不确定,只是定义了某些类型的角色(或变量)在函数中的操作形式,因此,必须将 模板参数实例化 才能使用函数,用模板实例化后的函数也称为模板函数 .
#分为 隐式实例化 显式实例化
显示实例化:显示的将类型参数写出来,以免靠编译器去推导。
隐式实例化:不主动写出参数类型,靠编译器自己推导(类似与auto)
//12-3 函数模版的隐式实例化
#include <iostream>
using namespace std;

template <class T>
T Max(T x, T y);	//函数模版的申明

int main()
{	
	int intX = 1, intY = 2;
	double dblX = 3.9, dblY = 2.9;
	cout << Max(intX, intY) << endl;	//实参为int型,生成int型模板函数,并对第二个参数进行检查
	//或者cout << Max<int>(intX, intY) << endl;
	cout << Max(dblX, dblY) << endl;	//实参为double型,生成double型模板函数,并对第二个参数进行检查
	//或者cout << Max<double>(dblX, dblY) << endl;

	return 0;
}

template <class T>
T Max(T x, T y)		//函数模版的实现
{
	return (x > y ? x : y);
}

3.1.2、函数模板、普通函数间的关系

1、函数模板与普通函数是可以进行重载的
2、普通函数优先于函数模板执行
3、函数模板与函数模板之间也是可以进行重载的

3.1.2.1易错点:
# 函数模板支持重载 ,既可以模板之间重载(同名模板),也可以实现模板和普通函数间的重载,但模板的重载相比普通函数的重载要复杂一点,首先看一个例子:

template <class T1,class T2>

T1 Max(T1 a,T2 b){……}

    与

template <class T3,class T4>

T3 Max(T3 c,T4 d){……}

#看似不同的两个模板,仔细分析后发现,其本质是一样的,如果调用 Max(2,3.5); ,都实例化为 Max(int,double); ,会出现重复定义的错误。

      仅仅依靠返回值不同的模板重载也是不合法的,如:

template <class T1,class T2>

T1 Greater(T1 a,T2 b){……}

     

template <class T3,class T4>

                                        T3* Greater(T3 c,T4 d){……}

3.1.2.2重载例子:
//1函数模板和确定数据类型的函数的重载
#include <iostream.h>

template < class T >
T Max(T x, T y);

int Max(int x, int y)
{
	return x > y ? x : y;
}

int main()
{
	int intX = 1, intY = 2;
	double dblX = 3.0, dblY = 2.9;
	
	cout << Max(intX, intY) << endl;			//调用Max(int,int)

	cout << Max<double>(dblX, dblY) << endl;		//显示实例化为double型,生成double型模板函数
	cout << Max('A', '8') << endl;				//隐式实例化char型,生成char型模板函数
	return 0;
}

template <class T>
T Max(T x, T y)
{
	return x > y ? x : y;
}

//2函数模板和函数模板的重载
#include <iostream.h>

template < class T >
T Max(T x, T y);

template <class T>
T Max(T x, T y, T z)
{
	return x > y ? (x > z ? x : z) : (y > z ? y : z);
}

int main()
{
	int intX = 1, intY = 2, intZ = 3;
	double dblX = 3.0, dblY = 2.9;
	
	cout << Max<int>(intX, intY) << endl;	//调用实例化的Max(int,int)
	cout << Max<int>(intX, intY, intZ) << endl;	//调用实例化的Max(int,int,int)
	cout << Max<double>(dblX, dblY) << endl;	//显示实例化为double型,生成double型模板函数
	cout << Max('A', '8') << endl;			//隐式实例化char型,生成char型模板函数
	return 0;
}

template <class T>
T Max(T x, T y)
{
	return x > y ? x : y;
}

//3 普通函数模板和数组的重载
#include <iostream>
using namespace std;

template <typename T>
T MAX(T a, T b)
{
	return a > b ? a : b;
}

template <typename T>
T MAX(T a[], int n)
{
	T max = a[0];
	for(int i = 1; i < n; i++)
	{
		if(max < a[i])
		{
			max = a[i];
		}
	}

	return max;
}

int main()
{
	cout << MAX(3, 4) << endl;	//或cout << MAX<int>(3, 4) << endl;

	int a[] = {2, 9, 7, 3, 8, 5};
	cout << MAX(a, sizeof(a) / sizeof(a[0])) << endl;//或cout << MAX<int>(a, sizeof(a) / sizeof(a[0])) << endl;
	return 0;
}

3.1.2.3优先级与执行顺序:
int Max(int i1,int i2)
{
	cout<<"Normal Max"<<endl;
	return i1>i2? i1:i2;
}
template<class T> 
T Max(T t1, T t2)
{
	cout<<"Template Max,sizeof(t1):"<<sizeof(t1)<<endl;
	return t1>t2? t1:t2;
}
int main(int argc, char* argv[])
{
	int i1=1,i2=9;
	char c1='a',c2='b';
	int iRet=Max(i1,i2);			//调用普通函数int Max(int i1,int i2)
	char cRet=Max(c1,c2);			//调用模板实例化生成的char Max(char a,char b)
	int iRet2=Max(c1,c2);			//调用模板实例化生成的char Max(char a,char b),最后将返回值隐式转换成int型
	int cRet2=Max<char>(i1,i2);		//调用模板实例化生成的char Max(char a,char b),
	return 0;
}

3.1.3、模板头文件与实现文件

注:模板不能写成头文件与实现文件形式(类似inline函数),或者说不能将声明与实现分开,这样会导致编译报错。分开可以编译,但是在链接的时候是有问题的。

3.1.4、模板的特化:偏特化与全特化

template <> //此处模板的参数只有一个,全部特化出来就是全特化
const char *add(const char *pstr1, const char *pstr2)
{
  size_t len = strlen(pstr1) + strlen(pstr2) + 1;
  char *ptmp = new char(len);
  strcpy(ptmp, pstr1);
  strcat(ptmp, pstr2);
  return ptmp;;
}

具体的模板特化例子:

template <typename T>
T add(T x, T y)
{
  cout << "T add(T, T)" << endl;
  return x + y;
}
template <typename T>
T add(T x, T y, T z)
{
  cout << "T add(T, T, T)" << endl;
  return x + y + z;
}
int add(int x, int y)
{
  cout << "int add(int, int)" << endl;
  return x + y;
}
double add(double x, double y)
{
  cout << "double add(double, double)" << endl;
  return x + y;
}
template <>
const char *add(const char *pstr1, const char *pstr2)
{
  size_t len = strlen(pstr1) + strlen(pstr2) + 1;
  char *ptmp = new char(len);
  strcpy(ptmp, pstr1);
  strcat(ptmp, pstr2);
  return ptmp;;
}
void test()
{
  int ia = 3, ib = 4, ic = 5;
  double da = 1.1, db = 5.5;
  char ca = 'a', cb = 1;
  string s1 = "hello";
  string s2 = "world";
  const char *pstr1 = "hubei";
  const char *pstr2 = ",wuhan";
  cout << "add(ia, ib) = " << add(ia, ib) << endl;//隐式实例化
  cout << "add(da, db) = " << add<double>(da, db) << endl;//显示实例化
  cout << "add(ca, cb) = " << add(ca, cb) << endl;
  cout << "add(s1, s2) = " << add(s1, s2) << endl;
  /* cout << "add(ia, db) = " << add(ia, db) << endl;//函数模板必须进行严格的
推导,如果没有普通函数形式,这就话就error */
  cout << "add(ia, ib, ic) = " << add(ia, ib, ic) << endl;
  cout << "add(pstr1, pstr2) = " << add(pstr1, pstr2) << endl;
}

3.1.5、函数模板的参数类型

1、类型参数,class T 这种就是类型参数
2、非类型参数 常量表达式,整型:bool/char/short/int/long/size_t,注意:float/double这些就不是整型

代码例子:

#include<iostream>
using namespace std;

template <typename T = int, short kMin = 10>
T multiply(T x, T y)
{
	return x * y * kMin;
}
int main()
{
	int ia = 3, ib = 4;
	double da = 3.3, db = 4.4;
	cout << "multiply(ia, ib) = " << multiply(ia, ib) << endl; 
	cout << "multiply(ia, ib) = " << multiply<int, 4>(ia, ib) << endl;
	cout << "multiply(ia, ib) = " << multiply<double, 4>(da, db) << endl;
}

代码运行结果:

3.1.6、成员函数模板

就是类的成员函数也可以设置为模板

class Point
{
public:
  //.............
  //成员函数模板,成员函数模板也是可以设置默认值
  template <typename T = int>
  T func()
 {
    return (T)_dx;
 }
private:
  double _dx;
  double _dy;
};
void test()
{
Point pt(1.1, 2.2);
cout << "pt.func() = " << pt.func<int>() << endl;
cout << "pt.func() = " << pt.func<double>() << endl;
cout << "pt.func() = " << pt.func() << endl;
}

3.2、类模板

使用与函数模板也差不多,只是要注意模板的嵌套(函数模板与类模板都可以嵌套,比如函数参数是模板,类模板里面还有类模板),直接使用例子看类模板。

#include<iostream>
using namespace std;

//类模板
template <typename T, size_t kSize = 10>//类型参数T与非类型参数kSize
class Stack
{
public:
	Stack()
		: _top(-1)
		, _data(new T[kSize]())
	{
	}
	~Stack();
	bool empty() const;
	bool full() const;
	void push(const T &t);
	void pop();
	T top() const;
private:
	int _top;
	T *_data;
};
//类模板在类外面定义成员函数时候需要注意,模板是有类型的,需要使用参数加类型
template <typename T, size_t kSize>
Stack<T, kSize>::~Stack()
{
	if (_data)
	{
		delete[] _data;
		_data = nullptr;
	}
}
template <typename T, size_t kSize>
bool Stack<T, kSize>::empty() const
{
	return -1 == _top;//_top = -1
}
template <typename T, size_t kSize>
bool Stack<T, kSize>::full() const
{
	return _top == kSize - 1;
}
template <typename T, size_t kSize>
void Stack<T, kSize>::push(const T &t)
{
	if (!full())
	{
		_data[++_top] = t;
	}
	else
	{
		cout << "The Stack is full, cannot push any data" << endl;
	}
}
template <typename T, size_t kSize>
void Stack<T, kSize>::pop()
{
	if (!empty())
	{
		--_top;
	}
	else
	{
		cout << "The Stack is empty" << endl;
	}
}
template <typename T, size_t kSize>
T Stack<T, kSize>::top() const
{
	return _data[_top];
}
void test() 
{
	Stack<int, 8> st;
}
void test1()
{
	Stack<string> st;
}
int main()
{
	test();
	test1();

	return 0;
}

注意:

模板的嵌套:

//嵌套模版类的模版类
#include <iostream>
using namespace std;

template<class T>
class Outside			//外部Outside类定义
{
public:
	template <class R>
	class Inside		//嵌套类模板定义
	{
	private:
		R r;
	public:
		Inside(R x)	//模板类的成员函数可以在定义时实现
		{
			r=x;
		}
		//void disp();
		void disp() {cout << "Inside: " << r << endl;}
	};

	Outside(T x) : t(x)			//Outside类的构造函数
	{}
	
	//void disp();
	void disp()
	{
		cout<<"Outside:";
		t.disp();
	}

private:
	Inside<T> t;
};

//template<class T>
//template<class R>
//void Outside<T>::Inside<R>::disp()	//模板类的成员函数也可以在定义外实现
//{			//但必须是在所有类定义的外边,不能放在Outside内Inside外去实现.
//	cout<<"Inside: "<<Outside<T>::Inside<R>::r<<endl;
//}

//template<class T>
//void Outside<T>::disp()
//{
//	cout<<"Outside:";
//	t.disp();
//}

int main()
{
	Outside<int>::Inside<double> obin(3.5);	//声明Inside类对象obin
	obin.disp();

	Outside<int> obout(2);		//创建Outside类对象obout
	obout.disp();

	return 0;
}

代码运行结果:

#在 Outside 类内使用 Inside<T> t; 语句声明了 Inside<T> 类的对象,在 Outside 模板类对象创建时,首先采用 隐式实例化 先生成 Inside<T> 类的定义,而后根据此定义创建对象成员 t

总结:

#模板的套嵌可以理解成在另外一个模板里面定义一个模板。以模板(类,或者函数)作为另一个模板(类,或者函数)的成员,也称 成员模板
# 成员模版不能声明为 virtual

模板做参数:

#模板包含类型参数(如 class Type )和非类型参数(如 int NUM NUM 是常量),实际上, 模板的参数可以是另一个模板 ,也就是说,下述形式是合法的:

template<template <class T1> class T2, class T3,int Num>

#上述简单示例将原来简单的 class T2 Typename T2 扩充为 template <class T1> class T2 ,来看一段示例代码。
//A模板做为B模板的参数

//文件“Stack.h”的内容如下
template <class T,int num>		//类型参数表
class Stack						//Stack类定义
{
	private:
		T sz[num];				//存储空间,用数组表示
	public:
		int ReturnNum();		//判断栈是否为空
};

template<class T1,int num1>		//参数列表不要求字字相同,但形式要相同
int Stack<T1, num1>::ReturnNum()
{
	return num1;				//返回数组大小
}


#include <iostream>
//#include "Stack.h"
using namespace std;

template<template<class Type,int NUM> class TypeClass, class T1, int N>
void disp()				//函数模板,其类型参数表中包含一个类模板
{
	TypeClass<T1,N> ob;		//类模板的隐式实例化,创建对象ob
	cout<<ob.ReturnNum()<<endl;	//调用ob的public成员函数
}

int main()
{
	disp<Stack,int,8>();		//函数模板的隐式实例化,并调用
	system("pause");
	return 0;
}

#上述代码中定义了函数模板disp(),该模板的类型参数表中又包含了一个类模板TypeClass,在函数模板disp内可以对类TypeClass进行实例化处理。

四、可变模板参数

是C++11新增的最强大的特性之一,它对参数进行了高度的泛化,它能表示0到任意个数、任意类型的参数。

4.1、模板参数包

template<typename… Args> class tuple;//tuple是元组的意思,其模板参数就是模板参数包

Args标识符的左侧使用了省略号,在C++11中Args被称为“模板参数包”,表示可以接受任意多个参数作为模板参数,编译器将多个模板参数打包成“单个”的模板参数包.

4.2、函数参数包

template<typename…T> 
void f(T…args);//args就是函数参数包 

args 被称为函数参数包,表示函数可以接受多个任意类型的参数.
在C++11标准中,要求函数参数包必须唯一且是函数的最后一个参数; 模板参数包则没有。
当使用参数包时,省略号位于参数名称的右侧,表示立即展开该参数,这个过程也被称为解包。

4.3、可变模板参数的优势(有两条)

1、参数个数,那么对于模板来说,在模板推导的时候,就已经知道参数的个数了,也就是说在编译的时候就确定 了,这样编译器就存在可能去优化代码

#获取可变模板参数的个数
sizeof...(Args)
sizeof...(args)

2、参数类型,推导的时候也已经确定了,模板函数就可以知道参数类型了。

#include<iostream>
#include<string>
using namespace std;

template <typename... Args>
void print(Args... args)
{
	cout << "sizeof...(Agrs) = " << sizeof...(Args) << endl;
	cout << "sizeof...(agrs) = " << sizeof...(args) << endl;
}
void display()
{
	cout << endl;
}
template <typename T, typename... Args>
void display(T t, Args... args)
{
	cout << t << " ";
	display(args...);//当... 位于args右边的时候叫做解包
}
void test()
{
	string s1 = "hello";
	print();
	print(1, 2.2);
	print('a', true, s1);
	print(1, 2.2, 'b', "hello");
}
void test2()
{
	string s1 = "hello";
	display();
	display(1, 2.2);
	display('a', true, s1);
	display(1, 2.2, 'b', "hello");
}
template <class T>
T sum(T t)
{
	return t;
}
template <typename T, typename... Args>
T sum(T t, Args... args)
{
	return t + sum(args...);
}
void test3()
{
	cout << "sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) = "
		<< sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) << endl;
}

test2运行结果:

test3运行结果:

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

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

相关文章

vue3+threejs新手从零开发卡牌游戏(十四):调整卡组位置,添加玩家生命值HP和法力值Mana信息

由于之前的卡组位置占了玩家信息的位置&#xff0c;所以这里将它调整到site区域&#xff1a; 修改game/site/p1.vue&#xff0c;在site右下角添加一个卡组区域&#xff1a; // 初始化己方战域 const init () > {let sitePlane scene.getObjectByName("己方战域Plan…

【工具-MobaXterm】

MobaXterm ■ MobaXterm简介■ MobaXterm下载安装■ MobaXterm主要功能■ 创建SSH session■ 创建串口session■ 远程文件传输和下载■ 运行图形应用程序■ Unix 命令集(GNU/ Cygwin)工具箱功能 ■ MobaXterm配置■ 设置黑色主题■ 设置终端字体■ 右键粘贴■ 右键复制■ 文件保…

【干货】Apache DolphinScheduler2.0升级3.0版本方案

升级背景 因项目需要使用数据质量模块功能&#xff0c;可以为数仓提供良好的数据质量监控功能。故要对已有2.0版本升级到3.0版本以上&#xff0c;此次选择测试了3.0.1 和 3.1.1 两个版本&#xff0c;对进行同数据等任务调度暂停等操作测试&#xff0c;最后选择3.0.1 版本 原因…

【每日力扣】70. 爬楼梯与746. 使用最小花费爬楼梯

&#x1f525; 个人主页: 黑洞晓威 &#x1f600;你不必等到非常厉害&#xff0c;才敢开始&#xff0c;你需要开始&#xff0c;才会变的非常厉害。 70. 爬楼梯 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢…

Java基础入门day21

day21 思考&#xff1a;构造方法能否实现重写 引申出来三个问题&#xff1a; 一个类是否可以继承它自身 一个类是否可以继承它的同名类 构造方法能否实现重写 结论&#xff1a; 一个类如果继承了自己&#xff0c;会出现递归构造调用 一个类可以继承它的同名类&#xff0c;必…

ESCTF-逆向赛题WP

ESCTF_reverse题解 逆吧腻吧babypybabypolyreeasy_rere1你是个好孩子完结撒花 Q_W_Q 逆吧腻吧 下载副本后无壳&#xff0c;直接拖入ida分析分析函数逻辑&#xff1a;ida打开如下&#xff1a;提取出全局变量res的数据后&#xff0c;编写异或脚本进行解密&#xff1a; a[0xBF, …

matlab和stm32的安装环境。能要求与时俱进吗,en.stm32cubeprg-win64_v2-6-0.zip下载太慢了

STM32CubeMX 6.4.0 Download STM32CubeProgrammer 2.6.0 Download 版本都更新到6.10了&#xff0c;matlab还需要6.4&#xff0c;除了st.com其他地方都没有下载的,com.cn也没有。曹 还需要那么多固件安装。matlab要求制定固件位置&#xff0c;然后从cubemx中也指定…

python3游戏GUI--开心打地鼠游戏By:PyQt5(附下载地址)

文章目录 一&#xff0e;前言二&#xff0e;游戏预览1.启动2.开始游戏3.游戏结束4.排行榜 三&#xff0e;游戏思路四&#xff0e;总结 一&#xff0e;前言 第一次用PyQt做游戏&#xff0c;有点小紧张呢。本次使用PyQt5制作一款简单的打地鼠游戏&#xff0c;支持基本游戏玩法、…

本地部署大模型的几种工具(下-相关比较)

比较项目chatglm.cppvllmOllamalmstudio功能特点通过C优化性能&#xff0c;支持多平台运行推理加速简化易用、本地运行大模型简化操作、本地运行大模型操作系统要求都可以&#xff0c;linux下运行更方便都可以&#xff0c;linux下运行更方便都可以&#xff0c;windows目前还是预…

2024华为产业链企业名单大全(附下载)

更多内容&#xff0c;请前往知识星球下载&#xff1a;https://t.zsxq.com/18fsVdcjA 更多内容&#xff0c;请前往知识星球下载&#xff1a;https://t.zsxq.com/18fsVdcjA

利用 Scapy 库编写 ARP 缓存中毒攻击脚本

一、ARP 协议基础 参考下篇文章学习 二、ARP 缓存中毒原理 ARP&#xff08;Address Resolution Protocol&#xff09;缓存中毒是一种网络攻击&#xff0c;它利用了ARP协议中的漏洞&#xff0c;通过欺骗或篡改网络中的ARP缓存来实施攻击。ARP协议是用于将IP地址映射到物理MAC…

【Leetcode每日一题】 动态规划 - 解码方法(难度⭐)(43)

1. 题目解析 题目链接&#xff1a;91. 解码方法 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 这是一道类似斐波那契数列的题目~ 当我们遇到一个类似斐波那契数列的问题时&#xff0c;我们通常会想到使用动态规划&…

网络安全学习路线(2024)

国家和企业越来越重视网络安全了&#xff0c;现在也有很多很厂商加招网络安全岗位&#xff0c;同时也有很多对网络安全感兴趣的朋友&#xff0c;准备转行或从事网络安全。 通常&#xff0c;网络安全的内容包括&#xff1a; 网络安全技术、网络安全管理、网络安全运作&#xff…

【MySQL数据库】数据类型和简单的增删改查

目录 数据库 MySQL的常用数据类型 1.数值类型&#xff1a; 2.字符串类型 3.日期类型 MySQL简单的增删改查 1.插入数据&#xff1a; 2.查询数据&#xff1a; 3.修改语句&#xff1a; 4.删除语句&#xff1a; 数据库 平时我们使用的操作系统都把数据存储在文件中&#…

谷歌关键词优化十招搞定提升你的存在感-华媒舍

在当今的数字化时代&#xff0c;谷歌已成为我们生活中不可或缺的一部分。作为世界上最大的搜索引擎之一&#xff0c;谷歌每天处理着海量的搜索请求。要在谷歌上获得更多的曝光和存在感&#xff0c;关键词优化是必不可少的。本文将向您介绍十招搞定谷歌关键词优化的方法&#xf…

力扣刷题44-46(力扣0062/0152/0198)

62. 不同路径 题目描述&#xff1a; 一个机器人位于一个 m x n 网格的左上角 &#xff0c;机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角。问总共有多少条不同的路径&#xff1f; 思路&#xff1a; 其实就是问(0,0)->(m-1,n-1)一共有几条路。 第一个…

web自动化测试系列-selenium的安装和运行(一)

目录 web自动化系列之如何安装selenium 1.web自动化中的三大亮点技术 2.web自动化能解决什么问题 &#xff1f; 3.为什么是selenium ? 4.selenium特点 5.selenium安装 6.下载浏览器及驱动 7.测试代码 web自动化系列之如何安装selenium web自动化 &#xff0c;一个老生…

【No.17】蓝桥杯图论上|最短路问题|Floyd算法|Dijkstra算法|蓝桥公园|蓝桥王国(C++)

图的基本概念 图&#xff1a; 由点(node&#xff0c;或者 vertex)和连接点的边(edge)组成。图是点和边构成的网。 树&#xff1a;特殊的图树&#xff0c;即连通无环图树的结点从根开始&#xff0c;层层扩展子树&#xff0c;是一种层次关系&#xff0c;这种层次关系&#xff0…

【C++】哈希应用之位图

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》《算法》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 目录 前言 1.位图的概念 2.位…

一道很有意思的题目(考初始化)

这题很有意思&#xff0c;需要你对初始化够了解才能解出来 &#xff0c;现在我们来看一下吧。 这题通过分析得出考的是初始化。关于初始化有以下知识点 &#xff08;取自继承与多态&#xff08;继承部分&#xff09;这文章中&#xff09; 所以根据上方那段知识点可知&#xf…