C++ 11 【可变参数模板】【lambda】

news2024/11/23 4:46:13

💓博主CSDN主页:麻辣韭菜💓

⏩专栏分类:C++修炼之路

🚚代码仓库:C++高阶🚚

🌹关注我🫵带你学习更多C++知识
  🔝🔝

目录

 

前言

 一、新的类功能 

1.1默认成员函数——移动构造、移动赋值

1.2强制生成默认函数的关键字default:

1.3const延长生命周期的问题

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

1.5 其他新功能

缺省值

 委托构造

二、可变参数模板 

利用递归函数展开

逗号表达式展开参数包

三、Lambda 

lambda表达式

 lambda表达式语法

 1.捕捉列表

函数对象与lambda表达式 


 

前言

上篇重点讲解了右值引用,本篇的可变参数模板Lambda也是11里面非常有用的。如果学会这两个以后编程会感觉非常的爽。废话不多说直接开始!!!

 一、新的类功能 

1.1默认成员函数——移动构造、移动赋值

在C++11后,类又新增了两个默认成员函数:移动构造移动赋值

针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下:
  
  • 如果你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任 意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类 型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造, 如果实现了就调用移动构造,没有实现就调用拷贝构造。

  • 如果你没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中 的任意一个,那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数,对于内 置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋 值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造 完全类似)
  • 如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。
#include "String.h"

class Person
{
public:
	Person(const char* name = "", int age = 0)
		:_name(name)
		, _age(age)
	{}
	/*Person(const Person& p)
 
	:_name(p._name)
	,_age(p._age)
	{}*/
	/*Person& operator=(const Person& p)
	{
	if(this != &p)
	{
	_name = p._name;
	_age = p._age;
	}
	return *this;
	}*/
	/*~Person()
	{}*/

private:
	gx::string _name;
	int _age;
};
int main()
{
	Person s1;
	Person s2 = s1;
	Person s3 = move(s1);
	Person s4;
	s4 = move(s2);
	return 0;
}

如果把上面代码的拷贝构造 赋值重载、析构的任意一个代码注释取消注释就会得到下面结果

为什么我们一但自己写了默认成员函数编译器就不会自己生成移动构造、赋值? 

 如果我们实现了 析构、拷贝构造、赋值重载,就证明当前的类中涉及到了 动态内存管理,是需要自己进行 深拷贝 的,编译器无能为力,移动语义 也应该根据自己的实际场景进行设计,所以编译器就没有自动生成

 那如果有些场景就需要我们自己写拷贝构造、析构、赋值重载这些函数那怎么办?

1.2强制生成默认函数的关键字default:

Person(Person&& p) = default;
Person& operator=(Person&& p) = default;
	

C++11后STL中所有容器都增加了移动构造和移动赋值

插入系列的函数也同样增加了右值的版本。

 其他容器详情请看官网cplusplus.com 

1.3const延长生命周期的问题

插入函数之所以会延长生命周期

当您创建一个临时对象并将其作为参数传递给函数时,这个临时对象的生命周期通常只在表达式中有效。一旦表达式结束,临时对象就会被销毁。但是,如果这个临时对象被传递给一个需要更长时间使用它的函数,比如一个需要对对象进行修改的函数,那么就需要延长这个临时对象的生命周期。

在C++中,如果一个函数的参数是一个const类型,这意味着函数不会修改这个对象。但是,如果这个参数是通过引用传递的,那么即使它是const,它仍然需要在函数调用期间保持有效,以便函数可以访问它。这就是所谓的生命周期延长。

既然可以延长对象生命周期那是不是也可以像这样?下图这样返回的对象加const

从结果来看显然是不可以的。出现了野引用的问题。 所以说引用也不是安全的。

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

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

 

在person类中我们在拷贝构造函数后面加 =delete 就无法再使用这个这个函数。

注意:delete这个关键字只对默认成员函数有效

那什么样的类是不希望其他人来调用的它的默认成员函数?

比如IO流

 

每个IO流对象的缓冲区都是不一样的,随意拷贝都会造成资源混乱。

1.5 其他新功能

在C++98中,类中的内置类型是不对初始化的。而在C++11中出现了缺省参数 可以给类的成员给缺省值。

