从C语言到C++_21(模板进阶+array)+相关笔试题

news2024/11/17 7:17:17

目录

1. 非类型模板参数

1.1 array

1.2 非类型模板参数的使用场景

1.3 注意事项

2. 模板的特化

2.1 函数模板的特化

2.2 类模板的特化

2.3 全特化和偏特化(半特化)

3. 模板关于分离编译

4. 模板优缺点

5. 模板相关笔试题

本章完。


1. 非类型模板参数

对于函数模板和类模板,模板参数并不局限于类型,普通值也可以作为模板参数。

STL 的 array 就有一个非类型模板参数。

T 是类型,而 N 这里并不是类型,而是一个常量。

类型模板参数定义的是虚拟类型,注重的是你要传什么,而非类型模板参数定义的是常量。

1.1 array

array是一个固定大小的顺序容器(静态数组),是 C++11 新增的,它有什么独特的地方吗?

很可惜,基本没有,并且 vector 可以完全碾压 array,这就是为什么只在这里简单地讲讲 array。

看段代码:

#include <iostream>
#include <array>
#include <vector>
using namespace std;

int main()
{
    vector<int> v(100, 0);
    array<int, 100> arr;

    cout << "v : " << sizeof(v) << endl;
    //这里sizeof算的是成员变量的大小,VS2022下vector应该有四个成员变量,32位平台每个指针是4个字节,因此16字节
    cout << "arr : " << sizeof(arr) << endl;

    return 0;
}

vector 是开在空间大的堆上的而 array 是开在栈上的,堆可比栈的空间大太多太多了。

array 能做的操作几乎 vector 都能做,因为 vector 的存在 array 显得有些一无是处。

所以我们拿 array 去对标 vector 是不对的,拿去和原生数组比还是可以对比的。

但是 array 也只是封装过的原生数组罢了,就是有了接口函数,

比起原生数组,array 的最大优势也只是有一个越界的检查,读和写都可以检查到是否越界。

原生数组的读检查不到,写只能检查到后面几个数,

#include <iostream>
#include <array>
#include <vector>
using namespace std;

int main()
{
    int a[10];
    array<int, 10> arr; // array也不会初始化

    int x = a[15]; // 没报错
    a[10] = 2; // 报错
    a[11] = 2; // 没报错

    int y = arr[15]; // 报错
    arr[10] = 2; // 报错
    arr[11] = 2; // 报错

    return 0;
}

在 C++11 增加完 array 后备受吐槽,从简化的角度来说完全可以不增加 array。

并且现在大多数人都习惯了用原生数组,基本没人用array。

1.2 非类型模板参数的使用场景

假设我们要定义一个静态栈: 

#define N 100

template<class T>
class Stack
{
private:
    int _arr[N];
    int _top;
};

如果定义两个容量不一样的栈,一个容量是100 另一个是 500,能做到吗?

这就像 typedef 做不到一个存 int 一个存 double,而使用模板可以做到 st1 存 int,st2 存 double。

这里你的 #define 无论是改 100 还是改 500 都没办法解决这里的问题,

对应的,这里使用非类型模板参数就可以做到 s1 存 100,s2 存 500。

#include <iostream>
using namespace std;

template<class T, size_t N>
class Stack
{
private:
    int _arr[N];
    int _top;
};

int main()
{
    Stack<int, 100> st1;  // 大小是100
    Stack<int, 500> st2;  // 大小是500

    return 0;
}

在模板这定义一个常量 N,派遣它去做数组的大小。

于是我们就可以在实例化 Stack 的时候指定其实例化对象的大小了,分别传 100 和 500。

1.3 注意事项

注意事项 ①:非类型模板参数是是常量,是不能修改的。

#include <iostream>
using namespace std;

template<class T, size_t N>
class Stack 
{
public:
    void modify()
    {
        N = 10; // 错误	C2106	“ = ”: 左操作数必须为左值	
    }
private:
    int _arr[N];
    int _top;
};

int main()
{
    Stack<int, 100> st1;
    st1.modify();

    return 0;
}

注意事项 ②:有些类型是不能作为非类型模板参数的,比如浮点数、类对象以及字符串。

