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

news2025/7/18 8:49:35

一、开发者需要对模板参数负责

       1.1 为您模板参数提供匹配的操作

         在进行模板设计时,函数模板或类模板一般只做模板参数(typename T)无关的操作为主,但是也不见得就不会关联模板参数自身的操作,尤其是在一些自定义的数据类型作为模板参数传入时。

        看下面这段代码,在v1<v2时,就调用了标准库里的operator<操作符比较函数,由于typename T是std::string,在string类本身是支持了值比较的,因此调用正常。

template <typename T>
inline T mymin(const T &v1, const T &v2)
{
	return (v1<v2)?v1:v2;
};

//会调用bool operator<(const T& obj1, const T& obj2)操作符函数"<"
mymin<std::string>("guangzhou","shenzhen");

        那现在再来调整一下代码,自定义数据类型DataTest,其包含两个int型成员变量,现在将该类型作为模板参数传递给mymin函数时,会编译异常,提示并不支持operator<,虽然int型支持operator<,但两个int型的组合在一起就需要模板使用者来为模板参数(typename T)操作行为负责:

class DataTest
{
public:
	DataTest(const int &id_d,const int &id_p) 
		: id_domain(id_d),id_point(id_p)
	{
	};
	~DataTest()
	{
	};
	int id_domain;
	int id_point;
};

//
DataTest adt(3,4),bdt(3,6);
mymin<DataTest>(adt,bdt);    //error, no match for 'operator<'

        为此,我们就需要给DataTest定义其operator<操作支持了

class DataTest
{
public:
	DataTest(const int &id_d,const int &id_p) 
		: id_domain(id_d),id_point(id_p)
	{
		
	};
	~DataTest()
	{
		
	};
	int id_domain;
	int id_point;
};
inline bool operator<(const DataTest& obj1, const DataTest& obj2) 
{ 
	if(obj1.id_domain<obj2.id_domain)
			return true;
	if(obj1.id_domain==obj2.id_domain&&obj1.id_point<obj2.id_point)
			return true;
	return false;
};

//
DataTest adt(3,4),bdt(3,6);
mymin<DataTest>(adt,bdt);    //OK, find 'operator<' success

        1.2 模板嵌套-类模板作为模板参数

        再进一步深化一下代码设计,如果给函数模板mymin传递一个类模板A_Test呢,同样地为A_Test提供operator<操作支持,又会怎样。

template <typename T1, typename T2>
class A_Test
{
public:
	A_Test(const T1 &id_d,const T2 &id_p) 
		: id_domain(id_d),id_point(id_p)
	{
		
	};
	~A_Test()
	{
		
	};
	T1 getDomainID() const
	{
		return id_domain;
	};
	T2 getPointID() const
	{
		return id_point;
	};
private:
	T1 id_domain;
	T2 id_point;
};

template <typename T1, typename T2>
inline bool operator<(const A_Test<T1,T2>& obj1, const A_Test<T1,T2>& obj2) 
{ 
    if(obj1.getDomainID()<obj2.getDomainID()) 
        return true;
	if(obj1.getPointID()==obj2.getPointID()&&obj1.getPointID()<obj2.getPointID()) 
        return true;
	return false;
};
//
A_Test<int,float> a(3,4.2),b(2,3.8);
mymin<A_Test<int,float> >(a,b);    //OK,没什么区别,只是换成特例化类模板

       1.3 友元模板

         在外部由于operator<是放置在类声明体外声明与定义的,它就不能直接使用类模板内的成员变量,就需要为它提供额外的访问函数getDomainID、getPointID,这样转一手的操作显然不符合inline的诉求,其实在标准库里,通常类模板会operator<声明友元函数,函数或类被声明为友元后,在类模板内就是真不把自身当外人了,甚至是类模板的儿子(派生类)都比不上。

        为了区别前面一种方法,我们重新定义一个函数模板mymax,在类模板会operator>声明友元函数,看下列代码:

template <typename T1, typename T2>
class A_Test
{
public:
	A_Test(const T1 &id_d,const T2 &id_p) 
		: id_domain(id_d),id_point(id_p)
	{
		
	};
	~A_Test()
	{
		
	};
	//template <typename T> friend bool operator>(const T& obj1, const T& obj2); //why is eroor
	template <typename T3, typename T4>
	friend bool operator>(const A_Test<T3,T4>& obj1, const A_Test<T3,T4>& obj2);
private:
	T1 id_domain;
	T2 id_point;
};