缺省值

 没有缺省值我们得到_a的值是随机值。

给定缺省值 1

 委托构造

什么是委托构造? 简单来说就是一个构造函数可以复用其他构造函数

class Person
{
public:
	Person(const char* name, int age)
		:_name(name)
		, _age(age)
	{}

	
	Person(const char* name)
		:Person(name, 18) // 委托构造
	{}

private:
	gx::string _name; // 自定义类型
	int _age = 1;		   // 内置类型
};

int main()
{
	Person s1("张三");

	return 0;
}

 

这个委托构造了解一下就行了,说白了还是要调用构造函数。 

二、可变参数模板 

相比C++98的模板参数,C++11模板参数变成了不是固定的,可以接受任意个类型。和printf函数的可变参数列表是类似的。只是这里的模板参数变成了类型。

下面就是一个基本可变参数的函数模板 

// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}

如果要知道参数包中个数怎么解决?

template<class ...Args>
void ShowList(Args...args)
{
	cout << sizeof...(args) << endl;
}
int main()
{
	ShowList(1, 'x');
}

 如何解析出参数包里面的值?

由于语法不支持使用args[i]这样方式获取可变参数,所以我们的用一些奇招来一一获取参数包的值。

利用递归函数展开

void ShowList()
{
	cout << endl;
}
template <class T ,class ...Args>
void ShowList(const T& val, Args ...args)
{
	cout << __FUNCTION__ << "(" << sizeof...(args) << ")" << endl;
	cout << val << endl;
	ShowList(args...); //语法规定...必须在后面
}
int main()
{
	ShowList(1, 'A', std::string("sort"));
	return 0;
}

 上面结果确实调用3次ShowList这个函数再加上无参ShowList。

能不能不用模板参数T? 我就想直接用可变模板参数包?可以 。直接再套一层。

void _ShowList()
{
	cout << endl;
}
template <class T, class ...Args>
void _ShowList(const T& val, Args... args)
{
	cout << __FUNCTION__ << "(" << sizeof...(args) << ")" << endl;

	cout << val  << endl;
	_ShowList(args...);
}
template <class ...Args>
void ShowList(Args... args)
{
	_ShowList(args...);
}

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

	return 0;
}

逗号表达式展开参数包

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);
	ShowList(1, 'A');
	ShowList(1, 'A', std::string("sort"));
	return 0;
}

在上篇的初始化列表 我们知道C++11在arr这个数组创建时,会初始化 列表里面的内容。实现的关键是逗号表达式。我们知道逗号表达式会按顺序执行逗号前面的表达式。 

编译器通过解析变成下面表达式

{(printarg(args), 0)...}将会展开成((printarg(arg1),0), (printarg(arg2),0), (printarg(arg3),0), etc... )

为什么要写出成 (Print(args), 0) 的形式?
这是一个逗号表达式,目的是让整个式子最终返回 0,用于初始化 arr 数组

 如果不想加0也是可以的。

 可变参数模板应用场景又是什么?线程

我们知道C语言中回调函数的传参。C的方案是用的void*   而C++11的线程库用可变参数模板对void* 这个指针进行封装。通过可变参数模板,就可以快乐的传递任何参数。剩下的事交给编译器来干。

可变参数包还可以用于优化STL容器中插入函数。

以容器list为例:

int main()
{
	list<gx::string> lt;
	gx::string s1("1111");
	lt.push_back(s1);
	lt.emplace_back(s1);
	return 0;
}

 

对于是深拷贝的类是没区别的。push_back和emplace_back二者没什么区别。

我们在来看看浅拷贝的类有没有影响

#include "Date.h"
int main()
{
	/*list<gx::string> lt;
	gx::string s1("1111");
	lt.push_back(s1);
	lt.emplace_back(s1);*/
	list<Date> lt2;
	Date d1(2024, 5, 20);
	Date d2(2024, 5, 21);
	lt2.push_back(d1);
	lt2.emplace_back(d2);

	return 0;

}

也是没有差别。

那如果是 d1\d2是右值那?

也是没区别

但是是下面这种就有区别了

emplace_back直接就是构造。这是因为可变参数包在参数传递的过程中,参数包不会展开。直到构造函数才展开。其实这里可以理解成(2023,5,28)它不是一个匿名对象,在参数包的眼里它实际是3个整型。