非类型模板参数基本上都是整型家族,char也是整形家族,也只有整型家族是有意义和价值的。

#include <iostream>
using namespace std;

template<class T, double N> // 错误	C2058	常量表达式不是整型
class Stack 
{

private:
    int _arr[N];
    int _top;
};

int main()
{
    Stack<int, 100> st1;

    return 0;
}

注意事项 ③:非类型的模板参数必须在编译期就能确认结果。

即非类型模板参数的实参只能是常量。

#include <iostream>
using namespace std;

template<class T, size_t N> // 错误	C2058	常量表达式不是整型
class Stack 
{

private:
    int _arr[N];
    int _top;
};

int main()
{
    size_t N;
    cin >> N;

    Stack<int, N> st1; // 错误	C2971	“Stack” : 模板参数“N”:“N”: 包含非静态存储持续时间的变量不能用作非类型参数

    return 0;
}

2. 模板的特化

通常情况下,使用模板可以实现一些与类型无关的代码

但是,对于一些特殊类型,可能我们就要对其进行一些 "特殊化的处理" 。

举例:如果不对特殊类型进行特殊处理就可能会出现一些问题,比如:

#include <iostream>
using namespace std;

class Date // 简化的日期类
{
public:
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
	{}

	bool operator<(const Date& d) const
	{
		if ((_year < d._year)
			|| (_year == d._year && _month < d._month)
			|| (_year == d._year && _month == d._month && _day < d._day))
		{
			return true;
		}
		else
		{
			return false;
		}
	}
private:
	int _year;
	int _month;
	int _day;
};

template<class T> // 函数模板 -- 参数匹配
bool Less(T left, T right)
{
	return left < right;
}

int main()
{
	cout << Less(1, 2) << endl;   // 可以比较,结果正确

	Date d1(2023, 1, 1);
	Date d2(2023, 1, 2);
	cout << Less(d1, d2) << endl;  // 可以比较,结果正确

	Date* p2 = &d2;
	Date* p1 = &d1;
	cout << Less(p1, p2) << endl;  // 可以比较,结果错误

	return 0;
}

这里我们想比较的是指针指向的内容,而不是指针本身,怎么解决?

2.1 函数模板的特化

首先,必须要先有一个基础的函数模板。

其次,关键字 template 后面接上一对空的 <> 尖括号。

然后,函数名后跟一对尖括号,尖括号中指定需要特化的内容。

最后,函数形参表必须要和模板函数的基础参数类型完全相同。

template<class T> // 函数模板 -- 参数匹配
bool Less(T left, T right)
{
	return left < right;
}

template<> // 针对某些类型要特殊化处理 ———— 使用模板的特化解决
bool Less<Date*>(Date* left, Date* right) {
	return *left < *right;
}

代码演示:

#include <iostream>
using namespace std;

class Date // 简化的日期类
{
public:
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
	{}

	bool operator<(const Date& d) const
	{
		if ((_year < d._year)
			|| (_year == d._year && _month < d._month)
			|| (_year == d._year && _month == d._month && _day < d._day))
		{
			return true;
		}
		else
		{
			return false;
		}
	}
private:
	int _year;
	int _month;
	int _day;
};

template<class T> // 函数模板 -- 参数匹配
bool Less(T left, T right)
{
	return left < right;
}

template<> // 针对某些类型要特殊化处理 ———— 使用模板的特化解决
bool Less<Date*>(Date* left, Date* right) 
{
	return *left < *right;
}

int main()
{
	cout << Less(1, 2) << endl;   // 可以比较,结果正确

	Date d1(2023, 1, 1);
	Date d2(2023, 1, 2);
	cout << Less(d1, d2) << endl;  // 可以比较,结果正确

	Date* p2 = &d2;
	Date* p1 = &d1;
	cout << Less(p1, p2) << endl;  // 可以比较,结果正确

	return 0;
}

解读:对于普通类型,它还是会调正常的模板。对于 Date* 编译器就会发现这里有个

专门为 Date* 而准备的特化版本,编译器会优先选择该特化版本。这就是函数模板的特化。

思考:现在我们加一个普通Less函数的函数重载,Date* 会走哪个版本?


