c++可调用对象、function类模板与std::bind

函数调用与函数调用运算符

先写一个简单的函数,如下:

/*函数的定义*/
int func(int i)
{
	cout<<"这是一个函数\t"<<i<<endl;
}

void test()
{
	func(1);//函数的调用
}

通过这个普通的函数可以看到,调用一个函数很简单,首先是函数名,后面跟一对圆括号,如果这个函数有参数,就在函数调用位置的函数名后圆括号中给出实参,在函数定义位置函数名后圆括号中给出形参

函数调用总离不开一对圆括号,这个“()”就叫函数调用运算符

特别的,如果在类中重载了这个函数调用运算符(),我们就可以像使用函数一样使用类的对象,换句话说就可以像函数调用一样来“调用”该类的对象

这种重载了()的类对象,就叫做函数对象,如下


class biggerThanZero
{
public:
	int m_i;
	biggerThanZero(int i):m_i(i)
	{
		cout<<"构造函数调用了"<<endl;
	}

	int operator()(int num)
	{
		if(num<0) return 0;
		return num;
	}
};

void test()
{
	biggerThanZero bz1(10);//这是对象定义的初始化,会调用类的构造函数
	
	int num=-20;
	cout<<bz1(num)<<endl;//这才是调用了类的函数调用运算符()

}

        

分析代码并观察运行结果可以看到,测试函数中的第一行代码是一个正常的调用构造函数对类对象进行初始化的代码,而接下来的两行代码里的bz1(num)才是对函数调用运算符的调用,观察其书写形式可以发现,其调用形式与文章开头的普通函数func调用形式是相同的

这就是可调用对象,即

  • func函数
  • 重载了函数调用运算符“()”的biggerthanzero类所生成的对象bz1,我们调用bz1时可以像调用函数那样进行调用

问题的引出

继续观察这两个可调用对象,可以发现他们的调用形式都是相同的,也就是int (int),同时这也是func的函数类型

注:函数类型就是返回值类型和参数类型的组合。

如func函数中的参数类型是int,而返回值类型int,因此func的函数类型就是int (int)。

同理,假如现在有一个函数的声明为 void fun(double);则这个fun函数的函数类型就是

void (double)

而我们又知道,我们可以使用一个函数指针来保存函数地址,那么操作这个函数指针就等同于操作这个函数指针所指向的函数,如下所示

void test()
{
	int (*fp)(int);//定义一个函数指针,也就是可以保存函数地址的指针
	fp=func;//在c++中,函数名就是这个函数的地址,也可以使用fp=&func
	fp(1);//由于fp现在指向func,因此调用fp(1)就等同于调用func(1)
}

但是对于相同的调用形式的函数对象,我们却不能使用函数指针进行统一调用

void test()
{
	int (*fp)(int,int);
	biggerThanZero bz(0);//类对象初始化
	fp=bz;//报错
}

这说明系统并没有把类biggerthanzero的对象bz看成一个函数指针,当然系统更不可能把类biggerthanzero看成一个函数指针

那么问题来了,对于具有相同调用形式的不同可调用对象,是否可以使用一个统一的调用呢?

function类模板的引出

暂时先搁置一下上边提到的概念,我们暂时已经知道以下三点:

  1. 普通函数和重载了函数调用运算符()的对象都是可调用对象
  2. 可以使用函数指针统一调用普通函数这个可调用对象
  3. 但是同为可调用对象的函数对象却无法使用函数指针进行统一调用

接下来,我们做两个实验来引出function类模板

观察以下代码

int max(int a,int b)
{
	return (a>b)?a:b;
}

int min(int a,int b)
{
	return (a<b)?a:b;
}

void test()
{
	cout<<max(1,2)<<endl;
	cout<<min(1,2)<<endl;
}

在这段代码里,我们定义了两个函数,分别是max函数和min函数,并且我们在测试函数中调用了他们。

实验一