再比如 下面这个。

有了可变参数包。编译器直接识别为const char* 的字符串。而不是一个匿名对象。 

结论:无脑用emplace_back就行。

三、Lambda 

在C++11之前,我们如果要对数据进行排序怎么做?用std::sort。

#include <algorithm>
#include <functional>
int main()
{
	int array[] = { 4,1,8,5,3,7,0,9,2,6 };
	// 默认按照小于比较,排出来结果是升序
	std::sort(array, array + sizeof(array) / sizeof(array[0]));
	// 如果需要降序,需要改变元素的比较规则
	std::sort(array, array + sizeof(array) / sizeof(array[0]), greater<int>());
	return 0;
}

如果我们要排序的是自定义类型那就需要用到仿函数。

struct Goods
{
	string _name;  // 名字
	double _price; // 价格
	int _evaluate; // 评价
	Goods(const char* str, double price, int evaluate)
		:_name(str)
		, _price(price)
		, _evaluate(evaluate)
	{}
};
struct ComparePriceLess
{
	bool operator()(const Goods& gl, const Goods& gr)
	{
		{
			return gl._price < gr._price;
		
		}	
	}
};
struct ComparePriceGreater
{
	bool operator()(const Goods& gl, const Goods& gr)
	{
		return gl._price > gr._price;
	}
};
int main()
{
	vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,
   3 }, { "菠萝", 1.5, 4 } };
	sort(v.begin(), v.end(), ComparePriceLess());
	
	sort(v.begin(), v.end(), ComparePriceGreater());
}

 生活中商品太多了难道每一种商品的排序都要写相应的仿函数,是不是有点太麻烦?,假设我们不以价格来进行排序。现在要求按水果的名字排序,是不是又要重写一个仿函数?

有没有一种办法一劳永逸?在C++11推出了lambda 

lambda表达式

int main()
{
	vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,
   3 }, { "菠萝", 1.5, 4 } };
	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
		return g1._price < g2._price; });
	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
		return g1._price > g2._price; });
	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
		return g1._evaluate < g2._evaluate; });
	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
		return g1._evaluate > g2._evaluate; });
}

 

上述代码就是使用C++11中的lambda表达式来解决,可以看出lambda表达式实际是一个匿名函

数。

 lambda表达式语法

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

我们先来一个简单的lambda语法

	[](int x, int y)->int {return x + y; };

 如果函数体的语句较多我们也是可以这样写代码的

	[](int x, int y)->int 
	{
		return x + y; 
	};

我们如果要调用这个函数对象太长了,我们可以加 auto 

	auto add1 = [](int x, int y)->int 
	{
		return x + y; 
	};
	cout << add1(1, 2) << endl;

最简单的lambda表达式

//最简单的lambda表达式 该lambda表达式没有任何意义
	[] {};

 1.捕捉列表

写一个交换函数。

​
	int x = 1, y = 0;
	auto swap1 = [](int& rx, int& ry)
	{
		int tmp = rx;
		rx = ry;
		ry = tmp;
	};
	swap1(x, y);
	cout << x << " " << y << endl;

参数列表是可以省略的

省略参数时,我们就要用捕捉列表

 

这时我们可以在参数列表后面加入关键字,mutable//异变 但是没什么用

 

 还是没有交换

上面的这种捕捉方式叫做传值捕捉,传值具有常性,不能修改 这时我们需要用到引用捕捉

这里的引用捕捉就很坑,不注意看还以为是取地址!! 

 

如果参数太多怎么办?难道要在捕捉列表中一个一个的捕捉吗?当然不用。我们直接全部引用捕捉

//全部引用捕捉
auto swap2 = [&]() 
	{
		int tmp = x;
		x = y;
		y = tmp;
	};

当然还有其他的捕捉方式

 //混合捕捉
	auto func1 = [&x, y]()
	{
		//...
	};



	 //全部传值捕捉
	auto func3 = [=]()
	{
		//...
	};

	 //全部引用捕捉,x传值捕捉
	auto func4 = [&, x]()
	{
		//...
	};

 这时我们就可以用lambad来创建线程