bool Less(Date* left, Date* right) 
{
    return *left < *right;
}

答案:函数重载,会走直接匹配的普通函数版本,因为是现成的,不用实例化。

你可以这么理解:原模板是生肉,模板特化是半生不熟的肉,直接匹配的普通函数是熟肉。

所以:函数模板不一定非要特化,因为在参数里面就可以处理,

写一个匹配参数的普通函数也更容易理解。

2.2 类模板的特化

刚才函数模板不一定非要特化,因为可以写一个具体实现的函数。

但是类模板我们没法实现一个具体的实际类型,就必须要特化了。

我们前面实现的仿函数(类模板)也有这样的问题:

#include <iostream>
using namespace std;

class Date // 简化的日期类
{
public:
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
	{}

	bool operator<(const Date& d) const
	{
		if ((_year < d._year)
			|| (_year == d._year && _month < d._month)
			|| (_year == d._year && _month == d._month && _day < d._day))
		{
			return true;
		}
		else
		{
			return false;
		}
	}
private:
	int _year;
	int _month;
	int _day;
};

template<class T> // 函数模板 -- 参数匹配
bool Less(T left, T right)
{
	return left < right;
}

template<> // 针对某些类型要特殊化处理 ———— 使用模板的特化解决
bool Less<Date*>(Date* left, Date* right) {
	return *left < *right;
}

类模板
template<class T>
struct Less2
{
	bool operator()(const T& x1, const T& x2) const
	{
		return x1 < x2;
	}
};

int main()
{
	Less2<Date> LessFunc1;
	Date d1(2023, 1, 1);
	Date d2(2023, 1, 2);
	cout << LessFunc1(d1, d2) << endl;  // 可以比较,结果正确

	Less2<Date*> LessFunc2;
	Date* p2 = &d2;
	Date* p1 = &d1;
	cout << LessFunc2(p1, p2) << endl;  // 可以比较,结果错误

	return 0;
}

加上类模板的特化:

#include <iostream>
using namespace std;

class Date // 简化的日期类
{
public:
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
	{}

	bool operator<(const Date& d) const
	{
		if ((_year < d._year)
			|| (_year == d._year && _month < d._month)
			|| (_year == d._year && _month == d._month && _day < d._day))
		{
			return true;
		}
		else
		{
			return false;
		}
	}
private:
	int _year;
	int _month;
	int _day;
};

template<class T> // 函数模板 -- 参数匹配
bool Less(T left, T right)
{
	return left < right;
}
template<> // 针对某些类型要特殊化处理 ———— 使用模板的特化解决
bool Less<Date*>(Date* left, Date* right) 
{
	return *left < *right;
}

类模板
template<class T>
struct Less2
{
	bool operator()(const T& x1, const T& x2) const
	{
		return x1 < x2;
	}
};
template<>// 类模板特化
struct Less2<Date*>
{
	bool operator()(const Date* x1, const Date* x2) const
	{
		return *x1 < *x2;
	}
};

int main()
{
	Less2<Date> LessFunc1;
	Date d1(2023, 1, 1);
	Date d2(2023, 1, 2);
	cout << LessFunc1(d1, d2) << endl;  // 可以比较,结果正确

	Less2<Date*> LessFunc2;
	Date* p2 = &d2;
	Date* p1 = &d1;
	cout << LessFunc2(p1, p2) << endl;  // 可以比较,结果错误,加上类模板特化后结果正确

	return 0;
}

2.3 全特化和偏特化(半特化)

全特化和偏特化的概念和缺省值很像,前面我们写的都叫作模板的全特化。

全特化:全特化即是将模板参数列表中所有的参数都确定化。

偏特化(又称半特化):将部分参数类表中的一部分参数特化。

(半特化并不是特化一半,就像半缺省并不是缺省一半一样)

偏特化有以下两种表现方式:

① 部分特化:将模板参数类表中的一部分参数特化。

// 将第二个参数特化为int
template <class T1>
class Data<T1, int>
{
public:
	Data() { cout << "Data<T1, int>" << endl; }
private:
	T1 _d1;
	int _d2;
};

② 参数更进一步的限制:偏特化并不仅仅是指特化部分参数,

