[C++随想录] 模版进阶

news2025/4/21 15:58:28

模版进阶

  • 模版中 class 与 typename
  • 非类型模版参数
  • 模版的分离编译
  • 模版的特化
    • 函数模版的特化
    • 类模板的特化
      • 1. 全特化
      • 2.偏特化


模版中 class 与 typename

一般情况下, 我们定义一个模版, 模版中的 class/ typename 的意义是一样的.
但是, 有一种情况除外👇👇👇

template<class Container>
void Print(const Container& v)
{
	Continer::iterator it = v.begin();

	while (it != v.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}


🗨️这是为什么呢?

  • 首先, iterator 迭代器是属于类的一种类型, 我们要指定类域 ⇒ 即Continer应该是一种类型才对
    其次, 编译器是从上到下编译的, 所以此时模版还没实例化 ⇒ 编译器不清楚Continer是一种类型还是一种对象?
    编译器为什么会有这种疑惑呢?
    因为 用 :: 调用内部成员有两种方式: 1. 类型 2.静态成员对象
    此时我们这里需要的是 类型 ⇒ 所以, 我们需要在前面加上 typename, 从而告诉编译器虽然这里还没有实例化, 但是这里是一种类型👇👇👇
template<class Container>
void Print(const Container& v)
{
	typename Container::iterator it = v.begin();
	while (it != v.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

非类型模版参数

🗨️根据前面的学习, 我们知道了模版的参数可以是 类型, 容器适配器等, 它们都是一种变量, 是一种类型~~, 有没有一种模版参数是常量的, 不可改变的呢?

  • 其实, 模板参数是分两类的:
    1. 类型模版参数 — — 在模版参数中跟在class/ typename的后面充当一种 类型
    2. 非类型模版参数 — — 用 常量 来充当模版的一个参数, 在函数/ 类中就当做一个常量使用

构造一个静态数组来练练手:

namespace muyu
{
	template<class T, size_t N = 10>
	class Array
	{

	public:

		Array()
		{

		}

		T& operator[](size_t pos)
		{
			return _arr[pos];
		}
		const T& operator[](size_t pos) const
		{
			return _arr[pos];
		}

		size_t size() const
		{
			return _size;
		}

	private:
		T _arr[N];
		size_t _size = N;
	};
}

void test_Array()
{
	muyu::Array<int, 10> arr;

	for (int i = 0; i < arr.size(); i++)
	{
		arr[i] = i;
		cout << arr[i] << " ";
	}
	cout << endl;

}

int main()
{
	test_Array();

	return 0;
}

运行结果:

0 1 2 3 4 5 6 7 8 9

总结:

  1. 非类型模版参数是常量, 不能修改
  2. 非类型模版参数必须是 整形家族

  3. 非类型模版参数的应用

    array数组 是非常的鸡肋, 跟 普通的数组没有什么两样, 还是 C++11 更新的😥😥😥

模版的分离编译

🗨️什么是分离编译?

  • 一个程序由多个源文件共同实现的, 每个源文件单独生成目标文件. 最后将所有的目标文件链接起来形成一个统一的可执行文件的过程.

接下来, 我们来看一下模版的分离编译的情况:

// stencil.h
template<class T>
// 声明
T& Add(const T& x, const T& y);


// implement.cpp
// 定义
template<class T>
T Add(const T& x, const T& y)
{
	return x + y;
}

// main.cpp
int main()
{
	Add(1, 2);

	return 0;
}


🗨️为什么模版的分离编译会出现 链接错误 ?

  • 首先, 编译的四个阶段 :预处理, 编译, 汇编, 链接
    编译阶段 — — 对代码进行 语法分析, 语义分析, 如果没有什么问题, 就生成汇编
    链接阶段 — — 将多个 .o文件 链接形成一个 目标文件, 同时检查 地址问题
    比如: 普通函数的声明与定义分离:
    编译阶段 — — 如果 语法检查, 语义检查没什么问题, 虽然定义没有, 但可以做一个承诺 -- 它的定义是有的, 先让它过去, 等链接阶段在深层次检查
    链接阶段 — — 进一步检查是否有定义(地址)
    🗨️普通函数是可以的, 为啥模版的分离编译有问题?
    • 先搞清楚, 普通函数的参数类型是已知的, 而模版参数是未知的 ⇐ 因为还没有模版实例化.
      C++编译器在处理 函数模版 和 类模版的时候, 要进行实例化函数模版 和 类模版, 要求编译器在实例化模版时必须在上下文可以查看到其定义实体; 而反过来, 在看到实例化模版之前, 编译器对模版的定义是不做处理的. 原因很简单, 编译器怎么会预先知道 typename实参 是什么呢?

🗨️那怎么样才能实现模版的分离编译呢?

  • 1. 在定义的地方 显示实例化
    不推荐这种, 因为不同类型就要显示实例化多次 ⇒ 那么就失去了模版的意义~~
// stencil.h
template<class T>
// 声明
T& Add(const T& x, const T& y);


// implement.cpp
// 定义
template<class T>
T Add(const T& x, const T& y)
{
	// 显示实例化
	template
	class Add<int, int>
	
	return x + y;
}

// main.cpp
int main()
{
	Add(1, 2);

	return 0;
}
  1. 将模版的声明与定义写在同一个文件中, 文件可以命名为 .hpp 或 .h 都是可以的

模版的特化

先看下面的例子👇👇👇

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	bool operator<(const Date& d)const
	{
		return (_year < d._year) ||
			(_year == d._year && _month < d._month) ||
			(_year == d._year && _month == d._month && _day < d._day);
	}
	bool operator>(const Date& d)const
	{
		return (_year > d._year) ||
			(_year == d._year && _month > d._month) ||
			(_year == d._year && _month == d._month && _day > d._day);
	}

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(2022, 7, 7);
	Date d2(2022, 7, 8);
	cout << Less(d1, d2) << endl; // 可以比较,结果正确

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

	return 0;

}

运行结果:

1
1
0

由于我们传的是 地址, 属于 内置类型 && 我们不能改变 内置类型的比较规则 ⇒ 就需要对模板进行特化。即:在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。
模板特化中分为函数模板特化与类模板特化。

函数模版的特化

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	bool operator<(const Date& d)const
	{
		return (_year < d._year) ||
			(_year == d._year && _month < d._month) ||
			(_year == d._year && _month == d._month && _day < d._day);
	}
	bool operator>(const Date& d)const
	{
		return (_year > d._year) ||
			(_year == d._year && _month > d._month) ||
			(_year == d._year && _month == d._month && _day > d._day);
	}

private:
	int _year;
	int _month;
	int _day;
};

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

// 函数模版的特化 -- Date*
template<>
bool Less<Date*>(Date* left, Date* right)
{
	return *left < *right;
}

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

	Date d1(2022, 7, 7);
	Date d2(2022, 7, 8);
	cout << Less(d1, d2) << endl; // 可以比较,结果正确

	Date* p1 = &d1;
	Date* p2 = &d2;
	cout << Less(p1, p2) << endl; // 这个时候改变了比较的类型

	return 0;

}

