c/c++开发,无可避免的模板编程实践(篇三)

news2024/10/2 12:21:37

一、模板与多态

        多态就是通过单一命名标记关联不同特定行为的能力。在C++中,主要是通过继承和虚函数来实现,由于继承和虚函数主要是在运行期进行处理,因此c++把这种多态称为“动多态”。而通过函数重载方式也可以单一命名标记关联不同行为,也是多态,这种方式是在编译期进行处理的,因此称为“静多态”。

        在前文就阐述过,函数模板就是一个家族函数,这就意味着它是具有单一命名标记关联不同特定行为的能力。其实从泛的层面上来说,无论是函数模板或是类模板,其本身都具备多态能力,并还可以向普通函数或类那样支持到重载、继承、虚函数,其多态特性就具有更多层次性和编码样式。

        1.1 函数模板本身就是静多态

        普通函数通过重载实现多态,定义了一组同函数名但参数类型及数量不同的函数,在实际函数调用时,编译器由传入的实参去推演采用哪个函数,从而实现不同特定行为的能力

void doit(const int &val){std::cout<<"doit int\n";};
void doit(const char &val){std::cout<<"doit char\n";};
void doit(const double &val){std::cout<<"doit double\n";};
void doit(const char &cval, const int &ival){std::cout<<"doit char and int\n";};
//
doit(10);
doit(2.5);
doit('a',100);

        而函数模板同样能达到此效果,支持特定行为更多,并更简洁:

template <typename T>
void doit_template(const T &val){std::cout<<"doit_template const T\n";};
template <typename T>
void doit_template(const T *val){std::cout<<"doit_template const T*\n";};		//函数模板重载
template <typename T1, typename T2>
void doit_template(const T1 &val1,const T2 &val2){std::cout<<"doit_template const T1,T2\n";};//函数模板重载
//
doit_template(10);
doit_template(2.5);
doit_template('a',100);
//
typedef void (*pdoit)(const int &val);
pdoit pf = &doit;
doit_template<pdoit>(pf);

        在看看下面这个例子,通过"..."省略号参数支持,实现不定参数集:

//
template <typename T>
void doit_arg(const T &val,...){std::cout<<"doit_arg const T ...\n";};
//
doit_arg(1,2,3);

        然后也将该函数进行重载:

//
template <typename T>
void doit_arg(const T &val,...){std::cout<<"doit_arg const T ...\n";};
template <typename T>
void doit_arg(const T &val){std::cout<<"doit_arg const T \n";};
//
doit_arg(1,2,3);    //OK
doit_arg(1);        //error,两个函数模板都满足,编译器无法推演

        但采用普通函数去重载,调用时是能识别的,因为普通函数比模板函数更具有确定性,编译器会优先精确匹配到它,注意该函数并不是模板函数的特例化(它没带模板标记)。另外如果是在模板之间选择匹配的话,特化程度更高的模板优先被选择。

//
template <typename T>
void doit_arg(const T &val,...){std::cout<<"doit_arg const T ...\n";};
template <typename T>
void doit_arg(const T &val){std::cout<<"doit_arg const T \n";};
void doit_arg(const double &val){std::cout<<"doit_arg const double \n";};
//
doit_arg(1,2,3);    //OK
//doit_arg(1);      //error
doit_arg(1.2);      //OK

        不同于普通类通过继承及虚函数来实现多态,类模板本身能实现多态,从实现方式来看,它可以归并在静多态方面。

template <typename T>
class ATest	//类模板本身能实现多态
{
public:
	T val;    //可以传入自定义的类型,将多态性封装在T来实现
};

//
ATest<int> i_c;
ATest<float> f_c;
ATest<std::string> s_c;

        1.2 模板的动多态

        类模板和普通模板一样,是可以被继承的,但有和普通模板不一样,类模板的继承有更多的设计选择。

template <typename T>
class ATest	//类模板本身能实现多态
{
public:
	T val;	//可以传入自定义的类型,将多态性封装在T来实现
};