而是针对模板参数更进一步的条件限制所设计出来的一个特化版本。

//两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public:
	Data() { cout << "Data<T1*, T2*>" << endl; }

private:
	T1 _d1;
	T2 _d2;
};
//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:
	Data(const T1& d1, const T2& d2)
		: _d1(d1)
		, _d2(d2)
	{
		cout << "Data<T1&, T2&>" << endl;
	}

private:
	const T1& _d1;
	const T2& _d2;
};
void test2()
{
	Data<double, int> d1; // 调用特化的int版本
	Data<int, double> d2; // 调用基础的模板 
	Data<int*, int*> d3; // 调用特化的指针版本
	Data<int&, int&> d4(1, 2); // 调用特化的指针版本
}

放一段代码体会偏特化的花哨玩法:

#include <iostream>
using namespace std;

template<class T1, class T2>
class Data
{
public:
	Data() { cout << "Data<T1, T2>" << endl; }
private:
	T1 _d1;
	T2 _d2;
};

// 全特化
template<>
class Data<int, char>
{
public:
	Data() { cout << "Data<int, char>" << endl; }
private:
	/*int _d1;
	char _d2;*/
};

// 偏特化
template <class T1>
class Data<T1, int>
{
public:
	Data() { cout << "Data<T1, int>" << endl; }
private:
	/*T1 _d1;
	int _d2;*/
};

template<class T1, class T2>
class Data<T1*,T2*>
{
public:
	Data() { cout << "Data<T1*, T2*>" << endl; }
};

template<class T1, class T2>
class Data<T1&, T2&>
{
public:
	Data() { cout << "Data<T1&, T2&>" << endl; }
};

template<class T1, class T2>
class Data<T1&, T2*>
{
public:
	Data() { cout << "Data<T1&, T2*>" << endl; }
};

int main()
{
	Data<int, int> d0;
	Data<double, int> d1;

	Data<int, char> d2;

	Data<double, double> d3;
	Data<double*, double*> d4;
	Data<int*, char*> d5;
	Data<int*, char> d6;

	Data<int&, char&> d7;
	Data<int&, double&> d8;
	Data<int&, double*> d9;

	return 0;
}

 这就对应说过的类型匹配原则,有更匹配的就去调用它,没有就逐层递减去匹配。

3. 模板关于分离编译

什么是分离编译?
一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,
最后将所有目标文件链 接起来形成单一的可执行文件的过程称为分离编译模式。
先说结论: 模板是不支持分离编译的。
假如有以下场景,模板的声明与定义分离开,在头文件中进行声明,源文件中完成定义:

在这里插入图片描述

 如上图所示,在template.cpp源文件中定义了Sub函数,并在template.h头文件中进行了声明。
但是在编译过程中,编译器是对各个源文件进行单独编译的,template.cpp源文件进行编译的过程中,没有检测到Sub函数模板的实例化,所以不会生成对应的代码,在main.cpp源文件中进行调用,链接阶段便会出错。如图:

在这里插入图片描述

 理解两个概念:

  • 导出符号表:编译完成后该源文件中地址(函数定义的位置)已经确定的函数
  • 未解决符号表:源文件中地址还没有确定的函数

这里main.cpp源文件编译完成后,没有找到Sub函数的定义,但是由于头文件中进行了声明,在预处理阶段头文件中的声明会拷贝到源文件中,所以并不会立即报错,而是将Sub函数放在未解决符号表中,链接阶段,在template.cpp文件的导出符号表中找Sub函数的入口地址,而如果Sub函数没有生成则会报错。
 

解决方法:
① 将声明和定义放到一个文件 "xxx.hpp" 里面或者xxx.h其实也是可以的。推荐使用这种。
② 模板定义的位置显式实例化。这种方法不实用,不推荐使用。

4. 模板优缺点

【优点】

① 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生。

② 增强了代码的灵活性。


【缺点】

① 模板会导致代码膨胀问题,也会导致编译时间变长。

② 出现模板编译错误时,错误信息非常凌乱,不易定位错误。

5. 模板相关笔试题

1. 下列的模板声明中,其中几个是正确的( )

1)template