仔细观察这段代码,可以发现max函数和min函数的函数类型是相同的,都是int(int ,int ),因此我们可以使用一个函数指针来对他们进行统一形式的调用,如下

void test()
{
	int (*fp)(int,int);//定义一个函数指针
	fp=max;//将max函数的函数地址赋给函数指针fp
	cout<<fp(1,2)<<endl;//现在fp指向的是max函数,调用fp(1,2)等同于调用max(1,2)
	fp=min;//将min函数的函数地址赋给函数指针fp
	cout<<fp(1,2)<<endl;//现在fp指向的是min函数,调用fp(1,2)等同于调用min(1,2)
}

运行结果如下: 

 其实,这就是函数指针最开始的作用,但是后来面向对象诞生之后这种写法就被替代了。

实验二

再进一步,我们可以使用一个map来对这段逻辑进行重写,如下:

void test()
{
	map<string,int(*)(int,int)> mp;
	mp.insert({"max",max});
	mp.insert({"min",min});

	cout<<mp["max"](1,2)<<endl;//等同于调用max(1,2)
	cout<<mp["min"](1,2)<<endl;//等同于调用min(1,2)
}

 在上述代码里,我们使用一个map对上述调用形式进行了统一的改写,map键值对的键类型是string,而值类型是函数类型为int(int,int)的函数指针

综上观察,我们可以说

  • 同为可调用对象,且函数对象和普通函数的调用形式是相同的,但是我们只能使用函数指针对调用形式相同(函数类型名相同)的普通函数进行统一调用
  • 但是我们却无法再进一步对相同调用形式的函数对象进行统一调用

为了解决以上问题,c++引入了function模板的概念,我们直接看代码

int func(int i)
{
	cout<<"这是一个函数\t"<<i<<endl;
}

class biggerThanZero
{
public:
	int m_i;
	biggerThanZero(int i):m_i(i)
	{
		cout<<"构造函数调用了"<<endl;
	}

	int operator()(const int& num)
	{
		if(num<0) return 0;
		return num;
	}
};

void test()
{
	function<int(int)> fp2;
	fp2=func;
	cout<<fp2(10)<<endl;
	biggerThanZero bz(0);
	fp2=bz;
	cout<<fp2(-1)<<endl;
}

现在我们可以拿出function类模板的定义和作用了

function的类模板,就是用来包装一个可调用对象的,换句话说,它可以像函数指针那样,将不同的可调用对象包装成一个相同的东西,以方便我们使用

正如我们使用函数指针那样,对于同一个函数指针,我们可以赋给它不同的函数地址,只要这些函数的调用形式是相同的,我们就可以使用这一个指针实现不同的函数调用,而function类模板实现的就是类似的功能

现在,我们就可以进一步使用map来对可调用对象进行统一封装了

void test()
{
	biggerThanZero bz(0);
	map<string,function<int(int)>> mp={
		{"func",func},
		{"biggerThanZero",bz}
	};

	mp["func"](20);
	cout<<mp["biggerThanZero"](-10)<<endl;
}

因此,我们可以把函数、可调用对象等都包装成function<int(int)>对象。 

但是,值得说明的是,无法对重载的函数包装进function中

我们只能这样写

void test()
{
	function<int(int,int)> fc;
	int(*fp)(int,int)=max;
	fc=fp;
	cout<<fc(1,2)<<endl;
	
}

可调用对象

在上边我们已经说了有关可调用对象的概念,这里再强调一下,普通函数和重载了函数调用运算符的类对象(也就是函数对象)都是可调用对象,他们都可以像使用普通函数调用那样进行调用

除了上述两个可调用对象外,其余还有一些可调用对象,我们一并进行介绍

1.函数指针

普通函数的函数名就是一个函数地址,是一个可调用对象,我们已经介绍过了


void func()
{
	cout<<"func运行了"<<endl;
}

void test()
{
	void(*fp)()=func;//定义函数指针,并使用func的函数地址进行初始化
	fp();//调用函数,这是一个可调用对象
}

