C++_进阶:C++11新增语法(2)

news2024/9/20 14:29:58

文章目录

    • 1.新的类功能
      • 1.1 新的默认成员函数
      • 2. 类成员变量初始化
      • 3. 强制生成默认函数的关键字default
      • 4. 禁止生成默认函数的关键字delete:
      • 5. final与override关键字
    • 2. 可变参数模板
    • 3. lambda表达式
      • 3.1 一个使用场景
      • 3.2 lambda表达式语法
      • **3.3 lambda捕捉列表详细说明**
      • 3.4 **底层的lambda**
    • 4. function包装器
    • 5. bind包装器

1.新的类功能

1.1 新的默认成员函数

在这里插入图片描述

在 C++入门 类和对象:构造函数,析构函数,拷贝构造 中,提及类的默认成员函数有以上6个,C++11 新增了两个:移动构造函数移动赋值运算符重载,所以之后的类的默认成员函数有8个了 。

针对移动构造函数和移动赋值运算符重载有一点需要注意:

  1. 如果你没有自己实现 移动构造函数 / 移动赋值重载函数,并且没有实现析构函数拷贝构造拷贝赋值重载,那么编译器会自动生成一个默认移动构造函数 / 默认移动赋值重载函数
    在这里插入图片描述
  2. 默认生成的移动构造函数 / 移动赋值重载函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造/移动赋值,如果实现了就调用移动构造/移动赋值,没有实现就调用拷贝构造/移动赋值。
    在这里插入图片描述

2. 类成员变量初始化

C++11允许在类定义时给成员变量初始缺省值,默认生成构造函数会使用这些缺省值初始化

3. 强制生成默认函数的关键字default

C++11可以让你更好的控制要使用的默认函数。假设你要使用某个默认的函数,但是因为一些原因这个函数没有默认生成。比如:我们提供了拷贝构造,就不会生成移动构造了,那么我们可以使用default关键字显示指定移动构造生成。

class Person
{
public:
	//显示生成拷贝构造
	Person(const Person& tmp)
	{
		age = tmp.age;
		name = tmp.name;
	}
	//强制生成移动拷贝
	Person(Person&& tmp) = default;

	string name;
	int age;
};

4. 禁止生成默认函数的关键字delete:

如果能想要限制某些默认函数的生成,在C++98中,是该函数设置成private,并且只声明不实现,这样只要其他人想要调用就会报错。在C++11中更简单,只需在该函数声明加上=delete即可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数。

//C++98的方法,把对应函数声明在private中,
//并且只声明不实现
class Date
{
public:
	//...实现的其他函数
private:
	Date(const Date& tmp);
	int year;
	int month;
	int day;
};
//C++11的方法,直接delete
class Date
{
public:
	Date(const Date& tmp) = delete;
private:
	int year;
	int month;
	int day;
};

5. final与override关键字

在C++_进阶:多态详解中有介绍。

2. 可变参数模板

相比C++98/03,类模版和函数模版中只能含固定数量的模版参数,C++11的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板

可变参数模板的语法

// Args 是一个模板参数包类型,写在<>中,前面要+...
// args是一个函数形参参数包,写在形参中,声明时Args后要+...
template <class ...Args>
void ShowList(Args... args)
{}


int main()
{
	ShowList(3 , 3.3 , 3 ,3.3);
}

在这里插入图片描述
上面的参数args前面有省略号,所以它就是一个可变模版参数,我们把带省略号的参数称为“参数包”,它里面包含了0到N(N>=0)个模版参数。我们无法直接获取参数包args中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数。

1️⃣递归函数方式展开参数包