2)template<T1,T2>

3)template<class T1,T2>

4)template<class T1,class T2>

5)template<typename T1,T2>

6)template<typename T1,typename T2>

7)template<class T1,typename T2>

8)<typename T1,class T2>

9)template<typeaname T1, typename T2, size_t N>

10)template<typeaname T, size_t N=100, class _A=alloc<T>>

11)template<size_t N>

A.3

B.4

C.5

D.6

2. 以下程序运行结果正确的是( )

template<typename Type>

Type Max(const Type &a, const Type &b)

{

cout<<"This is Max<Type>"<<endl;

return a > b ? a : b;

}

template<>

int Max<int>(const int &a, const int &b)

{

cout<<"This is Max<int>"<<endl;

return a > b ? a : b;

}

template<>

char Max<char>(const char &a, const char &b)

{

cout<<"This is Max<char>"<<endl;

return a > b ? a : b;

}

int Max(const int &a, const int &b)

{

cout<<"This is Max"<<endl;

return a > b ? a : b;

}

int main()

{

Max(10,20);

Max(12.34,23.45);

Max('A','B');

Max<int>(20,30);

return 0;

}

A.This is Max This is Max<Type> This is Max<char> This is Max<int>

B.This is Max<int> This is Max<Type> This is Max<char> This is Max<int>

C.This is Max This is Max<int> This is Max<char> This is Max<int>

D.This is Max This is Max<Type> This is Max<char> This is Max

3. 关于模板的编译说法错误的是( )

A.模板在.h文件中声明,在.cpp里面实现

B.模板程序一般直接在一个文件里面进行定义与实现

C.不久的将来,编译器有望支持export关键字,实现模板分离编译

D.模板不能分离编译,是因为模板程序在编译过程中需要经过两次编译

4. 以下程序运行结果正确的是( )

template<class T1, class T2>

class Data

{

public:

Data() { cout << "Data<T1, T2>" << endl; }

private:

T1 _d1;

T2 _d2;

};

template <class T1>

class Data<T1, int>

{

public:

Data() { cout << "Data<T1, int>" << endl; }

private:

T1 _d1;

int _d2;

};

template <typename T1, typename T2>

class Data <T1*,T2*>

{

public:

Data() { cout << "Data<T1*, T2*>" << endl; }

private:

T1 _d1;

T2 _d2;

};

template <typename T1, typename T2>

class Data <T1&, T2&>

{

public:

Data(const T1& d1, const T2& d2)

: _d1(d1)

, _d2(d2)

{

cout << "Data<T1&, T2&>" << endl;

}

private:

const T1 & _d1;

const T2 & _d2;

};

int main()

{

Data<double, int> d1;

Data<int, double> d2;

Data<int *, int*> d3;

Data<int&, int&> d4(1, 2);

return 0;

}

A.Data<T1, T2> Data<T1, int> Data<T1*, T2*> Data<T1&, T2&>

B.Data<T1, int> Data<T1, T2> Data<T1&, T2&> Data<T1*, T2*>

C.Data<T1, int> Data<T1, T2> Data<T1*, T2*> Data<T1&, T2&>

D.Data<T1, T2> Data<T1, T2> Data<T1*, T2*> Data<T1&, T2&>

答案:

1. D

分析:正确的定义为:4 6 7 9 10 11,一共6个

2. A

分析:Max(10,20);    //能够直接匹配int参数,调动非模板函数

Max(12.34,23.45); //double类型参数没有最佳匹配函数,此时只能调动模板函数

Max('A','B');   //能够直接匹配char参数,调动非模板函数

Max<int>(20,30); //由于直接实例化了函数,因此要调动模板函数,但是,由于进行函数的int特化,所以会调动特化版本的模板函数

3. A

A.模板不支持分离编译,所以不能在.h声明,在.cpp实现

B.由于不支持分离编译,模板程序一般只能放在一个文件里实现

C.不支持分离编译并不是语法错误,而是暂时的编译器不支持,不久将来,或许会被支持

D.模板程序被编译两次,这是不能分离编译的原因所在

4. C

分析:Data<double, int> d1; // 调用特化的int版本