//
template <typename T1, typename T2>
inline bool operator>(const A_Test<T1,T2>& obj1, const A_Test<T1,T2>& obj2) 
{ 
	if(obj1.id_domain<obj2.id_domain)    //直接访问私有成员不含糊
			return true;
	if(obj1.id_domain==obj2.id_domain&&obj1.id_point<obj2.id_point)      //直接访问私有成员不含糊
			return true;
	return false;
};

//
template <typename T>
inline T mymax(const T &v1, const T &v2)
{
	return (v1>v2)?v1:v2;
};
//
A_Test<int,float> a(2,4.2),b(2,3.8);
mymin<A_Test<int,float> >(a,b);        //OK

A_Test<std::string,std::string> a_s("guangzhou","huangpu"),b_s("shenzhen","baoan");
if(a_s<b_s);    //OK
if(a_s>b_s);    //OK,两者都可以实现,不看operator内部实现的话

        1.4 节省模板的配套操作

        当然在实际设计中,我们最好还是避免同一模板的多个实例化中隐含的编译进开销:

template <typename T1, typename T2>
inline bool operator>(const A_Test<T1,T2>& obj1, const A_Test<T1,T2>& obj2) 
{ 
	if(obj1.id_domain<obj2.id_domain)
			return true;
	if(obj1.id_domain==obj2.id_domain&&obj1.id_point<obj2.id_point)
			return true;
	return false;
};

template <typename T>
inline T mymax(const T &v1, const T &v2)
{
	return (v1>v2)?v1:v2;
};

//
template <typename T>
inline T mymin(const T &v1, const T &v2)
{
	return (v1>v2)?v2:v1;            //这样同样也能达成效果
};

        1.5 有度地规划类模板操作符

        在设计类模板时需要慎重并认真地思考如何支撑一些通用的操作符调用,因为往往类模板设计者和使用者往往不会是同一个作者,那么使用者对待类模板时,会习惯性地把普通类型的调用习惯用到类模板上来设计其代码时,缺发现其实并不支持。例如,对于一个常规数据类型,输出显示是没有问题的,但是对于自定义的类模板,那就要想到使用者会有这样的用法习惯,给与到支持。

std::cout << mymin<std::string>("guangzhou","shenzhen") << std::endl;

A_Test<int,float> a(3,4.2),b(2,3.8);
std::cout << mymin<A_Test<int,float> >(a,b) << std::endl; //error

        那么这些常用的一些操作函数,还是需要通盘考虑和设计的,当然使用者如果谨慎,他也应该明确知道模板参数行为需要使用者本身去把控。

template <typename T1, typename T2>
class A_Test
{
public:
	A_Test(const T1 &id_d,const T2 &id_p) 
		: id_domain(id_d),id_point(id_p)
	{
		
	};
	~A_Test()
	{
		
	};
	template <typename T3, typename T4>
	friend std::ostream &operator<<(std::ostream &os, const A_Test<T3,T4>& obj);
private:
	T1 id_domain;
	T2 id_point;
};

//
template <typename T1, typename T2>
inline std::ostream &operator<<(std::ostream &os, const A_Test<T1,T2>& obj)
{
	os << "(";
	os << obj.id_domain << "," << obj.id_point;
	os <<")";
	return os;
};
//
A_Test<int,float> a(2,4.2),b(2,3.8);
std::cout << mymin<A_Test<int,float> >(a,b) << std::endl;    //OK
//
A_Test<std::string,std::string> a_s("guangzhou","huangpu"),b_s("shenzhen","baoan");
std::cout << mymin<A_Test<std::string,std::string> >(a_s,b_s) << std::endl; //OK
std::cout << mymax<A_Test<std::string,std::string> >(a_s,b_s) << std::endl; //OK

        1.7 按需增加必要支持

        当然如何把控是模板设计的度,都是设计者及使用者的考验,例如,想更进一步使用上述的模板函数和类模板:

A_Test<DataTest,DataTest> ad_s(DataTest(3,4),DataTest(4,1)),bd_s(DataTest(2,4),DataTest(3,1));