template <typename T>
class AChild1 : public ATest<T>
{
public:
	T c_val;	
};

template < typename T=int>
class AChild2 : public ATest<int>
{
public:
	int c_val;	
};

class AChild3 : public ATest<char>
{
public:
	char c_val;	
};

template < typename T1,typename T2>
class AChild4 : public ATest<T1>
{
public:
	T2 c_val;	
};

template <typename T>
class AChild5 : virtual public ATest<T>
{
public:
	T c_val;	
};

template <typename T>
class AChild6 : private ATest<T>
{
public:
	T c_val;	
};

//
	AChild1<double> ichild1;
	ichild1.val = 10.2;
	ichild1.c_val = 11.5;
	//
	AChild2<> ichild2;
	ichild2.val = 10;
	ichild2.c_val = 15;
	//
	AChild3 ichild3;
	ichild3.val = 'a';
	ichild3.c_val = 'b';
	//
	AChild4<int,float> ichild4;
	ichild4.val = 10;
	ichild4.c_val = 20.3;
    //
	AChild5<int> ichild5;
	ichild5.val = 1;
	ichild5.c_val = 5;
	//
	AChild6<float> ichild6;
	//ichild6.val = 2.6; //私继承
	ichild6.c_val = 5.9;

        仅仅就是类继承这一特点,类模板就能演化成不同的继承设计,支持特例化继承、virtual继承、私继承、缺省默认模板参数继承、模板参数扩展继承等等。

        模板参数也是类型,那么在继承实现中,可以继承模板参数

template <typename T>
class AChild7 : public T
{
public:
	AChild7():T()
	{
		
	}
	~AChild7()
	{
		
	}
};

template <typename T>
class AChild8 : public T,public ATest<T>    //多重继承
{
	public:
	AChild8():T()
	{
		
	}
	~AChild8()
	{
		
	}
};
//
template <typename T,typename Base>
class AChild10 : public Base,public ATest<T>    //多重继承
{
	public:
	AChild10():Base(),ATest<T>()
	{
		
	}
	~AChild10()
	{
		
	}
};
//
	AChild7<std::string> ichild7;
	ichild7.append("hello");
	std::cout<<"ichild7 size = "<<ichild7.size() <<std::endl;
	//
	AChild8<std::string> ichild8;
	ichild8.val = "hi";

        但将模板参数作为基类来继承,也会引入风险,就是前文讲述的,使用者需要自行把控传入模板参数类型的行为,例如上述继承中,传入一个int型(原生类型等)

AChild7<int> ichild7_;	//error: base type 'int' fails to be a struct or class type

        另外,为基类模板参数设置默认缺省值,派生类在不指定类型时,就能实现继承。

template <typename T=int>
//template <typename T>
class ATest	//类模板本身能实现多态
{
public:
	T val;	//可以传入自定义的类型,将多态性封装在T来实现
};
//下面三种继承是一致的
template < typename T=int>
class AChild2 : public ATest<int>
{
public:
	int c_val;	
};

class AChild3 : public ATest<char>
{
public:
	char c_val;	
};
class AChild9 : private ATest<>
{
	public:	
};

        既然基类能作为模板参数,那么派生类用来作为模板参数呢,这种倒施逆行的方式在一些特定场景可能会有奇效哦。

template <typename Derived>
class MyBase
{
	public:
	void doSomething(Derived val)
	{
		val.doSomething();
	};
};

class MyDerived : public MyBase<MyDerived>
{
	public:
	void doSomething(void)
	{
		std::cout<< "just boring!" <<std::endl;
	};
};

//
	MyDerived myd;
	myd.doSomething();

        类模板和普通类一样,支持到普通函数成员虚拟化设计,但函数模板成员不能声明为虚函数,因为虚函数调用机制实现使用了一个大小固定的表,每个虚函数都对应表的一个入口,但成员函数模板在构建虚函数表之前是无法确定的。