运行结果:

1
1
1

  1. 函数模版的特化离不开原有模版
  2. 函数模版的特化的写法: template<> 函数名后面要跟上特化的类型, 然后 改变里面进行比较的类型
  3. 函数模版的特化, 还不如写一个特殊类型的同名函数和原函数模版构成 函数重载
  • 函数 模版的特化, 还不如写一个 函数重载👇👇👇
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	bool operator<(const Date& d)const
	{
		return (_year < d._year) ||
			(_year == d._year && _month < d._month) ||
			(_year == d._year && _month == d._month && _day < d._day);
	}
	bool operator>(const Date& d)const
	{
		return (_year > d._year) ||
			(_year == d._year && _month > d._month) ||
			(_year == d._year && _month == d._month && _day > d._day);
	}

private:
	int _year;
	int _month;
	int _day;
};

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

// 函数的重载
bool Less(Date* d1, Date* d2)
{
	return *d1 < *d2;
}

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

	Date d1(2022, 7, 7);
	Date d2(2022, 7, 8);
	cout << Less(d1, d2) << endl; // 可以比较,结果正确

	Date* p1 = &d1;
	Date* p2 = &d2;
	cout << Less(p1, p2) << endl; // 这个时候改变了比较的类型

	return 0;

}

类模板的特化

函数模版的特化 可以用 函数重载 代替, 难道类模版的特化 也可以用 类的重载 来代替?
打你一耳光哦, 你听过类的重载吗~~
当然不行的啦