// 递归终止函数
template <class T>
void ShowList(const T& t)
{
	cout << t << endl;
}
// 展开函数
template <class T, class ...Args>
void ShowList(T value, Args... args)
{
	cout << value << " ";
	ShowList(args...);
}
int main()
{
	ShowList(1, 'A', std::string("sort"));
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
输出结果:
在这里插入图片描述


2️⃣逗号表达式展开参数包

template <class T>
void PrintArg(T t)
{
	cout << t << " ";
}

//展开函数
template <class ...Args>
void ShowList(Args... args)
{
	int arr[] = { (PrintArg(args), 0)... };
	cout << endl;
}

int main()
{
	ShowList(1, 'A', std::string("sort"));
	return 0;
}

这种展开参数包的方式,不需要通过递归终止函数,是直接在expand函数体中展开的, printarg不是一个递归终止函数,只是一个处理参数包中每一个参数的函数。这种就地展开参数包的方式实现的关键是逗号表达式。我们知道逗号表达式会按顺序执行逗号前面的表达式。

expand函数中的逗号表达式:(printarg(args), 0),也是按照这个执行顺序,先执行
printarg(args),再得到逗号表达式的结果0。同时还用到了C++11的另外一个特性——初始化列表,通过初始化列表来初始化一个变长数组
在这里插入图片描述

在这里插入图片描述

在STL容器中,empalce相关接口函数就是用可变参数模板实现的。
在这里插入图片描述
在这里插入图片描述

// emplace_back的接口
template <class... Args>
void emplace_back (Args&&... args);
//

Args&&… 万能引用参数包类型,能接受一个及以上的左值或者右值

emplace的功能是尾插,首先我们看到的emplace系列的接口,支持模板的可变参数,并且万能引用。他与push_back有什么区别呢?

int main()
{
	std::list< std::pair<int, char> > mylist;
	// emplace_back支持可变参数,拿到构建pair对象的参数后自己去创建对象
	// 那么在这里我们可以看到除了用法上,和push_back没什么太大的区别
	mylist.emplace_back(10, 'a');
	mylist.emplace_back(20, 'b');
	mylist.emplace_back(make_pair(30, 'c'));
	mylist.push_back(make_pair(40, 'd'));
	mylist.push_back({ 50, 'e' });
	for (auto e : mylist)
	cout << e.first << ":" << e.second << endl;
	return 0;
}
	//...
	//在模拟的list类中,模拟实现emplace_back
	template <class... Args>
	void emplace_back(Args&&... args)	{			
		insert(end(), std::forward<Args>(args)...);
	}
	template <class... Args>
	//emplace_back复用insert 再实现一个模板的可变参数的insert
	iterator insert(iterator pos, Args&&... args)
	{
		Node* cur = pos._node;
		Node* newnode = new Node(std::forward<Args>(args)...);
		Node* prev = cur->_prev;
				
		// prev  newnode  cur
		prev->_next = newnode;
		newnode->_prev = prev;
		newnode->_next = cur;
		cur->_prev = newnode;

			return iterator(newnode);
	}
int main()
{
	// 下面我们试一下带有拷贝构造和移动构造的bit::string,再试试呢
	// 我们会发现其实差别也不到,emplace_back是直接构造了,push_back
	// 是先构造,再移动构造,其实也还好。
	std::list< std::pair<int, bit::string> > mylist;
	mylist.emplace_back(10, "sort");
	mylist.emplace_back(make_pair(20, "sort"));
	mylist.push_back(make_pair(30, "sort"));
	mylist.push_back({ 40, "sort"});
	return 0;
}

3. lambda表达式

3.1 一个使用场景

struct Goods
{
	string _name; // 名字
	double _price; // 价格
	int _evaluate; // 评价
	Goods(const char* str, double price, int evaluate)
		:_name(str)
		, _price(price)
		, _evaluate(evaluate)
	{}
};

int main()
{
	vector<Goods> v = { { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };
	
	// lambda表达式填写sort的第三个参数
	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
		return g1._evaluate > g2._evaluate;
	});
}

sort的第三个参数是仿函数类型。对于像Goods一样的自定义类型,在C++11之前,每次为了实现一个algorithm算法,都要重新去写一个类,如果每次比较的逻辑不一样,还要去实现多个类,特别是相同类的命名。C++11之后,就可以使用lambda表达式,避免每次都去实现一个类,提高便利性。lambda表达式实际是一个匿名函q数。

