C++ Primer 第十六章 模板与泛型编程 重点解读

文章目录

  • 1 定义模板
    • 1.1 类模板成员函数的实例化:
    • 1.2 在类代码内简化模板类名的使用:
    • 1.3 令模板自己的类型参数成为友元(C++11)
    • 1.4 模板类型别名
      • 1.4.1 typedef
      • 1.4.2 为模板定义类型别名(C++11)
    • 1.5 函数模板与类模板的区别
    • 1.6 使用类的类型成员——`typename`
    • 1.7 成员模板
    • 1.8 控制实例化(C++11)
      • 1.8.1 实例化定义会实例化所有成员
    • 1.9 ⭐关于智能指针的删除器
      • 1.9.1 [在运行时绑定删除器](https://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-api-4.6/a01034_source.html) —— `shared_ptr`
      • 1.9.1 在编译时绑定删除器 ——`unique_ptr`
  • 2 模板实参推断
    • 2.1 类型转换与模板类型参数
    • 2.2 函数模板显示实参
    • 2.3 尾置返回类型(C++11)与类型转换
    • 2.4 进行类型转换的标准库模板类
    • 2.5 引用折叠和右值引用参数
    • 2.6 理解 `std:move`
    • 2.7 在调用中使用`std::forward`保持类型信息
  • 3 重载与模板
    • 3.1 如何选择重载版本
    • 3.2 处理C风格字符串指针和字符串字面常量
  • 4 可变参数模板(C++11)
    • 4.1 编写可变模板参数函数模板
    • 4.2 包扩展
    • 4.3 转发参数包 —— `emplace`
  • 5 模板特例化
    • 5.1 类模板特例化
    • 5.2 类模板部分特例化(偏特化)
      • 5.2.1 类模板的全特化和偏特化
      • 5.2.2 通过类模板的偏特化实现`std::remove_reference`
      • 5.2.3 函数模板不支持偏特化
    • 5.3 间接的对函数模板进行偏特化
      • 5.3.1 借助类模板偏特化
  • ※ 5.3.2`Concepts`(C++20)
      • 5.3.3 使用concept间接实现函数模板的偏特化

1 定义模板

1.1 类模板成员函数的实例化:

如果一个成员函数没有被使用,则它不会被实例化。成员函数只有在被用到时才进行实例化

1.2 在类代码内简化模板类名的使用:

在一个类模板的作用域内,我们可以直接使用模板名而不必指定模板实参

1.3 令模板自己的类型参数成为友元(C++11)

template<typename T>
class Test2 {
	friend T;
private:
	int t2 = 0;
};


class Test1 {
public:
	void func() {
		Test2<Test1> t;
		cout << t.t2 << endl;    //访问Test2类的私有成员
	}
private:
	int t = 1;

};

int main() {
	Test1 t;
	t.func();
	return 0;
}

1.4 模板类型别名

1.4.1 typedef

  • typedef可以为一个实例化的模板定义别名,但是不能定义一个typedef引用一个模板
typedef Blob<string> StrBlob;   //ok
typedef Blob<T> TBlob  //error

1.4.2 为模板定义类型别名(C++11)

  • template<typename T> using twin = pair<T, T>;
    twin<string> authors;  //authors 的类型为pair<string, string>
    
  • template<typename T> using twin = pair<T, int>;
    twin<string> authors;    //authors 的类型为pair<string, int>
    
    

1.5 函数模板与类模板的区别

  • 默认参数:函数模板不允许由默认参数(C++11之后支持了函数模板的默认实参);类模板在模板参数列表中可以有默认参数
  • 特化:函数模板只能全特化;类模板可以全特化、偏特化
  • 调用方式:函数模板可以进行类型推导、隐式调用、显示调用;类模板只能显示调用
template<typename T> constexpr
auto func(const T& x, const T& y) -> decltype(x + y) {
	cout << typeid(T).name() << ": ";
	return x + y;
}

int main() {
	int x1 = 10, y1 = 20;
	cout << func<int>(x1, y1) << endl;    //显示

	double x2 = 2.2, y2 = 3.3;
	cout << func(x2, y2) << endl;	//隐式
}

1.6 使用类的类型成员——typename

string::size_type* t;

上述代码由于编译器有string的定义,从而知道size_type是一个类型

  • 在普通(非模板)代码中,编译器掌握类的定义,因此,他知道通过作用域访问的名字是类型还是static成员。
T::size_type * p;    
//1.p是一个size_type类型的指针
//2.表达式为size_type这个静态成员与p相乘

如果T是一个模板类型参数,编译器无法知道size_type是一个类型还是一个static成员,

  • 默认情况下,C++语言假定通过作用域运算符(::)访问的名字不是类型。因此,我们需要手动声明使用一个模板类型参数的类型成员——typename
template <typename T>
typename T::value_type top(const T& c) {
    if (!c.empty()) return c.back();
    else return typename T::value_type();    //当容器为空,返回容器的value_type的默认值
}

1.7 成员模板

  • 一个类可以包含本身是模板的成员函数。这种成员被称为成员模板

问:为什么成员模板不能是虚函数?

答:C++中的成员模板函数不能被声明为虚函数。这是因为模板函数只有在使用时才会被实例化而虚函数表的大小在编译期间就需要确定。如果允许成员模板函数为虚函数,那么编译器就必须知道我们一共为该成员模板函数实例化了多少个不同的版本,以便确定类的虚函数表的大小。这是不可能的,因为模板函数可以被实例化为任意数量的版本,这取决于模板参数的类型和数量。

  • 实例:定义unique_ptr的删除器

1.8 控制实例化(C++11)

在大系统中,在多个文件中实例化相同模板的额外开销可能非常严。在新标准中,我们可以通过显示实例化(explicit instantiation)来避免这种开销

//显式实例化的形式:
template [declaration];  	//实例化定义

extern template [declaration];   //实例化声明
//Application.cc

//这些模板类型必须在程序其他位置进行实例化
extern template class Blob<string>;
extern template int compare(const int&, const int&);
Blob<string> sa1, sa2;  	//实例化会出现在其他位置

//接收initializer_list的构造和拷贝构造函数在本文件中实例化
Blob<int> a1 = {0,1,2,3}		
Blob<int> a2(a1);

int i = compare(a1[0], a2[0]); 		//实例化出现在其他位置
//templateBuild.cc
//实例化文件必须为每个在其他文件中声明extern的类型和函数提供一个(非extern)的定义
template int compare(const int&, const int&);
template class Blob<string>;    //实例化模板的所有成员

1.8.1 实例化定义会实例化所有成员

一个类模板的实例化定义会实例化该模板的所有成员,包括内联的成员函数。因为当编译器遇到一个实例化定义时,他不了解程序使用哪些成员函数

1.9 ⭐关于智能指针的删除器

shared_ptr:

在这里插入图片描述

unique_ptr:

在这里插入图片描述

通过观察shared_ptrunique_ptr的模板可以发现,shared_ptr的删除器只需要在创建或reset指针时传递给它一个可调用对象即可;而删除器类型是一个unique_ptr对象的类型的一部分,用户必须在定义unique_ptr时以显示模板实参的形式提供删除器类型

1.9.1 在运行时绑定删除器 —— shared_ptr

  • shared_ptr必须能直接访问其删除器,删除器必须保存为一个指针或一个封装了指针的类(如function)
int main() {
	int* p = new int(1);
	shared_ptr<int> sp(p, [](int* p) { delete(p); });
	int* p2 = new int(2);
	sp.reset(p2, [](int* p) { 
		delete(p); 
		cout << "haha" << endl; 
		});
    //程序最后打印haha 
}

我们可以确定shared_ptr不是将删除器直接保存为一个成员,因为删除器的类型知道运行时才会确定

1.9.1 在编译时绑定删除器 ——unique_ptr

unqiue_ptr类中,删除器的类型是类类型的一部分,即unique_ptr有两个模板参数.

由于删除器的类型是unique_ptr类型的一部分,因此删除器成员的类型在编译时就确定了。从而删除器可以直接保存在unique_ptr对象中

通过在编译时绑定删除器,unique_ptr避免了间接调用删除器的运行时开销。

通过在运行时绑定删除器,shared_ptr使用户重载删除器更为方便

2 模板实参推断

2.1 类型转换与模板类型参数

template <typename T> T fobj(T t1, T t2) { return T(); }
template <typename T> T fref(const T& t1, const T& t2) { return T(); }
int main() {
	int x1 = 1;
	const int x2 = 2;
	fobj(x1, x2);   //调用fobj(int, int)
	fref(x1, x2);	//调用fref(const int&, const int&)

	int a[10], b[10];
	fobj(a, b);		//调用f(int*, int*)
	fref(a, b);		//error:没有与参数列表匹配的函数模板
}

不能用于函数模板的类型转换:

  1. 算数转换
  2. 派生类向基类的转换
  3. 用户定义的转换

2.2 函数模板显示实参

template<typename T1, typename T2, typename T3>
T1 sum(T2 x, T3 y) {
	return x + y;
}

int main() {
	cout << sum<int>(1.1, 1) << endl;    //2
	cout << sum<double>(1.1, 1) << endl;     //2.1
}

2.3 尾置返回类型(C++11)与类型转换

template <typename It>
auto fcn(It beg, It en) -> decltype(*beg) {
	return *beg;
}

解引用返回一个序列的左值,因此通过decltype推断的类型为beg表示的元素的类型的引用

2.4 进行类型转换的标准库模板类

  • 其中typeremove_reference模板类的一个类型成员
template<typename It>
//为了使用模板参数的成员,type是一个类的成员,而该类依赖于一个模板参数,因此我们必须使用typename
//type的类型为非引用的
auto func2(It beg, It end) -> typename remove_reference<decltype(*beg)>::type {
	reutrn *beg;
}

在这里插入图片描述

其他标准类型转换模板在书606页

习题:

template<typename It>
//为了使用模板参数的成员,必须用typename
auto func2(It beg, It end) ->  decltype(*beg + 0) {		//合法:左值引用 + 右值 -> 左值
	return *beg;    //返回序列中一个元素的拷贝
}

int main() {
	list<int> lt{ 1,2 };
	decltype(func2(lt.begin(), lt.end())) t = 10;
}

2.5 引用折叠和右值引用参数

template <typename T> void func(T&&);

int main() {
	int i = 0;
	func(i);    //看起来实参的类型为int& && 
}
  • 我们不能直接定义一个引用的引用, 但是可以通过类型别名模板类型参数间接定义。
  • 新标准中(C++11), 折叠规则拓展到右值引用
    • X& &X& &&X&& & --折叠–> X&
    • X&& && --折叠–> X&&
typedef int& ref_type;
typedef ref_type&& refs_type;
int main() {
	int x = 10;
	refs_type y = x;   //y是一个左值引用:int& && -> 引用折叠 -> int&
}

2.6 理解 std:move

//std::move是如何定义的:
template <typename T>
typename remove_reference<T>::type&& move(T&& t) {
	return static_cast<typename remove_reference<T>::type&&>(t);
}

//考虑以下两种情况  P611
int main() {
	string s1("lvalue s1"), s2;
    s = move(string("rvalue s"));	//右值移动
    s = move(s1);	//左值移动,移动之后,s1的值是不确定的
}

2.7 在调用中使用std::forward保持类型信息

3 重载与模板

3.1 如何选择重载版本

  • 当有多个重载模板对一个调用提供同样好的匹配时,因该选择最特例化的版本
template <typename T> string debug_rep(const T& t) {}

template <typename T> string debug_rep(T* t) {}

int main() {
	string s("ss");
	const string* sp = &s;
	debug_rep(sp);    //调用第二个,T被绑定到const string
}
  • 如果一个非模板函数和一个模板函数提供同样好的匹配,则选择非模板版本

3.2 处理C风格字符串指针和字符串字面常量

4 可变参数模板(C++11)

4.1 编写可变模板参数函数模板

 template <typename T>
 ostream& print(ostream& os, const T& t) {
	 return os << t;
 }

 template <typename T, typename... Args>
 ostream& print(ostream& os, const T& t, const Args&... args) {
	 os << t << ' ';
	 return print(os, args...);    //包拓展
 }

4.2 包扩展

  1. 函数参数包名 + ...
print(os, args...); 
  1. function(函数参数包名) + ...
print(os, debug_rep(args...));    //扩展中的模式会独立地应用于包中的元素

4.3 转发参数包 —— emplace

template <class... Args>
inline
void StrVec::emplace_back(Args&&... args) {
	chk_n_alloc();
	alloc.construct(first_free++, std::forward<Args>(args)...);
}

5 模板特例化

5.1 类模板特例化

namespace std {
    template<>
    struct hash<Sales_data> {
        //用来散列一个无需容器的类型必须要定义下列类型
        typedef size_t result_type;
        typedef Sales_data argument_type;    //默认情况下,此类型需要operator==
        size_t operator()(const Sales_data& s) const {
            return hash<string>() (s.bookNo) ^      //构造匿名对象调用operator()返回一个哈希值
                   hash<unsigned>() (s.units_sold) ^ 
                   hash<double>() (s.revenue);
        }
    };
}  //关闭std命名空间

5.2 类模板部分特例化(偏特化)

5.2.1 类模板的全特化和偏特化

我们只能部分特例化类模板,而不能部分特例化函数模板 —— P628

template <typename T>
class add {
public:
	T operator()(T a, T b) {
		return a + b;
	}
};

template <typename T>  //类模板的全特化
class add<T*> { 
public:
	T operator()(T* a, T* b) {
		cout << "pointer" << endl;
		return *a + *b;
	}
};

template<>
class add<int*> {   //类模板的偏特化
public:
	int operator()(int* a, int* b) {
		cout << "int pointer" << endl;
		return *a + *b;
	}
};

5.2.2 通过类模板的偏特化实现std::remove_reference

代码更具模板的匹配规则进行精确的匹配,当传入的是一个右值引用时,匹配第三个模板,此时T的类型为去掉右值引用性质之后的类型

template<class T> struct remove_reference {
	typedef T type;
};
template<class T> struct remove_reference<T&> {
	typedef T type;
};
template<class T> struct remove_reference<T&&> {
	typedef T type;
};

5.2.3 函数模板不支持偏特化

问:为什么函数模板不能支持偏特化,如果可以,发产生怎样的歧义?

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

template <typename T*>   //函数模板的偏特化,错误,产生歧义
T add(T* a, T* b) {
return *a + *b;
}

template<>
int add(int a, int b) {   //函数模板的全特化
	return a + b;
}

/*
template<>
class add<int*> {   //类模板的偏特化
public:
	int operator()(int* a, int* b) {
		cout << "int pointer" << endl;
		return *a + *b;
	}
};
*/

在这个例子中,如果我们尝试调用add(a, b),其中a和b都是指针类型,编译器将无法确定应该调用哪个函数。因为第一个函数模板可以通过类型推导将T推导为指针类型,而第二个函数模板是针对指针类型的偏特化版本。这就产生了歧义,编译器无法确定应该调用哪个函数。

5.3 间接的对函数模板进行偏特化

C++标准并不支持函数模板偏特化。然而在实际开发中,我们确实需要对一些函数模板进行偏特化。这是因为偏特化和函数重载容易出现歧义。

  1. 借助类模板偏特化:由于类可以进行偏特化处理,因此一种非常直观的方案就是使用Functor代替函数,并实现 operator ()。详细例子看5.2.1

  2. 使用Concepts:C++20提供了 Concepts 特性, Concepts 特性提出的动机是为了解决模板元编程过程中,编译器给出的报错信息冗余及编译器不能很好的给出准确的出错信息的问题。

5.3.1 借助类模板偏特化

template <typename T>
class add {
public:
	T operator()(T a, T b) {
		return a + b;
	}
};
template <typename T>  //全特化
class add<T*> {
public:
	T operator()(T* a, T* b) {
		cout << "pointer" << endl;
		return *a + *b;
	}
};
template<>
class add<int*> {   //偏特化
public:
	int operator()(int* a, int* b) {
		cout << "int pointer" << endl;
		return *a + *b;
	}
};

int main() {
	cout << add<int>()(10, 20) << endl;
	int x = 10, y = 20;
	cout << add<int*>()(&x, &y) << endl;
}

※ 5.3.2Concepts(C++20)

Concepts的提出就是为了解决上述问题,它通过将模板的类型约束抽象出来,然后在模板定义时再使用。这样成功解耦了模板类型约束和模板本身的一些类型逻辑

  • concept的定义形式
template < template-parameter-list > concept concept-name = constraint-expression;

其中,constraint-expression 是一个可以被evalbool的表达式或者编译期函数。在使用定义好的concept时,constraint-expression 会根据上面 template-parameter-list 传入的类型,执行编译期计算,判断使用该concept的模板定义是否满足。如果不满足,则编译期会给定一个具有明确语义的错误

template <typename T>
concept always_satisfied = true;

 /*		
 	将类型T抽象成integral类型,当模板实参使constraint-expression为true时,
 	才匹配template<inntegral T>的函数模板
 */
template <typename T>
concept integral = std::is_integral_v<T>;   

template <typename T>
concept signed_integral = integral<T> && std::is_signed_v<T>;

template <always_satisfied T>
T mul(T a, T b) {
    puts("always_satisfied");
    return a * b;
}

template <integral T>
T add(T a, T b) {
    puts("integral");
    return a + b;
}

template <signed_integral T>
T subtract(T a, T b) {
    puts("signed_integral");
    return a - b;
}

int main() {
    mul(1, 2);
    mul(1.0f, 2.0f);
    add(1, 2);
    subtract(1, 2);
}

5.3.3 使用concept间接实现函数模板的偏特化

  • 我们以5.2.2的代码为基准,使用concept编写函数模板的偏特化
//T是一个整数类型
template <typename T>
concept integral = std::is_integral_v<T>;

//T为一个整数类型的指针,特别注意,使用remove_pointer去除模板实参的指针性质
template <typename T>
concept integral_pointer = integral<typename std::remove_pointer<T>::type> && std::is_pointer_v<T>;

template <integral T>
void add(T a, T b) {
    cout << "integral: " << a + b << endl;
}

template <integral_pointer T>
void add(T a, T b) {

    cout << "integral_pointer: " << *a + *b << endl;
}

int main() {
    int x = 10, y = 20;
    add(&x, &y);
    add(10, 20);
     /*
    	output:
    	integral_pointer: 30
		integral: 30
    */
    
    float a = 1.1, b = 2.2;
    add(a, b);    //error:没有于参数列表匹配的重载函数,add实例参数类型为(float, float)
    add(&a, &b);    //error:没有于参数列表匹配的重载函数,add实例参数类型为(float*, float*)
}

template
concept integral_pointer = integral<typename std::remove_pointer::type> && std::is_pointer_v;

template
void add(T a, T b) {
cout << "integral: " << a + b << endl;
}

template <integral_pointer T>
void add(T a, T b) {

cout << "integral_pointer: " << *a + *b << endl;

}

int main() {
int x = 10, y = 20;
add(&x, &y);
add(10, 20);
/*
output:
integral_pointer: 30
integral: 30
*/

float a = 1.1, b = 2.2;
add(a, b);    //error:没有于参数列表匹配的重载函数,add实例参数类型为(float, float)
add(&a, &b);    //error:没有于参数列表匹配的重载函数,add实例参数类型为(float*, float*)

}


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

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

相关文章

SpringMVC多种类型数据响应

SpringMVC多种类型数据响应入门 1.概念 RequestMapping 作用&#xff1a;用于建立请求URL和处理请求方法之间的对应关系 位置&#xff1a; 类上&#xff0c;请求URL的第一级访问目录。此处不写的话&#xff0c;就相当于应用的根目录 方法上&#xff0c;请求URL的第二级访问目…

宠物店管理系统服务预约会员小程序效果如何

很多人都会养宠物&#xff0c;随着生活品质提升&#xff0c;尤其以年轻人为主的消费者在宠物食品、医疗、购买消费等方面有较高的消费属性&#xff0c;而在线下也有大量从业者&#xff0c;互联网程度及智慧化门店提升及客户赋能&#xff0c;线下经营的同时还需要线上发展拓客引…

鸿蒙4.0开发笔记之ArkTS装饰器语法基础@Builder组件内自定义构建函数与@Styles自定义组件重用样式(十)

文章目录 一、Builder自定义构建函数1、什么是自定义构建函数2、组件内定义构建函数3、组件外定义构建函数4、Builder装饰器练习 二、Styles重用样式函数1、重用样式的作用2、组件内定义Styles3、组件外定义4、Styles装饰器练习5、注意要点 一、Builder自定义构建函数 1、什么…

寿险公司通过开源治理保障数字创新,安全打通高质量服务新通道

某寿险公司致力于为消费者提供人性化的产品和服务&#xff0c;在中国保险市场中始终保持前列。该寿险公司以挖掘和满足客户需求为出发点&#xff0c;从产品开发、渠道销售、运营流程和售后服务等各环节&#xff0c;借助数字化工具&#xff0c;不断地努力探索并提升服务品质。 精…

NumSharp

github地址&#xff1a;https://github.com/SciSharp/NumSharp High Performance Computation for N-D Tensors in .NET, similar API to NumPy. NumSharp (NS) is a NumPy port to C# targetting .NET Standard. NumSharp is the fundamental package needed for scientific …

Linux Centos系统安装Mysql8.0详解

本文是基于服务器Linux Centos 8.0系统 安装 Mysql8.0真实运维工作实战为例&#xff0c;详细讲解安装的全过程。 1&#xff0c;检查卸载mariadb Mariadb数据库是mysql的分支&#xff0c;mariadb和mysql会有冲突&#xff0c;所以安装Mysql前&#xff0c;首先要检查是否安装了m…

6.如何利用LIO-SAM生成可用于机器人/无人机导航的二维/三维栅格地图--以octomap为例

目录 1 octomap的安装 2 二维导航节点的建立及栅格地图的构建 3 三维栅格地图的建立 1 octomap的安装 这里采用命令安装&#xff1a; sudo apt install ros-melodic-octomap-msgs ros-melodic-octomap-ros ros-melodic-octomap-rviz-plugins ros-melodic-octomap-server 这样…

HPE存储监控

存储设备构成了企业组织的支柱&#xff0c;使应用程序能够运行&#xff0c;可以进行市场研究&#xff0c;并且能够存储关键业务信息以用于日常运营&#xff0c;由于健康的存储网络对于日常业务运营的顺利运行是必不可少的&#xff0c;因此 IT 团队应确保存储基础架构的理想性能…

毫米波传感器系统性能测量(TI文档)

摘要 本应用报告讨论了使用德州仪器高性能毫米波传感器的系统性能测量结果。TI的毫米波传感器是一个77 GHz&#xff0c;高度集成的收发器&#xff0c;具有高速接口(CSI2)&#xff0c;可将原始ADC数据发送出去进行处理。毫米波传感器包括整个毫米波射频和模拟基带信号链&#xf…

ubuntu18.04安装miniconda和mysql

MySQL 1.更新软件包 apt-get update 2.mysql安装 apt-get install mysql-server 3.初始化配置mysql mysql_secure_installation 第一个选项是问你要不要安装密码插件&#xff0c;就是说安装了之后你必须用安全度很高的密码&#xff0c;不安装的话&#xff0c;可以随意设…

HAProxy部署Web集群(Nginx)实验

实验前准备 HAProxy服务器&#xff1a;192.168.188.11 内核版本最好要在2.6以上&#xff0c;使用uname -r查看自己的内核版本是否适用 Nginx服务器1&#xff1a;192.168.188.12 Nginx服务器2&#xff1a;192.168.188.13 客户端&#xff1a;192.168.188.1&#xff08;本机window…

渗透测试|HW蓝队

记录某个对某个钓鱼事件中获取的钓鱼样本进行分析&#xff0c;以及简单的制作学习 样本行为分析 首先看到是 qq 邮箱发来的某个压缩包大概本身是带密码的&#xff0c;反手就丢到虚拟机先看下大概文件&#xff0c;解压后是这样的一个快捷方式 然后打开属性查看快捷方式&#x…

基于STC12C5A60S2系列1T 8051单片机的IIC总线器件24C02实现掉电保存计时时间应用

基于STC12C5A60S2系列1T 8051单片机的IIC总线器件24C02实现掉电保存计时时间应用 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式及配置STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式介绍液晶显示器LCD1602简单介绍IIC通信简单…

RocketMQ-快速实战

MQ简介 MQ&#xff1a;MessageQueue&#xff0c;消息队列。是在互联网中使用非常广泛的一系列服务中间件。 Message&#xff1a;消息。消息是在不同进程之间传递的数据。这些进程可以部署在同一台机器上&#xff0c;也可以分布在不同机器上。&#xff08;数据形式&#xff1a…

2023工作中遇到问题一

1、vue中模拟鼠标点击下拉框 <xxx-select ref"aaa" :value"form.serviceCharge" placeholder"请输入" add"(value) > getBasGoods(value)" :configInfo"configInfo" />vue中模拟鼠标点击 this.$refs.aaa.$el.cli…

经典的回溯算法题leetcode全排列问题思路代码详解

目录 全排列问题 leetcode46题.全排列 leetcode47题.全排列II 对回溯算法感兴趣的朋友也可以多多支持一下我的其他文章。 回溯算法详解-CSDN博客 经典的回溯算法题leetcode组合问题整理及思路代码详解-CSDN博客 经典的回溯算法题leetcode子集问题思路代码详解-CSDN博客 …

AI文章生成器-免费批量原创文章生成的工具

在科技的大潮中&#xff0c;AI技术愈发成熟&#xff0c;文言一心文章生成器悄然崭露头角。这一创新性工具的出现&#xff0c;为广大用户提供了快速、高效的文章生成方式。147SEO的批量原创功能更是锦上添花&#xff0c;让文章创作变得更为轻松。正是在这背后&#xff0c;我们看…

iPhone苹果手机如何将词令网页添加到苹果iPhone手机桌面快捷打开?

iPhone苹果手机如何将词令网页添加到苹果iPhone手机桌面快捷打开&#xff1f; 1、在iPhone苹果手机上找到「Safari浏览器」,并点击打开&#xff1b; 2、打开Safari浏览器后&#xff0c;输入词令官方网站地址&#xff1a;ciling.cn ; 3、打开词令官网后&#xff0c;点击Safari…

数字图像处理(实践篇)十二 基于小波变换的图像降噪

目录 一 基于小波变换的图像降噪 &#xff08;1&#xff09;小波变换基本理论 &#xff08;2&#xff09;小波分析在图像处理中的应用 &#xff08;3&#xff09;小波变换原理 &#xff08;4&#xff09;小波降噪原理 &#xff08;5&#xff09;小波降噪算法的实现 &…

Windows系统IIS服务配置与网站搭建,结合内网穿透实现公网访问

文章目录 1.前言2.Windows网页设置2.1 Windows IIS功能设置2.2 IIS网页访问测试 3. Cpolar内网穿透3.1 下载安装Cpolar内网穿透3.2 Cpolar云端设置3.3 Cpolar本地设置 4.公网访问测试5.结语 1.前言 在网上各种教程和介绍中&#xff0c;搭建网页都会借助各种软件的帮助&#xf…