类模板的特化分为两种, 全特化 和 偏特化

1. 全特化

全特化, 顾名思义, 是 对类模板中的所有参数都 确定化

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

// 类模板的全特化
template<>
class Date<int, double>
{
public:
	Date()
	{
		cout << "Date<int, doule>" << endl;
	}
};

int main()
{
	Date<int, int> d1;
	Date<int, double> d2;

	return 0;
}

运行结果:

Date<class T1, class T2>
Date<int, doule>

妙用:👇👇👇

// 优先级队列, 默认是大堆
namespace muyu
{
	template <class T, class Continer = std::vector<T>, class Compare = Less<T> >
	class priority_queue
	{
	private:

		void AjustUp(int child)
		{
			Compare com;
			int parent = (child - 1) / 2;

			while (child > 0)
			{
				if( com(_con[parent], _con[child]) )
				{
					std::swap(_con[child], _con[parent]);

					// 在内部更新child 和 parent
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}

		void AjustDown(int parent)
		{
			Compare com;

			int child = 2 * parent + 1;

			while (child < _con.size())
			{
				// 找到孩子中大的那一个
				if (child + 1 < _con.size() &&  com(_con[child], _con[child + 1]) )
				{
					child++;
				}

				if ( com(_con[parent], _con[child]))
				{
					std::swap(_con[parent], _con[child]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}
	public:

		priority_queue()
		{

		}

		template<class InputIterator>
		priority_queue(InputIterator first, InputIterator last)
		{
			// 一股脑地倒进来
			while (first != last)
			{
				_con.push_back(*first);
				++first;
			}

			// 建堆
			for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--)
			{
				AjustDown(i);
			}
		}

		void push(const T& val = T())
		{
			_con.push_back(val);

			AjustUp(_con.size() - 1);
		}

		void pop()
		{
			std::swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();

			AjustDown(0);

		}

		const T& top() const
		{
			return _con[0];
		}

		bool empty() const
		{
			return _con.size() == 0;
		}

		size_t size() const
		{
			return _con.size();
		}

	private:
		Continer _con;
	};
}

// 日期类
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	bool operator<(const Date& d)const
	{
		return (_year < d._year) ||
			(_year == d._year && _month < d._month) ||
			(_year == d._year && _month == d._month && _day < d._day);
	}
	bool operator>(const Date& d)const
	{
		return (_year > d._year) ||
			(_year == d._year && _month > d._month) ||
			(_year == d._year && _month == d._month && _day > d._day);
	}
	friend ostream& operator<<(ostream& _cout, const Date& d);
	
private:
	int _year;
	int _month;
	int _day;
};

// 日期类重载留插入
ostream& operator<<(ostream& _cout, const Date& d)
{
	_cout << d._year << "-" << d._month << "-" << d._day;
	return _cout;
}

// 仿函数
template <class T>
class Less
{
public:
	bool operator()(const T& x, const T& y)
	{
		return x < y;
	}
};

// 全特化
template <>
class Less<Date*>
{
public:
	bool operator()(const Date* x, const Date* y)
	{
		return *x < *y;
	}
};

void test()
{
	muyu::priority_queue<Date*> pq;
	pq.push(new Date(2023, 9, 27));
	pq.push(new Date(2023, 9, 28));
	pq.push(new Date(2023, 9, 29));
	pq.push(new Date(2023, 9, 1));


	while (!pq.empty())
	{
		cout << *pq.top() << " ";
		pq.pop();
	}
	cout << endl;

}

int main()
{
	test();

	return 0;
}

运行结果:

2023-9-29 2023-9-28 2023-9-27 2023-9-1

这样的好处:

  1. 传参类型如果是 T, 那么就按照 T 来进行比较; 如果传参类型是 Date*, 那么就按照 Date 来进行比较
  2. 其实没有 模版的特化, 我们无法同时写出 T 和 Date* 的一个仿函数.

2.偏特化

偏特化又有两种形式: 部分特化 和 对参数做进一步限制

  1. 部分特化
template<class T1, class T2>
class Date
{
public:
	Date()
	{
		cout << "Date<class T1, class T2> " << endl;
	}
};

// 类模板的偏特化
template<class T1>
class Date<T1, double>
{
public:
	Date()
	{
		cout << "Date<T1, double>" << endl;
	}
};

// 类模板的偏特化
template<class T1>
class Date<T1, int&>
{
public:
	Date()
	{
		cout << "Date<T1, int&>" << endl;
	}
};

int main()
{
	Date<int, int> d1;
	Date<int, double> d2;
	Date<int, int&> d3;

	return 0;

}

运行结果:

Date<class T1, class T2>
Date<T1, double>
Date<T1, int&>
  1. 对参数做进一步限制
template<class T1, class T2>
class Date
{
public:
	Date()
	{
		cout << "Date<class T1, class T2> " << endl;
	}
};

// 类模板的偏特化
template<class T1, class T2>
class Date<T1*, T2*>
{
public:
	Date()
	{
		cout << "Date<T1*, T2*>" << endl;
	}
};

// 类模板的偏特化
template<class T1, class T2>
class Date<T1&, T2&>
{
public:
	Date()
	{
		cout << "Date<T1&, T2&>" << endl;
	}
};

int main()
{
	Date<int, int> d1;
	Date<int*, double*> d2;
	Date<int& , int&> d3;

	return 0;

}

运行结果:

Date<class T1, class T2>
Date<T1*, T2*>
Date<T1&, T2&>

那么, 我们可以把所有 有关迭代器的比较 特化成 迭代器指向内容的比较, 从而达到我们比较的目的

// 优先级队列, 默认是大堆
namespace muyu
{
	template <class T, class Continer = std::vector<T>, class Compare = Less<T> >
	class priority_queue
	{
	private:

		void AjustUp(int child)
		{
			Compare com;
			int parent = (child - 1) / 2;

			while (child > 0)
			{
				if( com(_con[parent], _con[child]) )
				{
					std::swap(_con[child], _con[parent]);

					// 在内部更新child 和 parent
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}

		void AjustDown(int parent)
		{
			Compare com;

			int child = 2 * parent + 1;

			while (child < _con.size())
			{
				// 找到孩子中大的那一个
				if (child + 1 < _con.size() &&  com(_con[child], _con[child + 1]) )
				{
					child++;
				}

				if ( com(_con[parent], _con[child]))
				{
					std::swap(_con[parent], _con[child]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}
	public:

		priority_queue()
		{

		}

		template<class InputIterator>
		priority_queue(InputIterator first, InputIterator last)
		{
			// 一股脑地倒进来
			while (first != last)
			{
				_con.push_back(*first);
				++first;
			}

			// 建堆
			for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--)
			{
				AjustDown(i);
			}
		}

		void push(const T& val = T())
		{
			_con.push_back(val);

			AjustUp(_con.size() - 1);
		}

		void pop()
		{
			std::swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();

			AjustDown(0);

		}

		const T& top() const
		{
			return _con[0];
		}

		bool empty() const
		{
			return _con.size() == 0;
		}

		size_t size() const
		{
			return _con.size();
		}

	private:
		Continer _con;
	};
}

// 日期类
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	bool operator<(const Date& d)const
	{
		return (_year < d._year) ||
			(_year == d._year && _month < d._month) ||
			(_year == d._year && _month == d._month && _day < d._day);
	}
	bool operator>(const Date& d)const
	{
		return (_year > d._year) ||
			(_year == d._year && _month > d._month) ||
			(_year == d._year && _month == d._month && _day > d._day);
	}
	friend ostream& operator<<(ostream& _cout, const Date& d);
	
private:
	int _year;
	int _month;
	int _day;
};

// 日期类重载留插入
ostream& operator<<(ostream& _cout, const Date& d)
{
	_cout << d._year << "-" << d._month << "-" << d._day;
	return _cout;
}

// 仿函数
template <class T>
class Less
{
public:
	bool operator()(const T& x, const T& y)
	{
		return x < y;
	}
};

// 偏特化
template <class T>
class Less<T*>
{
public:
	bool operator()(const T* x, const T* y)
	{
		return *x < *y;
	}
};

void test()
{
	muyu::priority_queue<Date*> pq;
	pq.push(new Date(2023, 9, 27));
	pq.push(new Date(2023, 9, 28));
	pq.push(new Date(2023, 9, 29));
	pq.push(new Date(2023, 9, 1));


	while (!pq.empty())
	{
		cout << *pq.top() << " ";
		pq.pop();
	}
	cout << endl;

}

int main()
{
	test();

	return 0;
}

模版的特化总结:

  1. 模版的特化离不开原模版, 不能独立存在
  2. 特化是做特殊化处理, 具体情况具体使用

弟子曰:一友常易动气责人。
阳明曰:学须反己。若徒责人,只见得人不是,不见自己非;若能反己,方见自己有许多未尽处,奚暇责人?
译文:
弟子说:有个朋友常常生气责怪别人。
先生说:如果只是责备别人,看不到自己的不足,那自己将无法进步,意识到这一点,怎么有空去指责别人呢?
心理学上有个说法叫:偏颇的思维定式。
如果事情成功了,人们倾向于这是自己的功劳。
如果事情做得很差,人们则认为这是别人的问题。
把问题归咎于别人,是人的本性。
所以,在某种意义上,反省自己并非一种美德,而是一种对自身偏颇思维的校正。
通过反省,我们才能公正地看待别人和自己。
认清自己,发现自己的问题和缺漏。
以缺为正,补偏救弊,这样才能不断改正,不断成长。

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

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

相关文章

【Linux】 ps命令使用

作为一个后端的程序员&#xff0c;我们经常用到ps -ef | grep XXX 到底什么事ps呢。 下面我们一起学习一下吧、 ps &#xff08;英文全拼&#xff1a;process status&#xff09;命令用于显示当前进程的状态&#xff0c;类似于 windows 的任务管理器。 ps命令 -Linux手册页 …

云原生SIEM解决方案

云原生&#xff08;Cloud Native&#xff09;是一种基于云计算的软件开发和部署方法论&#xff0c;它强调将应用程序和服务设计为云环境下的原生应用&#xff0c;以实现高可用性、可扩展性和灵活性。 云原生的优势有哪些 高可用性&#xff1a;云原生可以实现应用程序的高可用…

易点易动RFID固定资产管理系统:实现固定资产快速准确盘点的利器

在现代企业管理中&#xff0c;固定资产的盘点是一项重要而繁琐的任务。传统的盘点方法往往耗时耗力&#xff0c;容易出现错误和遗漏&#xff0c;给企业带来不必要的麻烦和损失。为了解决这些问题&#xff0c;我们推出了易点易动RFID固定资产管理系统&#xff0c;它利用射频识别…

Plsql连接报Initialization Error:Could not initialize oci.dll

可能原因&#xff1a;本地plsql为32位。 目前本机中使用的是 instantclient_19_6&#xff0c;其中的oci.dll库为64位。 到Oracle官网下载 http://www.oracle.com/technetwork/database/features/instant-client/index-097480.html 下载Instant Client for Microsoft Window…

JavaScript之正则表达式

详见MDN 正则表达式(RegExp) 正则表达式不是JS独有的内容&#xff0c;大部分语言都支持正则表达式 JS中正则表达式使用得不是那么多&#xff0c;我们可以尽量避免使用正则表达式 在JS中&#xff0c;正则表达式就是RegExp对象&#xff0c;RegExp 对象用于将文本与一个模式匹配 正…

轻松搭建个人web站点:OpenWRT教程结合内网穿透技术实现公网远程访问

&#x1f525;博客主页&#xff1a; 小羊失眠啦 &#x1f516;系列专栏&#xff1a; C语言、Linux &#x1f325;️每日语录&#xff1a;山不让尘&#xff0c;川不辞盈。 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 前言 uhttpd 是 OpenWrt/LuCI 开发者从零开始编写的 Web …

LinkedHashMap与LRU缓存

序、慢慢来才是最快的方法。 背景 LinkedHashMap 是继承于 HashMap 实现的哈希链表&#xff0c;它同时具备双向链表和散列表的特点。事实上&#xff0c;LinkedHashMap 继承了 HashMap 的主要功能&#xff0c;并通过 HashMap 预留的 Hook 点维护双向链表的逻辑。 1.缓存淘汰算法…

保护敏感数据的艺术:数据安全指南

多年来&#xff0c;工程和技术迅速转型&#xff0c;生成和处理了大量需要保护的数据&#xff0c;因为网络攻击和违规的风险很高。为了保护企业数据&#xff0c;组织必须采取主动的数据安全方法&#xff0c;了解保护数据的最佳实践&#xff0c;并使用必要的工具和平台来实现数据…

4WE6Y61B/CG24N9Z5L液压电磁阀

特点 1.直动式电磁铁操作方向滑阀作为标准类型; 2.安装面按DIN24 340 A型ISO4401和CETOP-RP 121H;3.电磁铁可任意旋转&#xff0c;线圈可拆卸的直流或交流湿式电磁铁; 4.可不放油液更换线圈; 5.可带有手动应急操作推杆&#xff0c;

史上最强,Jmeter性能测试-性能场景设计实例(详全)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、性能测试需求 …

Caffeine Cache

Caffeine Cache 高性能的 Java本地缓存库 底层使用 ConcurrentHashMap TinyLFU 一个近乎最佳的命中率 LRU&#xff1a;最近最少使用算法&#xff0c;每次访问数据都会将其放在我们的队尾&#xff0c;如果需要淘汰数据&#xff0c;就只需要淘汰队首即可。容易导致了热点数据…

『heqingchun-ubuntu系统下安装nvidia显卡驱动3种方法』

ubuntu系统下安装nvidia显卡驱动3种方法 一、安装依赖 1.更新 sudo apt updatesudo apt upgrade -y2.基础工具 sudo apt install -y build-essential python图形界面相关 sudo apt install -y lightdm注:在弹出对话框选择"lightdm" 二、第一种&#xff1a;使用…

如何构建安全的App网络通信?

前言 说到安全肯定逃不开数据的加解密&#xff0c;数据本地存储大多用对称加解密来实现&#xff0c;那网络传输数据的时候是不是也用对称加解密来实现&#xff1f;没错&#xff0c;常规网络通信时&#xff0c;大部分网络传输过程中基本也是用对称加解密来实现的&#xff0c;毕竟…

工程师必须记住的电路元件符号及英语翻译

很多电子小白第一次接触印刷电路板&#xff08;PCB&#xff09;时&#xff0c;总会头痛那些密密麻麻的元件字母符号&#xff0c;这些电路元件符号基本上都是采用英语缩写&#xff0c;下面我们来看看这些电路元件的英语符号有哪些&#xff1f; 电阻器&#xff08;Resistor&#…

fl studio21最新汉化破解版中文解锁下载完整版本

FL Studio 21 中文版是最受欢迎的音乐更改程序。这个工具可以改变和制作任何音乐或节奏。它还具有许多新功能&#xff0c;例如 Direct Wave 升级、对 Fruity Reverb 的 64 位支持&#xff0c;以及具有 125 个轨道的扩展混音器&#xff0c;可改善播放列表&#xff08;Playlist&a…

软考中级和高级,有什么区别?

软考是指软件技术人员职业资格考试&#xff0c;是由国家人力资源和社会保障部主管的全国性考试。软考分为初级、中级和高级三个等级&#xff0c;其中中级和高级是较为高级的考试等级。那么&#xff0c;软考中级和高级有什么区别呢&#xff1f;本文将从以下几个方面进行阐述。 …

CVE-2020-9483 apache skywalking SQL注入漏洞

漏洞概述 当使用H2 / MySQL / TiDB作为Apache SkyWalking存储时&#xff0c;通过GraphQL协议查询元数据时&#xff0c;存在SQL注入漏洞&#xff0c;该漏洞允许访问未指定的数据。 Apache SkyWalking 6.0.0到6.6.0、7.0.0 H2 / MySQL / TiDB存储实现不使用适当的方法来设置SQL参…

爆肝整理,Jmeter接口性能测试-跨线程调用变量实操(超详细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、Jmeter中线程运…

可观测性数据收集集大成者 Vector 介绍

如果企业提供 IT 在线服务&#xff0c;那么可观测性能力是必不可少的。“可观测性” 这个词近来也越发火爆&#xff0c;不懂 “可观测性” 都不好意思出门了。但是可观测性能力的构建却着实不易&#xff0c;每个企业都会用到一堆技术栈来组装建设。比如数据收集&#xff0c;可能…

微信页面公众号页面 安全键盘收起后页面空白

微信浏览器打开H5页面和公众号页面&#xff0c;输入密码时调起安全键盘&#xff0c;键盘收起后 键盘下方页面留白 解决办法&#xff1a; 1、&#xff08;简单&#xff09;只有在调起安全键盘&#xff08;输入密码&#xff09;的时候会出现这种情况&#xff0c;将input属性改为n…