Data<int, double> d2; // 调用基础的模板

Data<int *, int*> d3; // 调用特化的指针版本

Data<int&, int&> d4(1, 2); //调用特化的引用版本

本章完。

下一部分:C++中的继承,讲完继承讲多态。

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

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

相关文章

dhtmlx Event Calendar JavaScript new Crack

DHTMLX Event Calendar可帮助您开发类似 Google 的 JavaScript 事件日历&#xff0c;以高效地组织约会。用户可以通过拖放来管理事件&#xff0c;并以六种不同的模式显示它们。 JavaScript 事件日历功能 轻的 简单的 JavaScript API 六个默认视图&#xff1a;日、周、月、年、议…

Java并发编程学习16-线程池的使用(中)

线程池的使用&#xff08;中&#xff09; 引言1. 配置 ThreadPoolExecutor1.1 线程的创建与销毁1.2 管理队列任务1.3 饱和策略1.4 线程工厂1.5 定制 ThreadPoolExecutor 2. 扩展 ThreadPoolExecutor总结 引言 上篇分析了在使用任务执行框架时需要注意的各种情况&#xff0c;并…

死锁的成因以及解决方案(简析)

目录 一.为什么会产生死锁? 二.死锁产生的几个场景 一个线程一把锁的情况 关于可重入和不可重入锁的简单举例 两个线程两把锁的情况 多线程多把锁 如何解决死锁 一.为什么会产生死锁? 简单来说,就是进程加锁之后,没有被解锁而处于一直等待的状态 二.死锁产生的几个场景…

深入理解深度学习——BERT(Bidirectional Encoder Representations from Transformers):BERT的结构

分类目录&#xff1a;《深入理解深度学习》总目录 相关文章&#xff1a; BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;&#xff1a;基础知识 BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09…

软件架构模式—分层架构

这是软件架构模式博客系列第 2 章&#xff0c;我们将讨论分层架构模式。 分层架构模式是一种n层模式&#xff0c;其中组件按照水平层次进行组织。这是设计大多数软件的传统方法&#xff0c;旨在实现自我独立。这意味着所有组件之间相互连接&#xff0c;但彼此之间不相互依赖。…

测试体系与测试方案设计

如果我们想要测试一个系统&#xff0c;我们得先需要了解被测系统架构 业务架构:业务模型分析技术架构:技术组件、通讯协议分析数据架构:数据模型、数据存储引擎分析 电子商城 Mall 开源项目技术架构 经典技术架构 网关产品 Nginx Apache HttpdWeb 应用开发 Vue.js React移动应…

福州大学学报退稿率【爬虫+数据处理】

目录 一、爬虫 二、数据处理 2.1 历年投稿总数&#xff1a; 2.2 各稿件状态比例&#xff1a; 2.3 历年退稿率 三、总结&#xff08;福州大学学报退稿率&#xff09; 一、爬虫 从福州大学学报微信公众号可以发现稿件状态的查询接口&#xff0c; 根据测试可知稿件号由年份与当…

Linux共享内存

博客内容&#xff1a;共享内存 文章目录 一、认识共享内存结构二、如何创建共享内存&#xff1f;1.创建共享内存2.关联进程&#xff0c;取消进程3.释放共享内存 三、代码示例总结 一、认识共享内存结构 共享内存 共享内存指 (shared memory)在多处理器的计算机系统中&#xff…

新手速成!如何使用ChatGPT成为你的导师

1. 写在前面 最近我发现咱们的团队现在是人手ChatGPT&#xff0c;不光是我们团队&#xff0c;我整个行业的人都在用它解决生活跟工作中遇到的问题。可以看到的是大家也都是对它赞赏度很高 本文我将为大家介绍如何更加高效的使用ChatGPT提高工作效率&#xff0c;面向ChatGPT编程…

JavaScript高级学习总结

函数作用域 函数内部声明的变量&#xff0c;在函数外部无法被访问函数的参数也是函数内部的局部变量不同函数内部声明的变量无法互相访问函数执行完毕之后&#xff0c;函数内部的变量实际被清空了 块作用域 let声明的变量会产生块作用域&#xff0c;var不会产生块作用域cons…