std::cout << mymin<A_Test<DataTest,DataTest> >(ad_s,bd_s) << std::endl;
std::cout << mymax<A_Test<DataTest,DataTest> >(ad_s,bd_s) << std::endl;

        显然,针对DataTest,我们需要重新去设计其在上层调用涉及到的<、>、== 这三个操作符函数声明定义后,才能被A_Test类模板和mymin、mymax函数模板所识别使用。

inline bool operator<(const DataTest& obj1, const DataTest& obj2) 
{ 
	if(obj1.id_domain<obj2.id_domain)
			return true;
	if(obj1.id_domain==obj2.id_domain&&obj1.id_point<obj2.id_point)
			return true;
	return false;
};

inline bool operator>(const DataTest& obj1, const DataTest& obj2) 
{ 
	if(obj1.id_domain>obj2.id_domain)
			return true;
	if(obj1.id_domain==obj2.id_domain&&obj1.id_point>obj2.id_point)
			return true;
	return false;
};

inline bool operator==(const DataTest& obj1, const DataTest& obj2) 
{ 
	if(obj1.id_domain!=obj2.id_domain)
			return false;
	if(obj1.id_point!=obj2.id_point)
			return false;
	return true;
};

 二、模板隐藏的那些事

        2.1 成员函数或成员变量的模板参数

        类模板内的成员函数或成员变量类型都可以具有自己的模板参数。

template <typename T>
class OUT_Class
{
public:
	void myfun1(const T& obj);    //和外围模板使用同一个模板参数
	template <typename T1>
	void myfun2(const T1& obj);    //自定义了自己的模板参数
};

template <typename T>
void OUT_Class<T>::myfun1(const T& obj)
{
	std::cout << obj << std::endl;
};

template <typename T>    //主类的模板参数
template <typename T1>   //子类的模板参数
void OUT_Class<T>::myfun2(const T1& obj)
{
	std::cout << obj << std::endl;
};

OUT_Class<int> o_a;
o_a.myfun1(10);
o_a.myfun2<double>(10.5);

        模板在外部定义时,具有多个模板参数语句template <typename ***>,一个语句用于自身,另一个语句用于外围模板,语句测顺序是从外围到内部的。

        类模板内的函数模板 或类模板可以在主类模板内部定义,也可以放置外部定义。若是函数模板在类内部定义,则是一个显式内联函数。

//
template <typename T>
class OUT_Class
{
pbulic:
	template <typename T1>
	class INT_Class1	//内部直接定义
	{
		public:
		    T1 val;
	};
	template <typename T1>
	class INT_Class2;	//外部定义
};

//
template <typename T>
template <typename T1>
class OUT_Class<T>::INT_Class2
{
	public:
		T1 val;	
};

//
OUT_Class<int>::INT_Class1<float> o_a_i1;
o_a_i1.val = 11.4;
OUT_Class<int>::INT_Class2<float> o_a_i2;
o_a_i2.val = 12.4;

        2.2 联合(Union)模板

        模板设计还允许联合(Union)模板的存在:

template <typename T>
union Chunk
{
	T obj;
	unsigned char bytes[sizeof(T)];
};

//
union Chunk<int> uobj;
uobj.bytes[0] = 0xff;
std::cout << uobj.obj << std::endl; 

        2.3 模板的虚函数问题

        类模板内的成员函数模板不能声明为为虚函数,因为虚函数调用机制实现使用了一个大小固定的表,每个虚函数都对应表的一个入口,但成员函数模板在构建虚函数表之前是无法确定的。当然,类模板内的普通成员函数设置为虚函数是没问题的。

template <typename T>
class OUT_Class
{
public:
	//
	virtual ~V_Class()    //OK
	{
		
	};
	template <typename T>
	virtual void copy(const T& obj)    //error
	{
		
	};
};

        2.4 模板的编译链接影响

        目前类模板和普通类一样,是能和一个实体共享一个名称的,虽然要尽量避免这种情况。

template <typename T1, typename T2>
class A_Test
{
};

int A_Test; //OK

        大多C++编译器都不能支持其具有C连接,因此也不建议采用extern来指定编译。并由于模板是采用外部链接的,也不能在函数内部声明模板,因为编译器无法在链接时确定到该定义。另外也不允许在模板内将模板参数再作为类名、结构名等修饰名称。

extern "C" template <typename T> class C_Test{};    //error,不支持