3.2 lambda表达式语法

语法:

[capture-list] (parameters) mutable -> return-type { statement}

在这里插入图片描述

1️⃣ lambda表达式各部分说明:

  • [capture-list] : 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用。
  • (parameters)参数列表。与普通函数的参数列表一致,如果不需要参数传递,则以连同()一起省略
  • mutable :默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。
  • ->returntype返回值类型。用追踪返回类型形式声明函数的返回值类型,没有回值时此部分可省略。返回值类型明确情况下,也可省略由编译器对返回类型进行推导。
  • {statement} :函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量

3.3 lambda捕捉列表详细说明

捕捉列表描述了本作用域中那些数据可以被lambda使用。

int main()
{
	//本作用域有三个变量
	int x = 1, y = 2, z = 3;

	//1. []{}最简单的lambda, 该lambda表达式没有任何意义
	// auto f = lambda 没有其他的意思,就看右边部分就行
	auto f1 = [] {};

	//2. [x] 捕捉上文出现的变量x,使用的方式是传值
	auto f2 = [x] {};

	//3. [=] 表示 值传递方式 捕获所有父作用域中的变量(包括this)
	auto f3 = [=] {};

	//4. [&x] 表示 引用传递 捕捉所有父作用域中的变量(包括this)
	auto f4 = [&x] {};

	//5. [&] 捕捉上文出现的所有变量,使用的方式是引用
	auto f5 = [&] {};

	//6. [this] , 表示值传递方式捕捉当前的this指针
	//在类内使用
	[this] {};
}

通常使用样例:

int main()
{
	// 省略参数列表和返回值类型,返回值类型由编译器推导为int
	int a = 3, b = 4;
	[=] {return a + 3; };
	
	// 省略了返回值类型,无返回值类型
	auto fun1 = [&](int c) {b = a + c; };
	fun1(10)
		cout << a << " " << b << endl;
		
	// 各部分都很完善的lambda函数
	auto fun2 = [=, &b](int c)->int {return b += a + c; };
	cout << fun2(10) << endl;
	
	// 复制捕捉x
	int x = 10;
	auto add_x = [x](int a) mutable { x *= 2; return a + x; };
	cout << add_x(10) << endl;
	return 0;
}

📝备注:

  1. 父作用域包含lambda函数的语句块
  2. 语法上捕捉列表可由多个捕捉项组成,并以逗号分割。
    [=, &a, &b]:以引用传递的方式捕捉变量a和b值传递方式捕捉其他所有变量
    [&,a, this]:值传递方式捕捉变量a和this引用传递捉其他变量。
  3. 捕捉列表不允许变量重复传递,否则就会导致编译错误
    [=, a]:=已经以值传递方式捕捉了所有变量,捕捉a重复
  4. lambda表达式之间不能相互赋值,即使看起来类型相同

3.4 底层的lambda

以一段代码案例说明:

//这是一个计算汇率的仿函数
class Rate
{
public:
	Rate(double rate) : _rate(rate)
	{}

	double operator()(double money, int year)
	{
		return money * _rate * year;
	}
private:
	double _rate;
};

int main()
{
	// 函数对象
	double rate = 0.015;
	Rate r1(rate);
	cout << r1(10000, 2) << endl;

	// 第一个lambda
	// 两个lambda的内容与上面的仿函数一样
	auto r2 = [rate](double monty, int year)->double
	{
		return monty * rate * year;
	};

	cout << r2(10000, 2) << endl;
	
	// 第二个lambda
	int x = 1, y = 2;
	auto r3 = [=](double monty, int year)->double
	{
		return monty * rate * year;
	};

	cout << r3(10000, 2) << endl;


	return 0;
}