2.函数对象(仿函数)

具有operator()成员函数的类对象也是一个可调用对象,我们也已经介绍过了。在这里我们给出函数对象(仿函数)的定义

仿函数的定义:仿函数(functors)又称为函数对象(function objects),是一个能行使函数功能的类所定义的对象。仿函数的语法几乎和普通的函数调用一样

class TC
{
public:
	void operator()(int tv)
	{
		cout<<"TC::operator()执行了\t"<<tv<<endl;
	}
};

void test()
{
	TC tc;
	tc(10);//像调用函数那样调用()操作符,等价于tc.operator(10)
}

3.可转换为函数指针的类对象

其实,函数指针都是可调用对象,可以在代码中像函数一样使用

这在我们之前探讨函数指针的时候已经见到过了,当时我们定义了两个函数类型为int(int,int)的max函数与min函数,并使用同一个函数指针分别指向max函数和min函数的地址进行调用,使用函数指针进行调用的时候我们也观察到其调用与普通函数调用无异

现在我们说一种特殊的函数指针,如下

可转换为函数指针的类对象也叫做函数对象或者仿函数

在说这个问题之前,先说一下有关类型转换的问题

我们知道

  1. 一个类对象的类型就是定义这个对象的类本身,比如上边的对象tc的类型就是TC。
  2. 常用的普通类型之间是可以进行类型转换的,如int可以转换成double,而double可以转换成int
  3. 再进一步,我们又知道,我们可以将一个int类型的变量转换为一个类类型,只要这个类提供了对应的构造函数,如下:
    class TS
    {
    public:
    	int m_i;
    
    	TS(){
    		cout<<"无参构造函数调用了"<<endl;
    	};
    	TS(int a):m_i(a)
    	{
    		cout<<"带参数的构造函数调用了"<<endl;
    	}
    };
    
    
    void test()
    {
    	TS ts;
    	ts=10;//通过构造一个临时对象,将int类型的10转换为一个TS类型的变量
    }
    

  4. 当然,上述这种类型转换是隐式,而如果我们不希望编译器为我们使用这种int到类型之间的隐式类型转换,使用显示类型转换也是可以的,如下:
     

    class TS
    {
    public:
    	int m_i;
    
    	TS(){
    		cout<<"无参构造函数调用了"<<endl;
    	};
    	explicit TS(int a):m_i(a)//声明explicit,不允许编译器使用隐式类型转换
    	{
    		cout<<"带参数的构造函数调用了"<<endl;
    	}
    };
    
    
    void test()
    {
    	TS ts;
    	//由于构造函数使用了explicit关键字,不允许编译器使用隐式类型转换,因此我们只能使用显示类型转换
    	ts=TS(10);
    }

那么问题来了,是否可以将一个类类型转换为一个普通类型呢,如将上述的TS类类型转换成一个int类型?

答案当然也是肯定的。

类型转换运算符

类型转换运算符,也叫做类型转换函数,是类的一种特殊成员函数,它能将一个类类型对象转成某个其他类型数据

其一般形式为:

先说以下几点说明,我们再使用代码进行实验

  1. 末尾的const是可选项,表示不应该改变待转换对象的内容,但不是必须有const
  2. “类型名”表示要转换成的某种类型,一般只要是能作为函数返回类型的类型都可以。所以一般不可以转成数组类型或者函数类型(把一个函数声明去掉函数名剩余的部分就是函数类型,如void(inta,intb)),但是转换成数组指针、函数指针、引用等都是可以的
  3. 类型转换运算符,没有形参(形参列表必须为空),因为类型转换运算符是隐式执行的,所以无法给这些函数传递参数。同时,也不能指定返回类型,但是却会返回一个对应类型(“类型名”所指定的类型)的值
  4. 必须定义为类的成员函数

接下来,我们做实验以做验证

class TS
{
public:
	int m_i;