QT +OpenSSL配置

QT OpenSSL配置 1 查看自己QT支持的OPenSSL版本号1.1 查看版本号1.2 是否配置了OPenSSL 2 安装OPenSSL2.1 下载已经编译好的库2.2 自己编译代码2.2.1 下载perl2.2.1 下载OPenSSL源码 1 查看自己QT支持的OPenSSL版本号 1.1 查看版本号 新建项目testOpenSSLpro文件中加入QT ne…

(贪心) 649. Dota2 参议院 ——【Leetcode每日一题】

❓ 649. Dota2 参议院 难度&#xff1a;中等 Dota2 的世界里有两个阵营&#xff1a;Radiant&#xff08;天辉&#xff09;和 Dire&#xff08;夜魇&#xff09; Dota2 参议院由来自两派的参议员组成。现在参议院希望对一个 Dota2 游戏里的改变作出决定。他们以一个基于轮为过…

Debian11 dhclient 不自动执行问题

这两天用U盘安装Debian11&#xff0c;在”安装软件“一直提示失败&#xff0c;但可以跳过这一步继续往下安装&#xff0c;好在基本系统及grub能正常安装&#xff0c;最后系统也能正常起来了&#xff0c;但发现系统起来后没有ip地址&#xff0c;需要手动执行 dhclient 来获取ip。…

Java的第十二篇文章——集合

目录 第十二章 集合 学习目标 1. 集合框架的由来 2. 集合框架的继承体系 3. Collection接口 3.1 Collection接口的常用方法 4. Iterator接口 4.1 Iterator接口的抽象方法 4.2 获取迭代器接口实现类 4.3 迭代器的实现原理 4.4 并发修改异常 4.5 集合存储自定义对象并…

【Git常用命令及在IDEA中的使用】

Git常用命令及在IDEA中的使用 Git常用命令及在IDEA中的使用1 Git 概述1.1 Git 简介1.2 Git 下载与安装 2 Git 代码托管服务2.1 常用的Git 代码托管服务2.2 使用码云代码托管服务 3 Git 常用命令3.1 Git 全局设置3.2 获取 Git 仓库3.3 工作区、暂存区、版本库 概念3.4 Git工作区…

MyBatis面试题总结

1.概念/使用方法向的问题 1.1 什么是Mybatis? &#xff08;1&#xff09;Mybatis是一个半ORM框架&#xff0c;它内部封装了JDBC&#xff0c;开发时只需要关注SQL语句本身&#xff0c;不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。 &#xff08;2&a…

​​​​SpringBoot 监控神器——Actuator 保姆级教程

pom.xml info beans conditions heapdump shutdown mappings threaddump loggers 端点 metrics 端点 自定义Endpoint 自定义监控端点常用注解 使用Filter对访问actuator做限制 Spring Boot Monitor做监控页面 SpringBoot自带监控功能Actuator&#xff0c;可以帮助…

Kubernetes学习笔记-kubernetes应用扩展(2)-使用kubernetes服务目录扩展kubernetes20230623

一、服务目录介绍 服务目录就是列出所有的服务的目录。用户可以浏览目录并自行设置目录中列出的服务实例&#xff0c;无须处理服务运行所需的pod、service、configmap和其他资源。这听起来和自定义网站资源很类似。 服务目录并不会为每种服务类型的api服务器添加自定义资源&a…

全栈开发实战那些事

文章目录 一个网站是怎么来的&#xff1f; Git篇隔离项目和原有Git工程联系Git冲突的原因通常有以下几种&#xff1a; IDEA篇IDEA常用操作Git可视化操作&#xff08;提交代码前先pull更新merge最新版本一下再push&#xff0c;保证提交的最终项目是最新&#xff09; IDEA中Git冲…

Jenkins 发送文件到远程服务器:Publish Over SSH 插件

Jenkins 发送文件到远程服务器&#xff1a;Publish Over SSH 插件 文章目录 Jenkins 发送文件到远程服务器&#xff1a;Publish Over SSH 插件一、Publish Over SSH 插件1、概述2、主要功能和特点3、插件主页4、安装 Publish Over SSH 插件5、配置远程主机 二、发送文件到远程主…