对于上面的代码,我们在反汇编层面上就能很清晰的看到:
在这里插入图片描述
首先,对于生成仿函数对象并使用仿函数的过程,汇编先调用了仿函数的构造函数,再调用了仿函数的operator()函数

在这里插入图片描述
再看看第一个lambda,我们会发现它在汇编指令上与仿函数大致都相同,都是先调用了一个构造函数然后调用了一个operator(),并且这两个函数我们发现都是第一个lambda的。

在这里插入图片描述
第二个lambda也是相同的情况。

在这里插入图片描述
并且发现,lambda在捕获列表中捕获了rate之后,汇编也出现了形参入栈的代码,从这里看出,捕获的本质是给lambda的构造函数传参

在这里插入图片描述
还有一个细节,我这里的捕获列表[ rate ] 改成 [ = ] (捕获所有参数),但是入栈的参数却只有rate,这说明:就算是 [ = ] (捕获所有参数),对于函数体没有使用到的参数,lambda也是不会捕获的
在这里插入图片描述
这里我们让函数体再多使用一个x ,也确实发现 x 入栈了。

由此可以得出结论:lambda表达式本质上是匿名函数对象捕获的本质是给lambda的构造函数传参

4. function包装器

function包装器 也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。

作用:包装器可以包装 函数指针,仿函数,以及lambda。

int f(int a, int b)
{
	return a + b;
}

struct Functor
{
public:
	int operator() (int a, int b)
	{
		return a + b;
	}
};
int main()
{
	// 包装可调用对象
	// 函数名(函数指针)
	function<int(int, int)> func1 = f;
	// 仿函数
	function<int(int, int)> func2 = Functor();
	// lamber表达式
	function<int(int, int)> func3 = [](const int a, const int b)
		{return a + b; };
}

包装器要求:

  1. 需要包含头文件 < functional >
#include <functional>
  1. function声明:在<>内,()前为被包装函数的返回类型,()内为被包装函数的参数列表类型
 function<返回类型(参数列表类型)>
int f(int a, int b)
{
   return a + b;
}
//包装器func1包装返回类型为int,参数类型为int,int的函数
function<int(int, int)> func1 = f;

特殊场景:包装静态成员变量,与非静态成员变量

	class Plus
{
public:
	static int plusi(int a, int b)
	{
		return a + b;
	}
	double plusd(double a, double b)
	{
		return a + b;
	}
};
int main()
{
	// 包装静态成员函数
	function<int(int, int)> f4 = &Plus::plusi;
	cout << f4(1, 1) << endl;

	// 包装非静态成员函数
	function<double(Plus*, double, double)> f5 = &Plus::plusd;
	Plus pd;
	cout << f5(&pd, 1.1, 1.1) << endl;
	cout<< f5(Plus(), 1.1,1.1) <<endl;
}

📝备注 : 包装二者都要&。 包装非静态成员函数要多传一个 Plus * (类指针)作为function第一个参数类型,在调用包装器的时候,也要多传一个类对象(匿名对象也可以)作为第一个参数

5. bind包装器

bind用于实现原函数对象的参数顺序调整。

std::bind函数是一个函数模板,它就像一个函数包装器(适配器),受一个可调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表。一般而言,我们用它可以把一个原本接收N个参数的函数fn,通过绑定一些数,返回一个接收M个(M可以大于N,但这么做没什么意义)参数的新函数。同时,使std::bind函数还可以实现参数顺序调整等操作。

// 原型如下:
template <class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
// with return type (2)
template <class Ret, class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);

可以将bind函数看作是一个通用的函数适配器,它接受一个可调用对象(function对象或者一些函数对象),生成一个新的可调用对象来“适应”原对象的参数列表。

调用bind的一般形式:auto newCallable = bind(callable,arg_list);
其中,newCallable本身是一个可调用对象arg_list是一个逗号分隔的参数列表,应给定的callable的参数。当我们调用newCallable时,newCallable会调用callable,并给它arg_list中的参数。