extern "C++" template <typename T> void Func_Test(){};//可行,但多余

//
void test_in(const int& obj)
{
	template <typename T>    //不能在函数内部声明模板
	class AC{};
	//..
};
//
template <typename T>
class AClass
{
	class T *inc;	//error
	friend class T; //error
};

         2.5 模板的模板参数

        前面讲述到模板可以进行嵌套,其实,模板参数也能进行嵌套,即模板的模板参数,它的用法和类模板的用法类似:

template<typename T> class CX
{
	public:
		T val;	
};

template <template<typename T> class CX> 
void func(CX<int>& obj)
{
	std::cout << obj.val << std::endl;
};
//
CX<int> cx_i;
cx_i.val = 20;
func(cx_i);

        同时模板的模板参数也支持缺省实参模板

template<typename T=int> 
class CX
{
	public:
		T val;	
};
//template <template<typename T> class CX> 	//class 替换成 struct union 是错误的
template <template<typename T> typename CX> 
//void func(CX<T> &obj)	//error,模板的模板参数的参数只能被自身其他参数的声明使用
void func(CX<int> &obj)
{
	std::cout << obj.val << std::endl;
};
//或者这样更好理解
template <typename T,template<typename T1> typename CX> 
void func2(CX<T> &obj)
{
	std::cout << obj.val << std::endl;
};
//
CX<> cx_def;
cx_def.val = 21;
func(cx_def);

//
func2(cx_def);

        2.6 模板实参演绎事项

        在篇一中,就讲述到,模板在使用时,可以显式指定模板实参,或者不指定,交给编译器去进行实参演绎,因此,最好是吧那些无法演绎的试产放在模板参数列表前面,从而显式指定这些实参,把支持实参演绎的放在后面。

template<typename T1, typename T2> 
//inline T2 im_cast(const T1& obj)    //尝试一下这样设计呢
inline T1 im_cast(const T2& obj)
{
	return obj;
};

//
double val_ = im_cast<double>(-30);
std::cout << val_ << std::endl;

        2.7 模板参数是函数模板

        对于模板的嵌套,还需要说明的是,函数模板可以作为函数模板的模板参数:

//函数模板嵌套
template<typename Func, typename T> 
void doSomething(Func fp,const T obj)
{
	fp(obj);
};

template<typename T> 
void doit(const T obj)
{
	std::cout << obj << std::endl;
};

//
doSomething(doit<int>,3);
doSomething(&doit<int>,4);

        2.8 模板参数是函数指针

        另外函数模板也可以作为函数指针来使用,在具体使用函数指针时,指定模板实参即可

//
template <typename T> 
void func3(const T &obj)
{
	std::cout << obj << std::endl;
};

//
void (*pfunc3)(const int &obj);
pfunc3 = func3<int>;
pfunc3(15);
//
typedef void (*PFunc)(const float &obj);
PFunc pf = func3<float>;
pf(12.5);
(*pf)(13.5);

        2.9 命名空间与模板

        编译器在模板调用时,会依据上下文进行名称查找,其查找是有作用域限制的,如果模板需要调用另一个命名空间namespace定义数据类型,就需要明确指出模板参数所包含的命名空间。

//
namespace pyfree{
	class PVal
	{
		public:
			int val;
	};
	std::ostream &operator<<(std::ostream &os, const PVal& obj)
	{
		os << "(";
		os << obj.val;
		os <<")";
		return os;
	};
};
//
template <typename T> 
void func3(const T &obj)
{
	std::cout << obj << std::endl;
};
//
pyfree::PVal pval;
pval.val = 33;
func3<pyfree::PVal>(pval);

        总之。普通函数及类需要注意的问题,函数模板及类模板使用时一样要注意,另外由于模板的特殊性,还会延展出其很多新的问题点,当然也会对编程带来全新的编程架构和编码风格。

三、源码补充

        3.1 编译

        测试代码包含两个源文件template_test.h和test.cpp,通过g++ test -o test.exe编译运行测试:

 

       3.2 源代码

          template_test.h

#ifndef _TEMPLATE_TEST_H_
#define _TEMPLATE_TEST_H_

#include <ostream>
#include <iostream>