	TS(){
		cout<<"无参构造函数调用了"<<endl;
	};
	TS(int a):m_i(a)//声明explicit,不允许编译器使用隐式类型转换
	{
		cout<<"带参数的构造函数调用了"<<endl;
	}

	//类型转换运算符,必须定义为一个成员函数
	//表示编译器可以调用这个函数隐式的将TS类型的变量转换为一个int类型的变量
	operator int() const
	{
		return m_i;
	}
};


void test()
{
	TS ts1;//普通构造
	ts1=10;//调用带参构造,将int类型的10隐式转换为TS类型的变量
	int res=ts1+20;//隐式调用operator int()成员函数将对象ts1转换为int类型变量之后再做加法运算
	cout<<res<<endl;
	int res2=ts1.operator int()+30;//也可以显示的调用operator int()成员函数将对象ts1转换为int类型变量
	cout<<res2<<endl;
}

上述代码执行过程为

  1. 调用无参构造函数构造ts1对象
  2. 调用有参构造函数并生成一个临时对象,隐式的将int类型变量的10转换为一个TS类型变量,再把这个临时对象赋给ts1
  3. 隐式的调用operator int()成员函数将ts1转换为int类型,再进行加法运算,执行10+20
  4. 显示的调用operator int()成员函数将ts1转换为int类型,再进行加法运算,执行10+30

同样的,如果我们想拒绝隐式类型转换,也可以使用显示类型转换运算,如下:


class TS
{
public:
	int m_i;

	TS(){
		cout<<"无参构造函数调用了"<<endl;
	};
	TS(int a):m_i(a)//声明explicit,不允许编译器使用隐式类型转换
	{
		cout<<"带参数的构造函数调用了"<<endl;
	}

	//类型转换运算符,必须定义为一个成员函数
	//表示编译器可以调用这个函数隐式的将TS类型的变量转换为一个int类型的变量
	explicit operator int() const//拒绝编译器使用隐式类型转换
	{
		return m_i;
	}
};


void test()
{
	TS ts1;//普通构造
	ts1=10;//调用带参构造,将int类型的10隐式转换为TS类型的变量
	int res=static_cast<int>(ts1)+20;//显示类型转换
	cout<<res<<endl;
}

现在我们转回正题,我们现在已经知道了,类类型也可以转换为其他类型,如int类型,而类类型也同样可以转换为函数指针类型,而这种可以转换为函数指针的类对象,也是一种可调用对象 

为了说明问题,我们继续写一个测试代码

class TS
{
public:
	int m_i;

	TS(){
		cout<<"无参构造函数调用了"<<endl;
	};
	TS(int a):m_i(a)//声明explicit,不允许编译器使用隐式类型转换
	{
		cout<<"带参数的构造函数调用了"<<endl;
	}

	static void myfunc(int v1)
	{
		cout<<"static myfunc函数执行了\t"<<v1<<endl;
	}

	//类型定义,void(*)(int)是一个函数指针类型,使用tfpoint替换这个类型
	//等价于typedef void(*tfpoint)(int)
	using tfpoint=void(*)(int);
	operator tfpoint()
	{
		cout<<"类型转换运算符调用了"<<endl;
		return myfunc;
	}
};


void test()
{
	TS ts;
	//执行operator tfpoint(),之后再执行myfunc(123)
	ts(123);
}

4.lambda表达式

lambda表达式也是一种可调用对象,参见

lambda表达式c++-CSDN博客

可调用对象总结

其实,可调用对象首先被看作一个对象,程序员可以对其使用函数调用运算符“()”,那就可以称其为“可调用的”

function类模板

如果找通用性,上述提到的这几种可调用对象的调用形式都比较统一,那么,有没有什么方法能够把这些可调用对象的调用形式统一一下呢?有,那就是使用std::function把这些可调用对象包装起来。这在我们之前也已经实验过了

但有一点需要注意,function类模板用来往里装各种可调用对象,但是它不能装类成员函数指针,因为类成员函数指针是需要类对象参与才能完成调用的。