arg_list中的参数可能包含形如**_n的名字**,其中n是一个整数,这些参数是**“占位符”,表示newCallable的参数**,它们占据了传递给newCallable的参数的“位置”。数值n表示生成的可调用对象中参数的位置:_1为newCallable的第一个参数,_2为第二个参数,以此类推 , _1, _2 … _n都保存在命名空间placeholders,在使用他们的时候需要指定类域。

//使用举例1
int Sub(int a, int b)
{
	return a / b;
}
int main(){
	// bind 本质返回的一个仿函数对象
	// 调整参数顺序(不常用)
	// _1代表第一个实参
	// _2代表第二个实参
	// ...

//表示绑定函数plus 参数分别由调用 func1 的第一,二个参数指定
function<int(int, int)> func1 = bind(Sub, placeholders::_1,
placeholders::_2);
	func1(1, 2);// 1/2 结果是0
	
//表示绑定函数plus 参数分别由调用 func1 的第二,一个参数指定
function<int(int, int)> func2 = bind(Sub, placeholders::_2,
placeholders::_1);
	func1=2(1, 2); // 2/1 结果是2
}

在这里插入图片描述
在这里插入图片描述

int Plus(int a, int b)
{
	return a + b;
}



//使用举例2,用于绑死参数
int main()
{
	// 调整参数个数 (常用)
	auto sub3 = bind(Sub, 100, _1);
	cout << sub3(5) << endl;

	auto sub4 = bind(Sub, _1, 100);
	cout << sub4(5) << endl;

	// 分别绑死第123个参数
	auto sub5 = bind(SubX, 100, _1, _2);
	cout << sub5(5, 1) << endl;

	auto sub6 = bind(SubX, _1, 100, _2);
	cout << sub6(5, 1) << endl;

	auto sub7 = bind(SubX, _1, _2, 100);
	cout << sub7(5, 1) << endl;

	function<double(Plus, double, double)> f6 = &Plus::plusd;
	Plus pd;
	cout << f6(pd, 1.1, 1.1) << endl;
	cout << f6(Plus(), 1.1, 1.1) << endl;

	// bind一般用于,绑死一些固定参数
	function<double(double, double)> f7 = bind(&Plus::plusd, Plus(), _1, _2);
	cout << f7(1.1, 1.1) << endl;

	//auto func1 = [](double rate, double monty, int year)->double {return monty * rate * year;};
	auto func1 = [](double rate, double monty, int year)->double {
		double ret = monty;
		for (int i = 0; i < year; i++)
		{
			 ret += ret * rate;
		}

		return ret - monty;
	};
}
class Sub
{
public:
int sub(int a, int b)
	{
	return a - b;
	}
};
int main()
{
	//使用举例3,绑定成员函数
	std::function<int(int, int)> func3 = std::bind(&Sub::sub, s,
		placeholders::_1, placeholders::_2);
	std::function<int(int, int)> func4 = std::bind(&Sub::sub, s,
		placeholders::_2, placeholders::_1);
	cout << func3(1, 2) << endl;
	cout << func4(1, 2) << endl;
}

本文就到这里,感谢你看到这里❤️❤️! 我知道一些人看文章喜欢静静看,不评论🤔,但是他会点赞😍,这样的人,帅气低调有内涵😎,美丽大方很优雅😊,明人不说暗话,要你手上的一个点赞😘!

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

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

相关文章

一款好用的研发项目管理软件能为公司带来什么?

一款高效的研发项目管理软件可以为公司带来竞争优势&#xff0c;将项目交付时间缩短45%&#xff0c;提高项目成功率至72%&#xff0c;提高研发效率&#xff0c;缩短上市周期。该软件含有丰富的数据分析功能&#xff0c;可以更好地掌握项目进度&#xff0c;发现和解决问题&#…

Unity教程(十)Tile Palette搭建平台关卡