int main()
{
	int n1, n2;
	cin >> n1 >> n2;
	thread t1([n1]( int num)
		{
			for (int i = 0; i < n1; i++)
			{
				cout << "线程:" << num << " " << i << endl;
			}
			cout << endl;
		},1);
	thread t2([n2](int num)
		{
			for (int i = 0; i < n2; i++)
			{
				cout << "线程:" << num << " " << i << endl;
			}
			cout << endl;
		}, 2);
	t1.join();
	t2.join();

	return 0;
}

如果要m个线程分别打印n次如何操作? 这里我们可以利用vector 把每个线程放进vector这个容器中。

#include <vector>
int main()
{
	int m, n;
	cin >> m >> n;
	vector<int> arr;
	arr.push_back(m);
	arr.push_back(n);
	
	vector<thread> vthds(m);
	for (int i = 0; i < arr[0]; i++)
	{
		
		
		vthds[i] = thread([i,arr]()
			{
				for (int j = 0; j< arr[1]; j++)
				{
					cout << "线程:" << i << " " << j << endl;
				}
				cout << endl;
			});
		
	}
	for (auto& t : vthds)
	{
		t.join();
	}
	return 0;
}

 当然这个打印会错乱,那是因为没有加锁导致线程串行。关于锁的问题我们在后序线程库在详细讲解。

   lambda 能不能相互赋值?
void (*PF)();
int main()
{
     auto f1 = []{cout << "hello world" << endl; };
     auto f2 = []{cout << "hello world" << endl; };
     //f1 = f2;   // 编译失败--->提示找不到operator=()
    // 允许使用一个lambda表达式拷贝构造一个新的副本
     auto f3(f2);
     f3();
     // 可以将lambda表达式赋值给相同类型的函数指针
     PF = f2;
     PF();
     return 0;
}

总结: 

捕捉列表描述了上下文中那些数据可以被 lambda 使用 ,以及 使用的方式传值还是传引用
[var] :表示值传递方式捕捉变量 var
[=] :表示值传递方式捕获所有父作用域中的变量 ( 包括 this)
[&var] :表示引用传递捕捉变量 var
[&] :表示引用传递捕捉所有父作用域中的变量 ( 包括 this)
[this] :表示值传递方式捕捉当前的 this 指针
注意:
a. 父作用域指包含 lambda 函数的语句块
b. 语法上捕捉列表可由多个捕捉项组成,并以逗号分割
比如: [=, &a, &b] :以引用传递的方式捕捉变量 a b ,值传递方式捕捉其他所有变量
[& a, this] :值传递方式捕捉变量 a this ,引用方式捕捉其他变量
c. 捕捉列表不允许变量重复传递,否则就会导致编译错误
比如: [=, a] = 已经以值传递方式捕捉了所有变量,捕捉 a 重复

d. 在块作用域以外的 lambda 函数捕捉列表必须为空
e. 在块作用域中的 lambda函数能捕捉父作用域中局部变量。
f. lambda 表达式之间不能相互赋值 ,即使看起来类型相同

函数对象与lambda表达式 

函数对象,又称为仿函数,即可以想函数一样使用的对象,就是在类中重载了 operator() 运算符的
类对象。

 lambda的大小是多大?

要清楚这个问题我们需要通过汇编

先搞一个函数对象和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.49;
	Rate r1(rate);
	r1(10000, 2);

	// lambda
	auto r2 = [=](double monty, int year)->double {return monty * rate * year; };
	r2(10000, 2);

	auto f1 = [] {cout << "hello world" << endl; };
	auto f2 = [] {cout << "hello world" << endl; };

	//f1 = f2;

	return 0;
}

 

f1 和 f2通过汇编我们发现他们两个类名不同,类名不同怎么相互赋值?

这时候我们就能回答大小为什么是1了

在C++中,sizeof运算符用来确定一个类型或对象在内存中的大小。对于一个lambda表达式,sizeof返回的是这个lambda表达式对象在内存中占用的大小。

在x86 32位架构上,指针通常是4字节大小。因此,如果你的lambda表达式没有捕获任何局部变量或外部变量(或者只捕获了通过引用捕获的变量),那么lambda表达式的大小很可能是1字节,这是因为:

  1. Lambda表达式可能被编译器优化为一个很小的函数对象,它只包含一个指向其代码的指针。
  2. 在某些编译器实现中,lambda表达式可能被优化为一个空的结构体,其中只包含一个指向其代码的指针,因此sizeof返回1,表示空结构体的大小。

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

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