std::function类模板的特点是:通过指定模板参数,它能够用统一的方式来处理各种可调用对象。

1.绑定普通函数

void func1(int num)
{
	cout<<"这是一个普通函数func1:"<<num<<endl;
}

void test()
{
	function<void(int)> fp1=func1;//绑定一个普通函数
	func1(10);
}

2.绑定类的静态成员函数


class TC
{
public:
	static void stcfunc(int num)
	{
		cout<<"TC类的静态成员函数执行了:"<<num<<endl;
	}
};


void test()
{
	function<void(int)> fp2=TC::stcfunc;
	fp2(20);
}

3.绑定仿函数


class TC
{
public:
	int m_i;
	TC(int i=0):m_i(i)
	{
		cout<<"构造函数运行了"<<endl;
	}

	void operator()(int num)
	{
		cout<<"函数调用运算符运行了:"<<num<<endl;
	}

	static void stcfunc(int num)
	{
		cout<<"TC类的静态成员函数执行了:"<<num<<endl;
	}
};


void test()
{
// 	function<void(int)> fp1=func1;//绑定一个普通函数
// 	func1(10);

	function<void(int)> fp2=TC::stcfunc;
	fp2(20);

	TC tc;
	function<void(int)> fp3=tc;//TC类声明了函数调用运算符,因此对象tc是一个函数对象
	fp3(30);
}

std::bind绑定器

std::bind能将对象以及相关的参数绑定到一起,绑定完后可以直接调用,也可以用std::function进行保存,在需要的时候调用。

 std::bind有两个意思:·

  • 将可调用对象和参数绑定到一起,构成一个仿函数,所以可以直接调用。·
  • 如果函数有多个参数,可以绑定部分参数,其他的参数在调用的时候指定。

绑定普通函数

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

void func1(int x,int y,int z)
{
    cout<<"x="<<x<<",y="<<",z="<<z<<endl;
}

void test()
{
    function<void(int,int,int)> bf1=bind(func1,10,20,30);
    bf1(10,20,30);

    auto bf2=bind(func1,10,20,30);
    bf2();

}

int main()
{
    test();
    // system("pause");
    return 0;
}

参数占位符

#include<iostream>
#include<functional>
using namespace std;
using namespace placeholders;

void func1(int x,int y,int z)
{
    cout<<"x="<<x<<",y="<<y<<",z="<<z<<endl;
}

void test()
{
    //_1和_2分别表示func1的第一个参数和第二个参数暂时不定,需要自己传入
    auto bf2=bind(func1,_1,_2,30);
    bf2(10,20);

    auto bf3=bind(func1,_2,_1,30);
    bf3(10,20);
}

int main()
{
    test();
    // system("pause");
    return 0;
}

可以看到,分别调整参数占位符的位置后,输出的结果是不一样的 

绑定函数对象


class CQ
{
public:
    void operator()(int x,int y)
    {
        cout<<x<<"\t"<<y<<endl;
    }
};

void test()
{
    CQ cq;
    bind(cq,10,20)();

    bind(cq,_1,20)(5);
}

绑定类成员函数

#include<iostream>
#include<functional>
using namespace std;
using namespace placeholders;

void func1(int x,int y,int z)
{
    cout<<"x="<<x<<",y="<<y<<",z="<<z<<endl;
}

class CQ
{
public:
    int m_a=0;
    void operator()(int x,int y)
    {
        cout<<x<<"\t"<<y<<endl;
    }

    void classFunc(int num)
    {
        cout<<"this is a class func:"<<num<<endl;
        m_a=num;
    }
};

void test()
{
    CQ cq;
    // bind(cq,10,20)();

    // bind(cq,_1,20)(5);

    bind(&CQ::classFunc,cq,55)();
    cout<<"m_a="<<cq.m_a<<endl;

    bind(&CQ::classFunc,&cq,_1)(23);
    cout<<"m_a="<<cq.m_a<<endl;

}