class DataTest
{
public:
	DataTest(const int &id_d,const int &id_p) 
		: id_domain(id_d),id_point(id_p)
	{
		
	};
	~DataTest()
	{
		
	};
	int id_domain;
	int id_point;
};
inline bool operator<(const DataTest& obj1, const DataTest& obj2) 
{ 
	if(obj1.id_domain<obj2.id_domain)
			return true;
	if(obj1.id_domain==obj2.id_domain&&obj1.id_point<obj2.id_point)
			return true;
	return false;
};

inline bool operator>(const DataTest& obj1, const DataTest& obj2) 
{ 
	if(obj1.id_domain>obj2.id_domain)
			return true;
	if(obj1.id_domain==obj2.id_domain&&obj1.id_point>obj2.id_point)
			return true;
	return false;
};

inline bool operator==(const DataTest& obj1, const DataTest& obj2) 
{ 
	if(obj1.id_domain!=obj2.id_domain)
			return false;
	if(obj1.id_point!=obj2.id_point)
			return false;
	return true;
};

inline std::ostream &operator<<(std::ostream &os, const DataTest& obj)
{
	os << "(";
	os << obj.id_domain << "," << obj.id_point;
	os <<")";
	return os;
};

template <typename T1, typename T2>
class A_Test
{
public:
	A_Test(const T1 &id_d,const T2 &id_p) 
		: id_domain(id_d),id_point(id_p)
	{
		
	};
	~A_Test()
	{
		
	};
	T1 getDomainID() const
	{
		return id_domain;
	};
	T2 getPointID() const
	{
		return id_point;
	};
	template <typename T3, typename T4>
	friend std::ostream &operator<<(std::ostream &os, const A_Test<T3,T4>& obj);
	//template <typename T> friend bool operator>(const T& obj1, const T& obj2); //why is eroor
	template <typename T3, typename T4>
	friend bool operator>(const A_Test<T3,T4>& obj1, const A_Test<T3,T4>& obj2);
private:
	T1 id_domain;
	T2 id_point;
};

template <typename T1, typename T2>
inline std::ostream &operator<<(std::ostream &os, const A_Test<T1,T2>& obj)
{
	os << "(";
	os << obj.id_domain << "," << obj.id_point;
	os <<")";
	return os;
};

template <typename T1, typename T2>
inline bool operator<(const A_Test<T1,T2>& obj1, const A_Test<T1,T2>& obj2) 
{ 
	if(obj1.getDomainID()<obj2.getDomainID()) return true;
	if(obj1.getPointID()==obj2.getPointID()&&obj1.getPointID()<obj2.getPointID()) return true;
	return false;
};

template <typename T1, typename T2>
inline bool operator>(const A_Test<T1,T2>& obj1, const A_Test<T1,T2>& obj2) 
{ 
	if(obj1.id_domain>obj2.id_domain) return true;
	if(obj1.id_domain==obj2.id_domain&&obj1.id_point>obj2.id_point) return true;
	return false;
};

template <typename T>
inline T mymin(const T &v1, const T &v2)
{
	return (v1<v2)?v1:v2;
};

template <typename T>
inline T mymax(const T &v1, const T &v2)
{
	return (v1>v2)?v1:v2;
};

//
template <typename T>
class OUT_Class
{
public:
	//
	virtual ~OUT_Class()
	{
		
	};
	/*
	template <typename T>
	virtual void copy(const T& obj)
	{
		
	};
	*/
	//
	void myfun1(const T& obj);
	template <typename T1>
	void myfun2(const T1& obj);
	//
	template <typename T1>
	class INT_Class1	//内部直接定义
	{
		public:
			T1 val;
	};
	template <typename T1>
	class INT_Class2;	//外部定义
};
template <typename T>
void OUT_Class<T>::myfun1(const T& obj)
{
	std::cout << obj << std::endl;
};

template <typename T>
template <typename T1>
void OUT_Class<T>::myfun2(const T1& obj)
{
	std::cout << obj << std::endl;
};

template <typename T>
template <typename T1>
class OUT_Class<T>::INT_Class2
{
	public:
		T1 val;	
};

template <typename T>
union Chunk
{
	T obj;
	unsigned char bytes[sizeof(T)];
};

//extern "C" template <typename T> class C_Test{}; //error,不支持