相关文章

龙迅#LT8711H支持TYPE-C/DP/EDP转HDMI功能应用,分辨率支持 1080p@60Hz,芯片内置固件!

1. 概述 LT8711H是一款高性能 Type-C/DP1.2/EDP 转 HD-DVI1.3 转换器&#xff0c;设计用于将 USB Type-C 源或 DP1.2 源连接到 HD-DVI1.3 接收器。 该LT8711H集成了符合 DP1.2 标准的接收器和符合 HD-DVI1.3 标准的发射器。此外&#xff0c;还包括两个用于 CC 通信的 CC 控制器…

如何去掉IDEA中烦人的警告波浪线

有时候想去掉idea中那些黄色的红色的warning波浪线&#xff0c;这些不是错误&#xff0c;并不影响执行&#xff0c;一直显示显得让人很烦躁&#xff0c;去"Editor" -> "Inspections"中一个个设置很麻烦。 可以通过设置代码检测级别来降低代码检查的严格…

26-unittest之装饰器(@classmethod)

unittest中的setUp可以在每个测试方法运行前执行&#xff0c;有效的减少了代码量。但有个弊端&#xff0c;比如打开浏览器操作&#xff0c;执行每个测试方法前都要重新打开一次&#xff0c;这样就会浪费很多时间。 是否可以只打开一次浏览器&#xff0c;执行完所有的测试方法后…

星火秘境游戏开发链游app定制开发源码部署

星火秘境是一款神秘而充满冒险的游戏&#xff0c;开发这样一款游戏需要综合考虑多个方面&#xff0c;包括游戏设计、美术设计、程序开发、音效制作等。下面我将简要介绍一下游戏开发和链游app搭建的一般流程&#xff1a; 游戏设计&#xff1a; 确定游戏类型&#xff1a;星火秘…

集团性企业数字化建设与经营管理解决方案

一、集团企业经营管理存在的问题 大型集团企业在内部财务经营管理过程中普遍存在五个方面的脱节问题。 1、战略与执行脱节&#xff1a;战略与执行“两张皮”&#xff0c;80%的企业管理者表示&#xff0c;战略执行过程中缺乏监督管理的系统或平台&#xff0c;造成执行结果大打…

手机怎么压缩视频?归纳了三种快速压缩方案

手机怎么压缩视频&#xff1f;在数字时代&#xff0c;手机已经成为我们记录生活的重要工具&#xff0c;而视频作为其中的一种主要形式&#xff0c;更是占据了极大的存储空间。然而&#xff0c;随着手机拍摄的视频越来越多&#xff0c;如何高效压缩视频以节省存储空间&#xff0…

什么无线领夹麦克风音质最好?领夹麦克风品牌排行榜前十名推荐

​在当今的数字化浪潮中&#xff0c;个人声音的传播和记录变得尤为重要。无论是会议中心、教室讲台还是户外探险&#xff0c;无线领夹麦克风以其卓越的便携性和连接稳定性&#xff0c;成为了人们沟通和表达的首选工具。面对市场上琳琅满目的无线麦克风选择&#xff0c;为了帮助…

OpenCV引入QT编译

OpenCV引入QT编译 为什么要引入QT编译编译方式 Reference: OpenCV 配置选项参考文档 网上实在找不到对应教程&#xff0c;在此做个记录。 为什么要引入QT编译 在没引入QT前&#xff0c;没有上述工具栏。 可以显示当前像素位置的像素值。 可以缩放查看每一个像素的大小。这对…

win10系统下WPS工具显示灰色全部用不了,提示登录

如果你在写文档或使用excel时发现导航栏的工具全部使用不了&#xff0c;弹出是需要您登录&#xff0c;可以通过以下操作不用登录。 按照 1&#xff08;搜索框&#xff09;—> 2&#xff08;应用&#xff09;—> 3&#xff08;WPS Office&#xff09;点鼠标左键—> 4&a…

巧用docker+jmeter快速实现分布式百万级并发