//模板-虚函数
template <typename T>
class VClass
{
	public:
	virtual void doit()
	{
		std::cout<< "VClass doit"<<std::endl;
	};
};

template <typename T>
class MYVBase : public VClass<T>
{
	public:
	virtual void doit()
	{
		std::cout<< "MYVBase doit"<<std::endl;
	};
};
template <typename T>
class MYVBase_C : public VClass<T>
{
	public:
};

//
	MYVBase<int> vbc;
	vbc.doit();        //MYVBase doit
	MYVBase_C<char> vbc_c;
	vbc_c.doit();      //VClass doit

        或者将基类作为派生类的模板参数,同样能实现虚函数效果

//模板-虚函数-另类
class VirtualClass
{
	public:
	virtual void doit()
	{
		std::cout<< "VirtualClass doit"<<std::endl;
	};
};

class NVirtualClass
{
	public:
};

template <typename VBase>
class MYNewVBase : public VBase
{
	public:
	void doit()
	{
		std::cout<< "MYNewVBase doit"<<std::endl;
	};
};

template <typename VBase>
class MYNewVBase_C : public VBase
{
	public:
};
//
	MYNewVBase<VirtualClass> nvbc;
	nvbc.doit();
	MYNewVBase_C<VirtualClass> nvbc_c;
	nvbc_c.doit();
	MYNewVBase<NVirtualClass> nvbc_;
	nvbc_.doit();

二、模板元编程

        模板实例化机制是一种递归语言机制,可以用于在编译期执行复杂计算,这种模板实例化所展现的编译器计算被叫做template meta programming。看下面的例子:

//
template <int N>
class POW2
{
	public:
	enum{ret=2*POW2<N-1>::ret};
};

template <>
class POW2<0>
{
	public:
	enum{ret=1};
};

//
std::cout<<"POW2<5>::ret = "<< POW2<5>::ret <<std::endl;   //ret = 32

        上面展示的是通过递归机制在编译期计算2的N次幂。编译器在实例化时,会不断递归下去,直到特例化POW2<0>结束。注意,枚举值POW2<5>::ret=32是编译期就确定下来的。替换静态常量也能达到同样效果。

        模板元编程可以通过展开循环来优化迭代效率,例如我们要计算两个数组的点乘,通常做法是:

for(int i=0; i<N; i++)
{
    result += a[i]*b[i];
}

        对于N很大的情况,其实这种循环计算效率是很慢的,但是如果在编译期展开计算表达式,在运行期直接计算,那么运行效率是很高的,这方面类似我们在嵌入式领域时对于字符编码、图像矩阵等就常常采用展开的方式,当然那些很多展开都是借助辅助工具自动创建的:

retsult = a[0]*b[0]+a[1]*b[1]+...+a[N-1]*b[N-1];

        对于我们自行编码时,如果我们手动重复撰写这些计算公式展开肯定很乏味,采用模板元编程可以解决这些烦恼

//基本模板
template <int DN, typename T>
class dotMultip{
	public:
	static T retsult (T* a, T* b)
	{
		return (*a)*(*b)+dotMultip<DN-1,T>::retsult(a+1,b+1);
	};
};
//特例化,循环结束
template < typename T>
class dotMultip<1,T>{
	public:
	static T retsult (T* a, T* b)
	{
		return (*a)*(*b);
	}
};
//辅助函数,初次调用进入循环
template <int DN, typename T>
inline T dotMultipFunc(T* a, T* b){
	return dotMultip<DN,T>::retsult(a,b);
};

//
	int a[5]={1,2,3,4,5};
	int b[5]={2,3,4,5,6};
	std::cout<<"dotMultipFunc<5>(a,b) = "<< dotMultipFunc<5>(a,b) <<std::endl;//=70

        在实例化过程中,dotMultipFunc<5>(a,b)的调用时如下过程展开:

dotMultipFunc<5>(a,b)
=dotMultip<5,int>::retsult(a,b)
=(*a)*(*b)+dotMultip<4,int>::retsult(a+1,b+1)
=(*a)*(*b)+(*(a+1))*(*(b+1))+dotMultip<3,int>::retsult(a+2,b+2)
=(*a)*(*b)+(*(a+1))*(*(b+1))+(*(a+2))*(*(b+2))+dotMultip<2,int>::retsult(a+3,b+3)
=(*a)*(*b)+(*(a+1))*(*(b+1))+(*(a+2))*(*(b+2))+(*(a+3))*(*(b+3))+dotMultip<1,int>::retsult(a+4,b+4)
=(*a)*(*b)+(*(a+1))*(*(b+1))+(*(a+2))*(*(b+2))+(*(a+3))*(*(b+3))+(*(a+4))*(*(b+4))

       当然,这运行效率的提高是有代价的,首先,数组的元数在编译期是已知的,并由于编译期展开的原因,可能会带来编译效率的下降,至于是否需要选用,取决实际项目中要求的权衡了。

三、模板与指针

        智能指针,本质上通过类模板可以有效的封装内存操作,管理使用基础对象的操作,能够动态分配和释放相关继承类的对象,这就是我们所描述的智能指针。

//智能指针示例
template < typename T>
class Handle
{
	public:
	Handle()
		:ptr(0)
	{
	};
	explicit Handle(T*p)
		:ptr(p)
	{
	};
	~Handle(){
		delete ptr;
		ptr = 0;
		std::cout<<"~Handle()\n";
	};
	//拷贝构造
	Handle(Handle<T> const&rhs)
	{
		//delete ptr;
		ptr = rhs.ptr;
	};
	//拷贝赋值运算
	Handle<T>& operator=(const Handle &rhs)
	{
		delete ptr;
		ptr = rhs.ptr;
		return *this;
	};
	//指针运算符
	T& operator*() const
	{
		assert(0!=ptr);
		return *ptr;
	};
	//指针运算符
	T* operator->() const
	{
		assert(0!=ptr);
		return ptr;
	};
	private:
	T *ptr;
	//
};
//
//
	Handle<MYNewVBase_C<VirtualClass> > first_hclass(new MYNewVBase_C<VirtualClass>());
	first_hclass->doit();
	(*first_hclass).doit();
	Handle<MYNewVBase_C<VirtualClass> > second_hclass = first_hclass;
	second_hclass->doit();

        上述例子,通过模板参数typename T实现对实体类指向,并给Handle模板赋予指针运算符“*”和"&",使得使用实体类T时,和以其本身实例化指针时使用是一致的。

四、源码

        建立test.h/cpp源文件,g++ test.cpp -o test.exe编译输出,测试如下:

         test.h

#ifndef _TEST_H_
#define _TEST_H_
#include <iostream>
#include <string>
#include <stdio.h>
#include <cassert>

//1.1 函数模板本身就是静多态
void doit(const int &val){std::cout<<"doit int\n";};
void doit(const char &val){std::cout<<"doit char\n";};
void doit(const double &val){std::cout<<"doit double\n";};
void doit(const char &cval, const int &ival){std::cout<<"doit char and int\n";};

template <typename T>
void doit_template(const T &val){std::cout<<"doit_template const T\n";};
template <typename T>
void doit_template(const T *val){std::cout<<"doit_template const T*\n";};		//函数模板重载
template <typename T1, typename T2>
void doit_template(const T1 &val1,const T2 &val2){std::cout<<"doit_template const T1,T2\n";};//函数模板重载

//
template <typename T>
void doit_arg(const T &val,...){std::cout<<"doit_arg const T ...\n";};
template <typename T>
void doit_arg(const T &val){std::cout<<"doit_arg const T \n";};
void doit_arg(const double &val){std::cout<<"doit_arg const double \n";};