extern "C++" template <typename T> void Func_Test(){};
/*
void test_in(const int& obj)
{
	template <typename T>	//不能在函数内部声明模板
	class AC{};
	//..
};
template <typename T>
class AClass
{
	class T *inc;	//error
	friend class T; //error
};
*/
template<typename T=int> 
class CX
{
	public:
		T val;	
};
//template <template<typename T> class CX> 	//class 替换成 struct union 是错误的
template <template<typename T> typename CX> 
//void func(CX<T> &obj)	//error,模板的模板参数的参数只能被自身其他参数的声明使用
void func(CX<int> &obj)
{
	std::cout << obj.val << std::endl;
};

template <typename T,template<typename T1> typename CX> 
void func2(CX<T> &obj)
{
	std::cout << obj.val << std::endl;
};
//
template<typename T1, typename T2> 
inline T1 im_cast(const T2& obj)
{
	return obj;
};
//
template<typename Func, typename T> 
void doSomething(Func fp,const T obj)
{
	fp(obj);
};

template<typename T> 
void doit(const T obj)
{
	std::cout << obj << std::endl;
};
//
namespace pyfree{
	class PVal
	{
		public:
			int val;
	};
	std::ostream &operator<<(std::ostream &os, const PVal& obj)
	{
		os << "(";
		os << obj.val;
		os <<")";
		return os;
	};
};
//
template <typename T> 
void func3(const T &obj)
{
	std::cout << obj << std::endl;
};

#endif

        test.cpp

#include "template_test.h"
#include <string>

int main(int argc, char* argv[])
{
	std::cout << mymin<std::string>("guangzhou","shenzhen") << std::endl;
	std::cout << mymax<std::string>("guangzhou","shenzhen") << std::endl;
	//
	DataTest adt(3,4),bdt(3,6);
	mymin<DataTest>(adt,bdt);
	//
	A_Test<int,float> a(2,4.2),b(2,3.8);
	std::cout << mymin<A_Test<int,float> >(a,b) << std::endl;
	std::cout << mymax<A_Test<int,float> >(a,b) << std::endl;
	//
	A_Test<std::string,std::string> a_s("guangzhou","huangpu"),b_s("shenzhen","baoan");
	if(a_s<b_s) std::cout << "a_s is min" << std::endl;
	if(a_s>b_s) std::cout << "a_s is max" << std::endl;
	//
	std::cout << mymin<A_Test<std::string,std::string> >(a_s,b_s) << std::endl;
	std::cout << mymax<A_Test<std::string,std::string> >(a_s,b_s) << std::endl;
	//
	A_Test<DataTest,DataTest> ad_s(DataTest(3,4),DataTest(4,1)),bd_s(DataTest(2,4),DataTest(3,1));
	std::cout << mymin<A_Test<DataTest,DataTest> >(ad_s,bd_s) << std::endl;
	std::cout << mymax<A_Test<DataTest,DataTest> >(ad_s,bd_s) << std::endl;
	//
	OUT_Class<int> o_a;
	o_a.myfun1(10);
	o_a.myfun2<double>(10.5);
	//
	OUT_Class<int>::INT_Class1<float> o_a_i1;
	o_a_i1.val = 11.4;
	OUT_Class<int>::INT_Class2<float> o_a_i2;
	o_a_i2.val = 12.4;
	//
	union Chunk<int> uobj;
	uobj.bytes[0] = 0xff;
	std::cout << uobj.obj << std::endl; 
	//
	int A_Test;	//OK
	//
	CX<int> cx_i;
	cx_i.val = 20;
	func(cx_i);
	CX<> cx_def;
	cx_def.val = 21;
	func(cx_def);
	//
	func2(cx_i);
	func2(cx_def);
	//
	double val_ = im_cast<double>(-30);
	std::cout << val_ << std::endl;
	//
	doSomething(doit<int>,3);
	doSomething(&doit<int>,4);
	//
	void (*pfunc3)(const int &obj);
	pfunc3 = func3<int>;
	pfunc3(15);
	//
	typedef void (*PFunc)(const float &obj);
	PFunc pf = func3<float>;
	pf(12.5);
	(*pf)(13.5);
    //
	pyfree::PVal pval;
	pval.val = 33;
	func3<pyfree::PVal>(pval);
	return 0;
};

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

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

相关文章

JVM基础学习