分享背景 碰到的问题&#xff1a; 一个JMeter实例可能无法产生足够的负载来对你的应用程序进行压力测试&#xff5e; 解决办法&#xff1a; 1、修改jmeter配置文件里的内存堆 2、引入jmeter分布式压测 带来的问题&#xff1a; 如果我们要做分布式负载测试–我们需要1个…

二叉树的顺序结构(堆的实现)

前言 普通的二叉树是不适合用数组来存储的&#xff0c;因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结 构存储。 现实中我们通常把堆 ( 一种二叉树 ) 使用顺序结构的数组来存储&#xff0c;需要注意的是这里的堆和操作系统 虚拟进程地址空间中的堆是两回事&…

Unity3D获得服务器时间/网络时间/后端时间/ServerTime,适合单机游戏使用

说明 一些游戏开发者在做单机游戏功能时&#xff08;例如&#xff1a;每日奖励、签到等&#xff09;&#xff0c;可能会需要获得服务端标准时间&#xff0c;用于游戏功能的逻辑处理。 问题分析 1、自己如果有服务器&#xff1a;自定义一个后端API&#xff0c;客户端按需请求…

性能狂飙:SpringBoot应用优化实战手册

在数字时代&#xff0c;速度就是生命&#xff0c;性能就是王道&#xff01;《极速启航&#xff1a;SpringBoot性能优化的秘籍》带你深入SpringBoot的内核&#xff0c;探索如何打造一个飞速响应、高效稳定的应用。从基础的代码优化到高级的数据库连接池配置&#xff0c;再到前端…

怎么用PHP语言实现远程控制两路照明开关

怎么用PHP语言实现远程控制两路开关呢&#xff1f; 本文描述了使用PHP语言调用HTTP接口&#xff0c;实现控制两路开关&#xff0c;两路开关可控制两路照明、排风扇等电器。 可选用产品&#xff1a;可根据实际场景需求&#xff0c;选择对应的规格 序号设备名称厂商1智能WiFi墙…

WPF国际化的最佳实践

WPF国际化的最佳实践 1.创建项目资源文件 如果你的项目没有Properties文件夹和Resources.resx文件&#xff0c;可以通过右键项目-资源-常规-添加创建或打开程序集资源 2.添加国际化字符串 打开Resources.resx文件&#xff0c;添加需要翻译的文本字符&#xff0c;并将访问修…

Go 1.19.4 切片与子切片-Day 05

1. 切片 1.1 介绍 切片在Go中是一个引用类型&#xff0c;它包含三个组成部分&#xff1a;指向底层数组的指针&#xff08;pointer&#xff09;、切片的长度&#xff08;length&#xff09;以及切片的容量&#xff08;capacity&#xff09;&#xff0c;这些信息共同构成了切片的…

el-input添加clearable属性 输入内容时会直接撑开

<el-inputclearablev-if"item.type number || item.type text":type"item.type":placeholder"item.placeholder":prefix-icon"item.icon || "v-model.trim"searchform[item.prop]"></el-input>解决方案 添加c…

inflight 守恒拥塞控制的稳定性

只要系统形成 E_best max(bw / delay) 共识&#xff0c;系统就是稳定的。 设两条流 f1&#xff0c;f2 共享瓶颈链路&#xff0c;用 cwnd 约束 inflight&#xff0c;其 cwnd 分别为 x&#xff0c;y&#xff0c;用简单的微分方程建模&#xff1a; d x d t c − b ∗ x − a ∗…

TCP/IP(网络编程)

一、网络每一层的作用 &#xff0a;网络接口层和物理层的作用&#xff1a;屏蔽硬件的差异&#xff0c;通过底层的驱动&#xff0c;会提供统一的接口&#xff0c;供网络层使用 &#xff0a;网络层的作用&#xff1a;实现端到端的传输 &#xff0a;传输层:数据应该交给哪一个任…

区块链(Blockchain)调查研究

文章目录 1. 区块链是什么&#xff1f;2. 区块链分类和特点3. 区块链核心关键技术3.1 共识机制3.2 密码学技术3.4 分布式存储3.5 智能合约 4. 区块链未来发展趋势5. 区块链 Java 实现小案例 1. 区块链是什么&#xff1f; 区块链是分布式数据存储、点对点传输、共识机制、加密算…