int main()
{
    test();
    // system("pause");
    return 0;
}

 注意,上面的代码中

第一个std::bind的调用中,第二个参数cq会导致生成一个临时的CQ对象,std::bind是将该临时对象和相关的成员函数以及多个参数绑定到一起,后续对myfunpt成员函数的调用修改的是这个临时的CQ对象的m_a值,并不影响真实的cq对象的m_a值。

如果将std::bind的第二个参数cq前增加&,这样就不会导致生成一个临时的CQ对象,后续的myfunpt调用修改的就会是cq对象的m_a值。

这就是为什么第二个std::bind模板调用后m_a的值改变的原因

绑定类成员变量

#include<iostream>
#include<functional>
using namespace std;
using namespace placeholders;

void func1(int x,int y,int z)
{
    cout<<"x="<<x<<",y="<<y<<",z="<<z<<endl;
}

class CQ
{
public:
    int m_a;
    CQ(int num=0):m_a(num)
    {
        cout<<"构造函数调用了"<<endl;
    }

    CQ(const CQ& cq):m_a(cq.m_a)
    {
        cout<<"拷贝构造函数调用了"<<endl;
    }

    void operator()(int x,int y)
    {
        cout<<x<<"\t"<<y<<endl;
    }

    void classFunc(int num)
    {
        cout<<"this is a class func:"<<num<<endl;
        m_a=num;
    }
    virtual ~CQ()
    {
        cout<<"析构函数执行了"<<endl;
    }

};

void test()
{
    CQ cq;
    function<int&(void)> fp=bind(&CQ::m_a,cq);
    fp()=66;
    cout<<cq.m_a<<endl;
}

int main()
{
    test();
    // system("pause");
    return 0;
}

注意观察输出

把成员变量地址当函数一样绑定,绑定的结果放在std::function<int &(void)>里保存。换句话说,就是用一个可调用对象的方式来表示这个变量,bind这个能力还是比较神奇的。

重点分析一下代码行“bf7()=60;”,因为其上面的那行代码用了&cq,所以这里等价于cq.m_a=60。如果cq前不用&,发现会调用两次CQ类的拷贝构造函数。

为什么调用两次拷贝构造函数呢?

第一次是因为第一个参数为cq,所以利用cq产生一个临时的CQ对象

第二次是因为std::bind要返回一个CQ对象(确切地说是经过std::bind包装起来的CQ对象),所以要返回的这个CQ对象(仿函数)复制自这个临时CQ对象,但bind这行执行完毕后,临时CQ对象被释放,返回的这个CQ对象(仿函数)放到了bf7里。 

所以上述std::bind代码行中,一般都应该用&cq,否则最终会多调用两次拷贝构造函数和两次析构函数,用了&cq后这4次调用全省了,提高了程序运行效率。

void test()
{
    CQ cq;
    function<int&(void)> fp=bind(&CQ::m_a,&cq);
    fp()=66;
    cout<<cq.m_a<<endl;
}

总结

  1. 因为有了占位符(placeholder)这种概念,所以std::bind的使用就变得非常灵活。可以直接绑定函数的所有参数,也可以仅绑定部分参数。绑定部分参数时,就需要通过std::placeholders来决定bind所在位置的参数将会属于调用发生时的第几个参数。
  2. std::bind的思想实际上是一种延迟计算的思想,将可调用对象保存起来,然后在需要的时候再调用。
  3. std::function一般要绑定一个可调用对象,类成员函数不能被绑定。而std::bind更加强大,成员函数、成员变量等都能绑定。

现在通过std::function和std::bind的配合,所有的可调用对象都有了统一的操作方法。

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

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

相关文章

transbigdata 笔记: 官方文档示例3:车辆轨迹数据处理

1 读取数据 轨迹数据质量分析 这一部分和 transbigdata笔记&#xff1a;data_summary 轨迹数据质量/采样间隔分析-CSDN博客 的举例是一样的 import pandas as pd import geopandas as gpd import transbigdata as tbddata pd.read_csv(Downloads/TaxiData-Sample.csv, names…