JVM分为两个子系统,两个组件一个子系统是Class loader类装载系统&#xff0c;另一个子系统是Execution Engine执行引擎一个组件是Runtime data area 运行时数据区&#xff0c;Native Interface 本地接口Class loader&#xff1a;根据给定的全限定类名来装载class文件到运行时数…

借助docker, 使用verdaccio搭建npm私服

为何要搭建npm私服 搭建npm私服好处多多&#xff0c;网上随便一篇教程搜出来都罗列了诸多好处&#xff0c;譬如: 公司内部开发环境与外网隔离&#xff0c;内部开发的一些库高度隐私不便外传&#xff0c;内网搭建npm服务保证私密性同属内网&#xff0c;可以确保使用npm下载依赖…

RPC技术选型

前言HTTP1.0 & HTTP1.1 & HTTP2.0 & RPCHTTP1.0无法复用连接HTTP1.0 协议时&#xff0c;HTTP 调用还只能是短链接调用&#xff0c;每次发送请求的时候&#xff0c;都需要进行一次TCP的连接&#xff0c;而TCP的连接释放过程又是比较费事的。这种无连接的特性会使得网…

金三银四跳槽季,JAVA面试撸题就来【笑小枫】微信小程序吧~

JAVA面试撸题就来【笑小枫】微信小程序啦~ 疫情已过&#xff0c;金三银四即将到来&#xff0c;小伙伴们是否有跳槽的打算呢&#xff1f;不管有没有&#xff0c;技术不能丢&#xff0c;让我们一起来撸题吧。 博主最近整理了一批面试题&#xff0c;包括JAVA基础、多线程与锁、Red…

媒体邀约电视台对商业活动选题有什么要求?如何邀请电视台报道

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好随着互联网&#xff0c;移动互联网的快速发展&#xff0c;大众在电视上消磨的时间就越来越短了&#xff0c;但是随着新媒体的出现&#xff0c;传统媒体不断的跟进发展&#xff0c;不断打造自己的媒体矩阵&#xff0c;虽然离…

界面控件DevExpress WinForm——轻松构建类Visual Studio UI(一)

DevExpress WinForm拥有180组件和UI库&#xff0c;能为Windows Forms平台创建具有影响力的业务解决方案。DevExpress WinForm能完美构建流畅、美观且易于使用的应用程序&#xff0c;无论是Office风格的界面&#xff0c;还是分析处理大批量的业务数据&#xff0c;它都能轻松胜任…

图穷了,来搞一搞(内附源码)

本章继续我们的爬虫教程&#xff0c;爬什么呢 &#xff0c;还是斗图&#xff0c;娱乐性的东西&#xff0c;为什么要爬&#xff1f; 因为我图库空了&#xff0c;发现这个网址的图库还是很丰富的。 「注意&#xff1a;如下文&#xff0c;是封装后拆分的&#xff0c;所以详情参照…

Word处理控件Aspose.Words功能演示:使用 C# 在电子邮件正文中发送 Word 文档

Aspose.Words 是一种高级Word文档处理API&#xff0c;用于执行各种文档管理和操作任务。API支持生成&#xff0c;修改&#xff0c;转换&#xff0c;呈现和打印文档&#xff0c;而无需在跨平台应用程序中直接使用Microsoft Word。此外&#xff0c;API支持所有流行的Word处理文件…

《精通Spring4.x 企业应用开发实战》第12章 Spring 的事务管理难点剖析

目录标题前言一、DAO 和事务管理的牵绊二、应用分层的迷惑三、事务方法嵌套调用的迷茫(事务传播行为)1.Spring 事务传播机制回顾2.相互嵌套的服务方法四、多线程的困惑1. Spring 通过单实例化 Bean 简化多线程问题2.启动独立线程调用事务方法五、联合军种作战的混乱1.Spring 事…

用了 ChatGPT 后,我迫不及待的问了它这 10 个问题

前言20230208 日&#xff0c;ChatGPT 已经联手微软登录必应搜索引擎&#xff0c;谷歌、百度等巨头更是紧随其后着急麻慌的推出自己的 AI 聊天产品&#xff0c;有理由相信&#xff0c;传统搜索引擎时代已经结束&#xff0c;不远的未来&#xff0c;每个人家里都会有一个可以陪你聊…

数字芯片是怎样设计出来的?