//1.2 模板的动多态
template <typename T=int>
//template <typename T>
class ATest	//类模板本身能实现多态
{
public:
	T val;	//可以传入自定义的类型,将多态性封装在T来实现
};

template <typename T>
class AChild1 : public ATest<T>
{
public:
	T c_val;	
};

template < typename T=int>
class AChild2 : public ATest<int>
{
public:
	int c_val;	
};

class AChild3 : public ATest<char>
{
public:
	char c_val;	
};

template < typename T1,typename T2>
class AChild4 : public ATest<T1>
{
public:
	T2 c_val;	
};

template <typename T>
class AChild5 : virtual public ATest<T>
{
public:
	T c_val;	
};

template <typename T>
class AChild6 : private ATest<T>
{
public:
	T c_val;	
};

template <typename T>
class AChild7 : public T
{
public:
	AChild7():T()
	{
		
	}
	~AChild7()
	{
		
	}
};

template <typename T>
class AChild8 : public T,public ATest<T>
{
	public:
	AChild8():T()
	{
		
	}
	~AChild8()
	{
		
	}
};

class AChild9 : private ATest<>
{
	public:	
};

//
template <typename T,typename Base>
class AChild10 : public Base,public ATest<T>    //多重继承
{
	public:
	AChild10():Base(),ATest<T>()
	{
		
	}
	~AChild10()
	{
		
	}
};

template <typename Derived>
class MyBase
{
	public:
	void doSomething(Derived val)
	{
		val.doSomething();
	};
};

class MyDerived : public MyBase<MyDerived>
{
	public:
	void doSomething(void)
	{
		std::cout<< "just boring!" <<std::endl;
	};
};

template <typename Derived>
class MyCBase
{
	public:
	void doSomething(Derived val)
	{
		val.doSomething();
	};
};

template <typename T>
class MyCDerived : public MyCBase<MyCDerived<T> >
{
	public:
	void doSomething(const T &val)
	{
		std::cout<< "just boring =" << val <<std::endl;
	};
};
//模板-虚函数
template <typename T>
class VClass
{
	public:
	virtual void doit()
	{
		std::cout<< "VClass doit"<<std::endl;
	};
};

template <typename T>
class MYVBase : public VClass<T>
{
	public:
	virtual void doit()
	{
		std::cout<< "MYVBase doit"<<std::endl;
	};
};
template <typename T>
class MYVBase_C : public VClass<T>
{
	public:
};
//模板-虚函数-另类
class VirtualClass
{
	public:
	virtual void doit()
	{
		std::cout<< "VirtualClass doit"<<std::endl;
	};
};

class NVirtualClass
{
	public:
};

template <typename VBase>
class MYNewVBase : public VBase
{
	public:
	void doit()
	{
		std::cout<< "MYNewVBase doit"<<std::endl;
	};
};

template <typename VBase>
class MYNewVBase_C : public VBase
{
	public:
};
//模板元编程
template <int N>
class POW2
{
	public:
	enum{ret=2*POW2<N-1>::ret};
	static int const retsult=2*POW2<N-1>::retsult;
};

template <>
class POW2<0>
{
	public:
	enum{ret=1};
	static int const retsult=1;
};
//基本模板
template <int DN, typename T>
class dotMultip{
	public:
	static T retsult (T* a, T* b)
	{
		return (*a)*(*b)+dotMultip<DN-1,T>::retsult(a+1,b+1);
	};
};
//特例化,循环结束
template < typename T>
class dotMultip<1,T>{
	public:
	static T retsult (T* a, T* b)
	{
		return (*a)*(*b);
	}
};
//辅助函数,初次调用进入循环
template <int DN, typename T>
inline T dotMultipFunc(T* a, T* b){
	return dotMultip<DN,T>::retsult(a,b);
};
//智能指针示例
template < typename T>
class Handle
{
	public:
	Handle()
		:ptr(0)
	{
	};
	explicit Handle(T*p)
		:ptr(p)
	{
	};
	~Handle(){
		delete ptr;
		ptr = 0;
		std::cout<<"~Handle()\n";
	};
	//拷贝构造
	Handle(Handle<T> const&rhs)
	{
		//delete ptr;
		ptr = rhs.ptr;
	};
	//拷贝赋值运算
	Handle<T>& operator=(const Handle &rhs)
	{
		delete ptr;
		ptr = rhs.ptr;
		return *this;
	};
	//指针运算符
	T& operator*() const
	{
		assert(0!=ptr);
		return *ptr;
	};
	//指针运算符
	T* operator->() const
	{
		assert(0!=ptr);
		return ptr;
	};
	private:
	T *ptr;
	//
};