微服务实战项目_天机学堂01_初识项目

文章目录 一.项目简述二.Jenkins三.模拟真实业务:紧急bug修复和代码阅读四.测试和部署五.代码阅读-获取登录用户 一.项目简述 Q:天机学堂是什么? A:天机学堂是一个基于微服务架构的生产级在线教育项目 主要有两个端(项目已上线,可以点击查看): 管理后台: https://tjxt-admi…

项目配置集成unocss指南

项目配置集成 unocss 指南 什么是 UnoCSS&#xff1f; Unocss 是一个基于 Tailwind CSS的工具 &#xff0c;它通过静态分析 HTML 和 CSS 代码&#xff0c;自动消除未使用的样式&#xff0c;以减小生成的 CSS 文件大小。这个工具可以帮助开发者在使用 Tailwind CSS 进行开发时…

【linux】visudo

碎碎念 visudo命令是用来修改一个叫做 /etc/sudoers 的文件的&#xff0c;用来设置哪些 用户 和 组 可以使用sudo命令。并且使用visudo而不是使用 vi /etc/sudoers 的原因在于&#xff1a;visudo自带了检查功能&#xff0c;可以判断是否存在语法问题&#xff0c;所以更加安全 …

大神们都在用的5款AI写作软件

在当今信息爆炸的时代&#xff0c;写作已经成为了人们生活和工作中不可或缺的一部分。然而&#xff0c;对于许多人来说&#xff0c;写作并不是一件轻松的事情。幸运的是&#xff0c;随着人工智能技术的不断发展&#xff0c;AI写作软件应运而生。这些软件利用先进的自然语言处理…

HTML5:dialog

JavaScript 练手小技巧&#xff1a;HTML5 的 dialog 标签制作对话框_dialog html-CSDN博客 <dialog id"dialog"> <h2 align"center">修改</h2> <input type"text" id"title1" placeholder"标题" value…

【论文阅读笔记】4篇Disentangled representation learning用于图像分割的论文

4篇应用解耦表示学习的文章&#xff0c;这里只关注如何解耦&#xff0c;更多细节不关注&#xff0c;简单记录一下。 1.Robust Multimodal Brain Tumor Segmentation via Feature Disentanglement and Gated Fusion Chen C, Dou Q, Jin Y, et al. Robust multimodal brain tum…

写点东西《最佳 Web 框架不存在 》

写点东西《&#x1f947;最佳 Web 框架不存在 &#x1f6ab;》 TLDR&#xff1b;您选择的 Web 应用程序框架并不重要。嗯&#xff0c;它很重要&#xff0c;但并不像其他人希望您相信的那样重要。 2024 年存在如此多的库和框架&#xff0c;而且最好的库和框架仍然备受争议&…

瑞_Java开发手册_(四)安全规约

&#x1f64a;前言&#xff1a;本文章为瑞_系列专栏之《Java开发手册》的安全规约篇。由于博主是从阿里的《Java开发手册》学习到Java的编程规约&#xff0c;所以本系列专栏主要以这本书进行讲解和拓展&#xff0c;有需要的小伙伴可以点击链接下载。本文仅供大家交流、学习及研…

Kafka 的架构

实验过程 1.三个虚拟机中解压kafka软件包 tar -zxvf kafka_2.11-1.1.1.tgz 2.修改 3 个节点配置文件 在 zookeeper 节点&#xff0c;进入 kafka_2.11-1.1.1/config 目录下&#xff0c;编辑 server.properties 文件 [rootdb1 ~]# cd kafka_2.11-1.1.1/config [rootdb1 con…

使用斐波那契(Fibonacci)数列来测试各大语言的性能