Unity开发2D类银河恶魔城游戏学习笔记 Unity教程&#xff08;零&#xff09;Unity和VS的使用相关内容 Unity教程&#xff08;一&#xff09;开始学习状态机 Unity教程&#xff08;二&#xff09;角色移动的实现 Unity教程&#xff08;三&#xff09;角色跳跃的实现 Unity教程&…

仕考网:考外省公务员可以调回本地吗?

一般情况下&#xff0c;公务员岗位是固定不可随意更换的&#xff0c;因为每个职位都对应特定的职责和要求。一旦考到外地的岗位&#xff0c;想要调回本地几乎是不可能的。因为这样的操作可能导致职位空缺&#xff0c;进而需要通过公共招聘流程来填补&#xff0c;而不是简单地从…

场外期权交易:找到适合你的那一款

各位期权爱好者们&#xff01;今天咱们来聊聊在进行场外期权交易时&#xff0c;怎么去评估和选择适合自己风险承受能力的期权产品。 第一&#xff0c;你得对自己有个清楚的认识。想想看&#xff0c;你是那种激进型的冒险家&#xff0c;还是保守型的稳健派呢&#xff1f;了解自己…

PostgreSQL数据库内核(三):缓冲区管理器

文章目录 共享缓冲区基础知识逻辑读和物理读LRU算法和CLOCK时钟算法 共享缓冲区管理器结构共享缓冲表层共享缓冲区描述符层共享缓冲页层 共享缓冲区管理器工作流程初始化缓冲区读缓冲区淘汰策略共享缓冲区锁 共享缓冲区基础知识 通常数据库系统都会在内存中预留buffer缓冲空间…

elasticsearch集成springboot详细使用

1.es下载&配置 配置JVM 配置跨域 配置https和密码 2.es启动 .\elasticsearch.bat 或 后台启动&#xff1a; nohup ./bin/elasticsearch& 浏览器访问&#xff1a;https://localhost:9200 输入账户&#xff1a;elastic / 123456 3.重置es密码 .\elasticsearch-r…

C:指针和数组之间的关系-学习笔记

目录 闲话&#xff1a; 引言&#xff1a; 1、数组名的理解 2、指针访问数组 3、一维数组传参的本质 4、二级指针 5、指针数组 6、指针数组模拟二维数组 结语&#xff1a; 闲话&#xff1a; 指针这个模块更新的比较慢&#xff0c;主要是小编还得学习指针的知识点&#…

ubuntu18.04 设置静态地址

修改配置文件 sudo vim /etc/netplan/01-network-manager-all.yaml 代码如下&#xff1a; network: version: 2 renderer: NetworkManager ethernets: ens33: # 配置的网卡名称&#xff0c;可以使用ifconfig -a查看本机的网卡 dhcp4: no # 关闭动态IP设置 …

黑神画Ⅹ--自主人工智能代理:从概念到实际应用

自主人工智能代理通过独立执行任务并做出最终决策来改变技术。与传统人工智能不同&#xff0c;它们能够分析、规划、适应并从经验中学习。机器学习和自然语言处理的进步扩大了它们在个人助理、聊天机器人、管理系统和自动驾驶汽车中的应用&#xff0c;展示了它们在各个领域的潜…

springmail发送邮件如何实现邮件动态内容?

springmail发送邮件怎么样&#xff1f;springmail发信优化方法&#xff1f; SpringMail作为一个强大的邮件发送框架&#xff0c;提供了多种方式来实现邮件内容的动态生成。AokSend将探讨如何通过SpringMail发送邮件&#xff0c;并动态生成邮件内容以满足不同的需求。 springm…

【图像去雾系列】使用SSR/MSR/MSRCR/MSRCP/automatedMSRCR算法对单图像进行图像增强,达到去雾效果

目录 一 图像去雾算法概述 二 SSR/MSR/MSRCR算法 三 实践 一 图像去雾算法概述 近些年来,出现了众多的单幅图像去雾算法,其主要可以分为 3 类:基于图像增强的去雾算法、基于图像复原的去雾算法和基于 CNN 的去雾算法。 ▲基于图像增强的去雾算法 通过图像增强技术突出图…