#endif //_TEST_H_

        test.cpp

#include "test.h"

int main(int argc, char* argv[])
{
	doit(10);
	doit(2.5);
	doit('a',100);
	//
	doit_template(10);
	doit_template(2.5);
	doit_template('a',100);
	typedef void (*pdoit)(const int &val);
	pdoit pf = &doit;
	doit_template<pdoit>(pf);
	//
	doit_arg(1,2,3);
	//doit_arg(1);
	doit_arg(1.2);
	//
	ATest<int> i_c;
	ATest<float> f_c;
	ATest<std::string> s_c;
	//
	AChild1<double> ichild1;
	ichild1.val = 10.2;
	ichild1.c_val = 11.5;
	//
	AChild2<> ichild2;
	ichild2.val = 10;
	ichild2.c_val = 15;
	//
	AChild3 ichild3;
	ichild3.val = 'a';
	ichild3.c_val = 'b';
	//
	AChild4<int,float> ichild4;
	ichild4.val = 10;
	ichild4.c_val = 20.3;
	//
	AChild5<int> ichild5;
	ichild5.val = 1;
	ichild5.c_val = 5;
	//
	AChild6<float> ichild6;
	//ichild6.val = 2.6; //私继承
	ichild6.c_val = 5.9;
	//
	AChild7<std::string> ichild7;
	ichild7.append("hello");
	std::cout<<"ichild7 size = "<<ichild7.size() <<std::endl;
	//
	AChild8<std::string> ichild8;
	ichild8.val = "hi";
	//
	//AChild7<int> ichild7_;	//error: base type 'int' fails to be a struct or class type
	//
	MyDerived myd;
	myd.doSomething();
	//
	MyCDerived<int> mycd;
	mycd.doSomething(100);
	//
	MYVBase<int> vbc;
	vbc.doit();
	MYVBase_C<char> vbc_c;
	vbc_c.doit();
	//
	MYNewVBase<VirtualClass> nvbc;
	nvbc.doit();
	MYNewVBase_C<VirtualClass> nvbc_c;
	nvbc_c.doit();
	MYNewVBase<NVirtualClass> nvbc_;
	nvbc_.doit();
	//
	std::cout<<"POW2<5>::ret = "<< POW2<5>::ret <<std::endl;
	std::cout<<"POW2<5>::retsult = "<< POW2<5>::retsult <<std::endl;
	//
	int a[5]={1,2,3,4,5};
	int b[5]={2,3,4,5,6};
	std::cout<<"dotMultipFunc<5>(a,b) = "<< dotMultipFunc<5>(a,b) <<std::endl;
	//
	Handle<MYNewVBase_C<VirtualClass> > first_hclass(new MYNewVBase_C<VirtualClass>());
	first_hclass->doit();
	(*first_hclass).doit();
	Handle<MYNewVBase_C<VirtualClass> > second_hclass = first_hclass;
	second_hclass->doit();
	return 0;
};

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

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

相关文章

TrueNas篇-硬盘直通

硬盘直通 在做硬盘直通之前&#xff0c;在trueNas(或者其他虚拟机)内是检测不到安装的硬盘的。 在pve节点查看硬盘信息 打开pve的shell控制台 输入下面的命令查看硬盘信息&#xff1a; ls -l /dev/disk/by-id/该命令会显示出实际所有的硬盘设备信息&#xff0c;其中ata代…