笔者使用最多的语言是C&#xff0c;目前项目中在使用Go&#xff0c;也使用过不少其它语言&#xff0c;像Erlang&#xff0c;Python&#xff0c;Lua&#xff0c;C#等等。最近看到C#夺冠&#xff0c;首次荣获 TIOBE 年度编程语言&#xff0c;同时也看到网上有不少Java与C#之争的文…

Ubuntu 22.04安装使用easyconnect

EasyConnect 百度百科&#xff0c;EasyConnect能够帮助您在办公室之外使用公司内网的所有系统及应用。在您的公司部署深信服远程应用发布解决方案后&#xff0c;您的公司所有业务系统及应用都可以轻松迁移至移动互联网上。您可以通过手机、PAD等智能移动终端随时随地开展您的业…

现代工程科技杂志现代工程科技杂志社现代工程科技编辑部2023年第21期目录

能源科技 配网故障停电原因及改进对策研究 上官安琪 110kV变电站电气自动化技术及应用策略 陈祥 变电运维误操作事故预控措施分析 高翔;韦婉 智能变电站变电运维安全与设备维护探究 温亮亮;覃万全 110kV变电站电气设计及其防雷保护案例研析 谢旭平 变电运维…

Kafka系列(四)

本文接kafka三&#xff0c;代码实践kafkaStream的应用&#xff0c;用来完成流式计算。 kafkastream 关于流式计算也就是实时处理&#xff0c;无时间概念边界的处理一些数据。想要更有性价比地和java程序进行结合&#xff0c;因此了解了kafka。但是本人阅读了kafka地官网&#…

探索 Python:发现有趣的库——第 1 章:数据可视化之旅

在一个充满活力的科技世界中&#xff0c;数据分析专家“算法仙”和编程爱好者“代码侠”相遇了&#xff0c;决定一起踏上数据可视化的探险之旅。他们将运用 Matplotlib 和 Seaborn 这两个强大的 Python 库&#xff0c;将枯燥的数据转化为生动的图形。 算法仙&#xff1a;你好&…

利用先进的条形码识别和 OCR 技术改善机场行李处理

机场每年处理数百万件行李&#xff0c;主要航空公司每家运输超过 1 亿件行李。每年有 2500 万件行李被错误处理&#xff0c;正确处理至关重要。使用最好的技术是关键&#xff0c;首先是从机场到飞机的正确转乘。 行李分拣 Dynamsoft 的客户是一家机场行李分拣解决方案提供商。…

【Linux 内核源码分析】RCU机制

RCU 基本概念 Linux内核的RCU&#xff08;Read-Copy-Update&#xff09;机制是一种用于实现高效读取和并发更新数据结构的同步机制。它在保证读操作不被阻塞的同时&#xff0c;也能够保证数据的一致性。 RCU的核心思想是通过延迟资源释放来实现无锁读取&#xff0c;并且避免了…

Go新项目-配置文件的选取及区别和写法(1)

先说结论&#xff1a;我们选型TOML yaml&#xff0c;toml&#xff0c;json&#xff0c;ini 实际业务都有用 实际栗子是&#xff1a;我们想要把Go的切片作为配置文件&#xff0c;YAML写起来比较吃力&#xff0c;TOML就很容易了。 配置文件是用于配置计算机程序的参数、初始化设…

FPGA设计时序约束十六、虚拟时钟Virtual Clock

目录 一、序言 二、Virtual Clock 2.1 设置界面 三、工程示例 3.1 工程设计 3.2 工程代码 3.3 时序报告 3.4 答疑 四、参考资料 一、序言 在时序约束中&#xff0c;存在一个特殊的时序约束&#xff0c;虚拟时钟Virtual Clock约束&#xff0c;根据名称可看出时钟不是实…

自动化测试——Python基础

文章目录 前言一、Python的基础语法1.标识符2.注释 二、Python中常见的数据类型1.Number&#xff08;数字&#xff09;1.1.int&#xff08;整数数据类型&#xff09;1.2.float&#xff08;浮点型&#xff09;1.3.bool&#xff08;布尔类型&#xff09; 2.String&#xff08;字符…