“泰山众筹:革新消费模式“

亲爱的伙伴们&#xff0c;是否渴望探索一种集日常消费与财富增长于一体的创新方式&#xff1f;今天&#xff0c;让我带您领略泰山众筹的魅力&#xff0c;这一革命性的消费增值理念&#xff0c;将彻底颠覆您的消费体验&#xff0c;让每一笔支出都化作通往财富之路的基石。 ✨ 泰…

了解LVS,配置LVS

项目一、LVS 1.集群Cluster Cluster: 集群是为了解决某个特定问题将堕胎计算机组合起来形成的单个系统 LB&#xff1a;负载均衡 HA&#xff1a;高可用 HPC&#xff1a;高性能计算 2.分布式 分布式是将一个请求分成三个部分&#xff0c;按照功能拆分&#xff0c;使用微服…

MySQL的InnoDB的页里面存了些什么 --InnoDB存储梳理(三)

文章目录 创建新表页的信息新增一条数据根据页号找数据信息脚本代码py_innodb_page_info根据地址计算页号根据页号计算起始地址 主要介绍表空间索引页里面有哪些内容&#xff0c;数据在表空间文件里面是怎么组织的 创建新表页的信息 CREATE TABLE test8 (id bigint(20) NOT N…

Nginx Web UI 部署

目录 1. 安装Docker 2. 拉取镜像 3. 启动程序 4. 访问测试 1. 安装Docker 准备一台虚拟机&#xff0c;关闭防火墙和selinu&#xff0c;进行时间同步 下载docker并配置加速器 # step 1: 安装必要的一些系统工具 sudo yum install -y yum-utils device-mapper-persisten…

8B 端侧小模型 | 能力全面对标GPT-4V!单图、多图、视频理解端侧三冠王,这个国产AI开源项目火爆全网

这两天&#xff0c; Github上一个 国产开源AI 项目杀疯了&#xff01;一开源就登上了 Github Trending 榜前列&#xff0c;一天就获得将近600 star。 这个项目就是国内大模型四小龙之一面壁智能最新大打造的面壁「小钢炮」 MiniCPM-V 2.6 。它再次刷新端侧多模态天花板&#xf…

Cobalt Strike 4.8 用户指南-第一节-Cobalt Strike介绍及安装

一、欢迎使用Cobalt Strike Cobalt Strike 是一个用于对手模拟和红队行动的平台。用于执行有针对性的攻击并模拟高级威胁行为者的后渗透行动。本节介绍 Cobalt Strike 功能集支持的攻击过程。本手册的其余部分将详细讨论了这些功能。 # 概述 图中的 Intrumentation & Tel…

数据重塑之数据去重

下面内容摘录自&#xff1a; 4章7节&#xff1a;用R做数据重塑&#xff0c;数据去重和数据的匹配-CSDN博客文章浏览阅读23次。数据重塑是数据分析和数据清洗中的重要步骤&#xff0c;其中包括数据去重和数据匹配。理解这两个概念以及它们的实现方法对于有效处理和分析数据至关重…

告别转换难题,四款PDF转CAD工具分享

CAD很难搞&#xff0c;将PDF转换为CAD更难搞&#xff0c;想要快速且完整的将PDF文件转换为CAD&#xff0c;自然不是靠一点点的复制重做&#xff0c;直接用PDF转CAD工具就能搞定。那用什么工具呢&#xff1f;我这就给你们捋捋几个神器是怎么帮我们搞定这个难题的。 1.PDF365在线…

milvus helm k8s开启权限管理,attu管理

version:2.4.5 apiVersion: v1 kind: ConfigMap metadata: name: my-release-milvus 该configMap 添加 &#xff0c;然后重启milvus 集群可生效 user.yaml: |-common:security:authorizationEnabled: true或者直接在value.yaml 中添加该配置 extraConfigFiles:user.yaml: |com…