Python 给视频添加背景音乐 | Python工具

目录 前言 环境依赖 代码 总结 前言 本文提供给视频添加背景音乐的python工具&#xff0c;一如既往的实用主义。 环境依赖 ffmpeg环境安装&#xff0c;可以参考我的另一篇文章&#xff1a;windows ffmpeg安装部署_阿良的博客-CSDN博客 本文主要使用到的不是ffmpeg&#x…

绘制正余弦曲线中的sin(x),cos(x)的使用

目录一、 基础知识1.1 头文件1.2 原型1.3 参数1.4 返回值二、使用1. 坐标与弧度的对应关系一、 基础知识 1.1 头文件 #include <math.h> 1.2 原型 double sin(double x) double cos(double x) 1.3 参数 参数是弧度制&#xff08;rad&#xff09; 1.4 返回值 返…

Python 采集 筷 实现视频批量保存

前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 刷到的视频怕它下架&#xff1f;我们来采集保存一下它 知识点: 动态数据抓包 requests发送请求 json数据解析 开发环境: python 3.8 运行代码 pycharm 2022.3 辅助敲代码 requests pip install requests 代码展示 需…

故障案例:MySQL唯一索引有重复值,官方却说This is not a bug

GreatSQL社区原创内容未经授权不得随意使用&#xff0c;转载请联系小编并注明来源。GreatSQL是MySQL的国产分支版本&#xff0c;使用上与MySQL一致。作者&#xff1a;飞鱼过天文章来源&#xff1a;GreatSQL社区原创 问题原因故障解决方案复现步骤参考文献 一、问题&#xff1a;…

图片压缩怎么弄?多种图片格式压缩大小的方法

平时接触的图片格式有许多种&#xff0c;比如jpg、png、gif、tiff、webp等&#xff0c;不同的场景都需要用不同的图片&#xff0c;但是当这些图片大小都不符合我们的使用要求时&#xff0c;该怎么去压缩图片大小呢&#xff1f;小编今天给大家分享一款支持多种图片格式压缩工具&…

openEuler RISC-V 成功适配 VisionFive 2 单板计算机

近日&#xff0c;RISC-V SIG 成功在 VisionFive 2 开发板上适配欧拉操作系统&#xff0c;目前最新版本的 openEuler RISC-V 22.03 V2 镜像已在 VisionFive 2 开发板上可用&#xff0c;这是 openEuler 推动 RISC-V 生态演进的又一新进展。下载链接​​https://mirror.iscas.ac.c…

如何在Power Virtual Agents中使用Power Automate

今天我们来介绍一下如何在Power Virtual Agents中使用PowerAutomate。我们以通过在PVA聊天机器人的对话框中输入“发布通知”后会把预设好的通知信息自动发布到Teams中的某个团队中为例。首先进入PVA聊天机器人编辑界面后选择“主题”-“新建主题”。 在“新建主题”中添加“触…

服务器容器配置日志(Linux+x86_64+Ubuntu18.04+CUDA11.0+python3.7)

一、创建并进入容器 &#xff08;平台使用教学详细&#xff0c;这部分略写&#xff09; 登上服务器后&#xff0c;打开终端输入如下进入自己建的容器 ssh -p XXXXX root10.XXX.XXX.XXX //按自己的宿主机端口写二、安装Conda&#xff08;miniconda3&#xff09; &#xff08…

数据库系统:1. 绪论

更好的阅读体验\huge{\color{red}{更好的阅读体验}}更好的阅读体验 文章目录1.1 数据库系统概述1.1.1 基本概念数据&#xff08;data&#xff09;数据库&#xff08;DataBase, DB&#xff09;数据库管理系统&#xff08;DataBase Management System, DBMS&#xff09;数据库系统…

极验3代 加密分析