芯片在我们的生活和工作中无处不在。例如&#xff0c;交通智能卡就嵌入了一颗带有微处理器、储存单元、芯片操作系统的芯片&#xff1b;而手机的主板则集成了数百颗芯片&#xff0c;有的负责无线电收发、有的负责功率放大、还有的负责存储照片和文件、处理音频&#xff0c;完成…

如何查找专用 IP 地址?

专用 IP 地址&#xff1a;这些地址在网络内部使用&#xff0c;例如&#xff0c;平板电脑、Wi-Fi 相机、无线打印机和台式电脑使用的家庭网络。这些类型的 IP 地址为设备提供了一种与路由器和专用家庭网络上的其他设备进行通信的方法。私有IP地址可以手动设置&#xff0c;也可以…

Mr. Cappuccino的第40杯咖啡——Kubernetes之Pod生命周期

Kubernetes之Pod生命周期Pod生命周期官方文档Pod的状态初始化容器案例钩子函数Exec命令TCPSocketHTTPGet案例容器探测Exec命令TCPSocketHTTPGet探测时间重启策略Pod生命周期官方文档 Pod生命周期官方文档 Pod的状态 pending&#xff1a;挂起&#xff0c;apiserver创建了pod资…

2月第2周榜单丨飞瓜数据B站UP主排行榜(哔哩哔哩平台)发布!

飞瓜轻数发布2023年2月6日-2月12日飞瓜数据UP主排行榜&#xff08;B站平台&#xff09;&#xff0c;通过充电数、涨粉数、成长指数三个维度来体现UP主账号成长的情况&#xff0c;为用户提供B站号综合价值的数据参考&#xff0c;根据UP主成长情况用户能够快速找到运营能力强的B站…

python基于django+vue微信小程序的校园跑腿平台

随着计算机技术的高速发展,现代计算机系统已经从以计算为中心向以信息化处理为中心的方向发展。而校园跑腿服务系统,不仅需要在硬件上为现代社会的学生们提供一个学习知识,获取知识的环境,更要在软件上为愿意上课的人提供必要的便利。于是校园跑腿服务系统系统便应运而生。 目前…

企业与第三方供应商合作时,会存在哪些安全风险?

随着现代社会的发展&#xff0c;企业供应链、产业供应链已日渐成熟。其中&#xff0c;供应商与企业的关系也由最初的纯粹买卖关系发展成了合作伙伴关系。在整个供应链体系中&#xff0c;供应商与其受众承担着供应链中环环相扣的责任&#xff0c;可以说&#xff0c;企业安全的薄…

站在行业C位,谷医堂打开健康管理服务新思路

对于农村及贫困地区老百姓来说&#xff0c;由于交通因素和家庭经济条件制约&#xff0c;看病难致身体调理情况一直不太乐观&#xff0c;这也导致心理压力很大。然而&#xff0c;随着近年中医药产业崛起与快速发展&#xff0c;这种局面很快就会得到改观&#xff0c;以湖南谷医堂…

MySQL InnoDB表的碎片量化和整理(data free能否用来衡量碎片?)

网络上有很多MySQL表碎片整理的问题&#xff0c;大多数是通过demo一个表然后参考data free来进行碎片整理&#xff0c;这种方式对myisam引擎或者其他引擎可能有效&#xff08;本人没有做详细的测试&#xff09;.对Innodb引擎是不是准确的&#xff0c;或者data free是不是可以参…

让逆向工程师们头疼的代码混淆,就像永远也走不出的“浪浪山”

目录 代码混淆究竟是什么&#xff1f; 如何做代码混淆&#xff1f; 代码混淆不等于加密 App 加固非一时之功 “我想离开浪浪山。” 在数次尝试破解某个App 时&#xff0c;某个逆向工程师无奈感慨道。 逆向工程师顾名思义就是把一个个完整的软件逆推&#xff0c;还原成一段段…

【MySQL】数据库操作

文章目录1、创建和管理数据库1.1 创建数据库1.2 查看数据库1.3 修改数据库1.4 删除数据库2、 创建表2.1 创建表CREATE2.2 创建表AS3、修改表3.1 添加列 ALTERT TABLE ADD3.2 修改列 ALTER TABLE MODIFY3.3 重命名列 ALTER TABLE CHANGE3.4 删除列 ALTER TABLE DROP4、重命名表 …