目标链接 aHR0cHM6Ly93d3cuZ2VldGVzdC5jb20vZGVtby9zbGlkZS1mbG9hdC5odG1s接口分析 极验参数重要信息 gt和challenge&#xff1b;gt是固定的&#xff0c;但是challenge每次请求会产生不同的&#xff0c;这里的请求的并没有什么加密参数。 下一个请求 gettype.php&#xff0c…

365天深度学习训练营-第J2周:ResNet50V2算法实战与解析

目录 一、前言 二、论文解读 1、ResNetV2结构与ResNet结构对比 2、关于残差结构的不同尝试 3、关于激活的尝试 三、模型复现 1.Residual Block 3、ResNet50V2架构复现 4.ResNet50V2模型结构大图 一、前言 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习…

自动定时播报股价变动(零代码):今天我的股票涨了吗?

你是否会关注某个上市公司每天的股价信息吗&#xff1f;比如自己公司~你是否需要一个机器人&#xff0c;每天在内部群自动播报今日收盘价&#xff1f;不用复制粘贴&#xff0c;也不用写python&#xff0c;今天教你一个零代码可以自动播报股价数据的好办法。来看看我自动化的效果…

从开始测试到年薪40W,我的自动化测试艰辛历程

我希望我的故事能够激励现在的软件测试人&#xff0c;尤其是还坚持在做“点点点”的测试人。 你可能会有疑问&#xff1a;“我也能做到这一点的可能性有多大&#xff1f;”因此&#xff0c;我会尽量把自己做决定和思考的过程讲得更具体一些&#xff0c;并尽量体现更多细节。 …

嵌入式ARM设计编程(四) ARM启动过程控制

文章和代码已归档至【Github仓库&#xff1a;hardware-tutorial】&#xff0c;需要的朋友们自取。或者公众号【AIShareLab】回复 嵌入式 也可获取。 一、实验目的 &#xff08;1&#xff09; 掌握建立基本完整的ARM 工程&#xff0c;包含启动代码&#xff0c;C语言程序等&…

如何选择好的IB课程学校?

在上海除了拼中考&#xff0c;你还可以走一条更有“选择权”的路——国际化学校&#xff01; 然而选择学校时&#xff0c;让家长最头痛的事情&#xff0c;莫过于为孩子选择什么样的国际化课程。 今天我们来聊聊IB课程&#xff01; 三大主流国际课程中&#xff0c;被公认含金量最…

操作系统引论

操作系统是管理硬件和软件的一种应用程序。操作系统是运行在计算机上最重要的一种软件&#xff0c;它管理计算机的资源和进程以及所有的硬件和软件。它为计算机硬件和软件提供了一种中间层&#xff0c;使应用软件和硬件进行分离&#xff0c;让我们无需关注硬件的实现&#xff0…

嵌入式C语言设计模式 --- 外观模式

1 - 什么是外观模式? 外观模式(Facade Pattern),是一种比较简单的结构型模式,它存在的目的,也是为了简单。 外观模式隐藏了一系列接口的复杂性,旨在为外部客户端提供一个更高层次且统一简单的接口,简化了客户端调用某些模块的一系列操作。 外观模式应该是软件工程师…

Git入门使用详细笔记小白教程

Git入门使用详细笔记小白教程 一、克隆远程仓库到本地 1.复制远程仓库的地址到URL中&#xff1a; 二、本地和远程冲突 注意事项&#xff1a; 一定要在第二次修改提交到本地之前先pull远程的项目&#xff01;&#xff01;&#xff01;不然就会本地和远程的项目起冲突。 冲突…

Selection bias mitigation in recommender system using uninteresting items学习笔记

0 简介 论文题目&#xff1a;Selection bias mitigation in recommender system using uninteresting items based on temporal visibility 发表情况&#xff1a;2023年&#xff0c;Expert Systems With Applications 论文地址&#xff1a;https://doi.org/10.1016/j.eswa.202…