【C++】C++入门第二课(函数重载 | 引用 | 内联函数 | auto关键字 | 指针空值nullptr)

目录

前言

函数重载

概念

重载函数的条件

C++支持重载函数的原理--名字修饰

引用

概念

特性

常引用(const引用)

使用场景

传值,传引用效率比较

引用和指针的区别

内联函数

概念

特性

auto关键字(C++11)

auto简介

auto的使用规则

指针空值nullptr(C++11)

C++98中的指针空值

结语


前言

这里是C++入门的第二课,主要还是补补C语言之前遗留下来的缺陷,在学习完本篇博客的内容之后,就可以进入激动人心的类和对象环节了。看过标题大家应该也知道要讲什么,话不多说,咱们开始今天的内容。

函数重载

自然语言中,一个词可以有多重含义,人们可以通过上下文来判断该词真实的含义,即该词被重
载了。比如:以前有一个笑话,国有两个体育项目大家根本不用看,也不用担心。一个是乒乓球,一个是男足。前者是“谁也赢不了!”,后者是“谁也赢不了!”

函数重载,简单来说就是允许定义同名函数,可以通过所传参数的数量和类型来判断运行哪一个函数,接下来咱们看具体定义。

概念

函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这
些同名函数的形参列表(参数个数类型类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。

注:形参列表(参数 1.类型 2.个数或或 3.类型顺序)不同,都可以是重载的条件。

重载函数的条件

1.重载参数类型不同

#include<iostream>
using namespace std;
int Add(int left, int right)
{
	cout << "int Add(int left, int right)" << endl;
	return left + right;
}
double Add(double left, double right)
{
	cout << "double Add(double left, double right)" << endl;
	return left + right;
}

int main()
{
	cout << Add(1, 2) << endl;
	cout << Add(1.1, 1.2) << endl;
	return 0;
}

代码的形参一份为整型,另一份为浮点型。当调用函数传入的是整型数据时,调用第一份声明形参为整型的代码;如果调用函数传入的是浮点型数据,则调用声明形参为浮点型的代码。 

2.参数个数不同

#include<iostream>
using namespace std;
void f()
{
	cout << "f()" << endl;
}
void f(int a)
{
	cout << "f(int a)" << endl;
}

int main()
{
	f();
	f(3);
	return 0;
}

当调用函数不传入参数时,调用无参版的函数;当调用函数传入参数时,调用含参的重载函数。 

3.参数类型顺序不同

#include<iostream>
using namespace std;
void f(int a, char b)
{
	cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
	cout << "f(char b, int a)" << endl;
}

int main()
{
	f(1, 'm');
	f('m', 1);
	return 0;
}

运行时会自动匹配最合适的重载函数。 

满足上述三点条件中任意一点重载函数都定义在同一作用域内,就可以构成重载。 

 注:返回值不同不能构成重载,只能通过上述讲的参数不同进行重载。

如果返回值可以重载,会造成巨大的问题,程序无法从调用处区分最终函数运行结束后会产生什么类型的返回值,因此用返回值区分重载本身就是不可行不可靠的。

调用函数查找匹配的时候,编译器自动会匹配最符合的重载函数。

C++支持重载函数的原理--名字修饰

为什么C++支持函数重载,而C语言不支持函数重载呢?
在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接

C++支持函数重载的原理,很大程度上依赖于“名字修饰”(name Mangling)的过程。这种机制使得编译器能够区分同名但参数列表不同的函数。

名字修饰是编译器自动进行的一种处理过程,它将C++源码中的函数名和变量名转换成包含更多信息的唯一标识符。这些信息通常包括函数的参数类型,参数数量等,甚至可能包括所属的类名(对于类成员函数),通过这种方式,每个重载的函数都会被赋予一个独一无二的名字,确保在最后链接的时候能够区分出它们。

C++中允许函数重载,也就是允许一个作用域中存在多个同名函数,只要他们的参数列表不同,在编译成目标代码之后,所有的函数名都能通过修饰出的不同的名字而区分开来,确保了每个函数的调用都能显示的映射到正确的函数体上。名字修饰通过在函数名中编码函数类型等信息,实现了这一点。

名字修饰实例

下面有一组重载函数:

void Fun(int a, int b);
void Fun(double a, double b);

经过编译器的处理修饰,这些函数最后可能被处理为(不同编译器修饰规则不同,具体修饰结果取决于编译器)

  • Fun(int, int) 可能被修饰为 _23Funii_
  • Fun(double, double) 可能被修饰为 _23Fundd_

通过这种修饰方式,尽管这两个函数的名字相同,但在编译器的处理后它们获得了不同的名字,使得编译后的代码能够轻松识别出不同的重载函数。

名字修饰使得C++能够有效的支持重载和模板等功能,虽然这种机制对程序员来说是透明的,但理解其背后的原理对于深入掌握C++很有帮助。同时,这也是C++与C语言的一个重要区别:C语言不支持重载函数,大部分原因在于它没有采用类似的名字修饰机制。

我们来看看在Linux环境下用不同编译器编译相同代码对函数名的处理结果:

采用C语言编译器编译后的结果

在Linux下,采用gcc编译完成后,函数名字的修饰没有发生改变

采用C++编译器编译的结果

在linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参数类型信息添加到修改后的名字中
通过这里就理解了C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修
饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。
到这里应该可以感受到,在C++中,编写代码方便的原因是大部分工作C++的编译器都帮你做掉了。这也使得匹配类型变慢,编译速度降低。但代码运行的是指令,这种匹配只会影响一部分编译速度,而不用担心会拖累代码运行的速度

引用

概念

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空
间,它和它引用的变量共用同一块内存空间。
比如:一个男孩,在家中被父母叫“儿子”,在学校里被叫“同学”。虽然称呼不同,但是最终的对象还是同一个人。引用就是一种给变量取别名的语法。

类型&  引用变量名(对象名) = 引用实体;

#include<iostream>
using namespace std;
int main()
{
	int a = 8;
	int& b = a;//定义引用类型
	cout << a << " " << b << endl;
	++a;
	cout << a << " " << b << endl;
	++b;
	cout << a << " " << b << endl;
    cout << &a << " " << &b << endl;
	return 0;
}

要注意的是,引用的类型必须和引用实体同种类型的。

特性

引用有几点特性:

  1. 引用在定义时必须要初始化
  2. 一个变量可以有多个引用
  3. 一旦引用一个实体,不能再引用其他实体(就跟变量不能重定义一样)
void TestRef()
{
	int a = 10;
	// int& ra; 这句语句在引用时未初始化,报错
	int& b = a;
	int& c = a;
	int& d = b;
	// int& d = a; 报错,重定义
}

常引用(const引用)

关于常引用,就是用const修饰的引用,只需要记住,在使用引用的时候,权限可以不变和缩小,但一定不能放大。这里的规则和指针极其相似。

void TestConstRef()
{
	const int a = 10;
	//int& ra = a; 该语句编译时会出错,a为常量
	const int& ra = a;
	// int& b = 10; 该语句编译时会出错,b为常量
	const int& b = 10;
	double d = 12.34;
	//int& rd = d; 该语句编译时会出错,类型不同
	const int& rd = d;//const引用支持不同类型
}

注:创建引用类型前加上const,可以在不同类型之间取别名,第三个案例中的rd里面的值为12。

使用场景

1.做参数

在用C语言中编写函数交换两个变量之间的值时,需要传指针才能实现,这时因为C语言函数传参是传值传递,传过去的只是原数据的拷贝,只有通过传址直接找到存储数据空间的地址,才能从函数内部影响到外部的数据。C++的引用成功的解决了此类问题,也就是常说的传引用。

#include<iostream>
using namespace std;

void Swap(int& left, int& right)
{
	int temp = left;
	left = right;
	right = temp;
}

int main()
{
	int a = 5;
	int b = 8;
	cout << a << " " << b << endl;
	Swap(a, b);
	cout << a << " " << b << endl;
	return 0;
}

引用也是极大的减少了代码中指针的使用比例,也是补了C语言指针难用复杂的一个大坑。

2.做返回值

#include<iostream>
using namespace std;
int& Count()
{
	static int digit = 0;
	digit++;
	return digit;
}
int main()
{
	int& d = Count();
	cout << d << endl;
	Count();
	cout << d << endl;
	d++;
	cout << d << endl;
	return 0;
}

main函数中的变量d和函数内部的digit使用的空间是同一块,当里面digit++的时候外面d的值也会++,得益于Count函数的传引用返回static静态变量的创建(静态变量的生命周期存在于整个程序运行当中),这里传引用返回是可行的。

再来看这样一个案例,下面的代码中想想会输出什么结果?

#include<iostream>
using namespace std;
int& Add(int a, int b)
{
	int c = a + b;
	return c;
}
int main()
{
	int& ret = Add(1, 2);
	Add(3, 4);
	cout << "Add(1, 2) is :" << ret << endl;
	return 0;
}

c作为函数中的一个临时变量,在出了Add函数后会自动销毁,保存c数据的那部分空间此时就不存在了,这时候使用传引用返回,接收时就是一块未定义的空间,其性质就跟野指针一样

如果想更深入的理解此部分内容,可以看看我曾写过的一篇文章,里面细致讲解了函数栈帧的创建和销毁:
关于函数栈帧的创建和销毁-CSDN博客

注:函数返回时,出了函数作用域,如果返回对象还在(还没还给系统),则可以使用
引用返回,如果已经还给系统了,则必须使用传值返回

传值,传引用效率比较

以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直
接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效
率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。

下方TestFun1是传引用,TestFun2是传值:

下方TestFun1是传引用返回,TestFun2是传值返回:

通过上述代码的比较,发现传值和指针在作为传参以及返回值类型上效率相差很大

这种效率差距在传递次数多,拷贝量大的情况下变得尤为明显。

引用和指针的区别

语法概念上引用就是一个别名,没有独立的空间,和其引用实体共用一块空间。
但其在底层上实际是有空间的,因为是按照指针的方式来实现的。

int main()
{
	int a = 10;
	int& ra = a;
	ra = 20;
	int* pa = &a;
	*pa = 20;
	return 0;
}

我们可以来看看上面这段代码的汇编:

会发现,引用和指针的汇编代码竟然一模一样!

引用和指针的不同点:

  1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
  2. 引用在定义时必须初始化,指针没有要求。
  3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体。
  4. 没有NULL引用,但有NULL指针。
  5.  在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)。
  6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小。
  7.  有多级指针,但是没有多级引用。
  8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理。 
  9. 引用比指针使用起来相对更安全

内联函数

概念

inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调
用建立栈帧的开销,内联函数提升程序运行的效率。

如果在上述函数前增加inline关键字将其改成内联函数,在编译期间编译器会用函数体替换函数的
调用,其功能和C语言的宏相似。

在用inline修饰Add函数之后,代码就不会通过call调用函数,而是用函数体代码直接做添加。

特性

  1. inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会
    用函数体替换函数调用
    ,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运
    行效率。
  2. inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建
    议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、
    是递归、且频繁调用
    的函数采用inline修饰,否则编译器会忽略inline特性。《C++prime》第五版关于inline的建议:
  3. inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址
    了,链接就会找不到。

内联是一种用空间换取时间的方式,编译出来的可执行程序可能会变大:

举个例子,Swap函数如果有10行代码:

  • 如果不选择使用inline:则代码量为---> Swap + 调用(call) = 10 + 1000(call) 行指令。
  • 如果选择使用inline:则代码量为---> Swap * Swap指令数 = 10 * 1000 行指令。

故内联适合小函数,如果调用函数代码量大且调用次数多还选择使用内联,会导致代码量爆炸式增长。但是这个问题其实不用我们操心,编译器会自动选择是否将函数内联,你提供的inline对编译器来说只是一个建议,不起决定性作用。

内联函数其实是补C语言中宏的一个坑。由于宏的暴力替换,没有类型安全的检查,同时导致代码可读性差,可维护性差等原因,C++的祖师爷想到了用内联函数这样的方法去解决此问题。

auto关键字(C++11)

随着程序变得越来越复杂,程序中用到的类型也越来越复杂,经常会出现,类型难以拼写或含义不明确导致出错的情况。

#include <string>
#include <map>
int main()
{
	std::map<std::string, std::string> m{ { "apple", "苹果" }, { "orange",
	"橙子" },{"pear","梨"} };
	std::map<std::string, std::string>::iterator it = m.begin();
	while (it != m.end())
	{
		//....
	}
	return 0;
}

在上面的这一段代码中,std::map<std::string, std::string>::iterator 就是一个类型,但是其类型太长,写起来非常麻烦且及其容易写错。虽然可以使用typedef取别名的方式,像这样:

typedef std::map<std::string, std::string> Map;

但是在实际中还是会遇到,typedef也解决不了的问题。

typedef char* pstring;
int main()
{
	//const pstring p1; 编译失败,p1底层为char* const p1而非const char* p1 
	const pstring* p2; // 编译成功,底层为char * const* p2
	return 0;
}

虽然第二条命令编译成功了,但p2的类型显然不是我们想要的。

在编程时,常常需要把表达式的值赋值给变量,这就要求在声明变量的时候清楚地知道表达式的
类型。然而有时候要做到这点并非那么容易,因此C++11给auto赋予了新的含义(之前版本auto有一个定义,但是基本没人用)。

auto简介

C++11中,标准委员会赋予了auto全新的含义即:作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得

#include<iostream>
using namespace std;
int TestAuto()
{
	return 10;
}
int main()
{
	int a = 10;
	auto b = a;
	auto c = 'a';
	auto d = TestAuto();
	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;
	cout << typeid(d).name() << endl;
	//auto e; 无法通过编译,使用auto定义变量时必须对其进行初始化
	return 0;
}

typeid(变量).name()是一个返回变量类型字符串的函数,我们可以借此观察 b c d 用auto声明过后的类型。

注:使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型。

auto的使用规则

可以

1.auto与指针和引用结合起来使用

用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&

2.在同一行定义多个变量

当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译
器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量

不可以

1.auto不能作为函数参数

2.auto不能直接用来声明数组

3.为了避免与C++98中的auto发生混淆,C++11只保留了auto作为类型指示符的用法

指针空值nullptr(C++11)

C++98中的指针空值

在良好的C/C++编程习惯中,声明一个变量时最好给该变量一个合适的初始值,否则可能会出现
不可预料的错误,比如未初始化的指针。如果一个指针没有合法的指向,我们基本都是按照如下
方式对其进行初始化:

int* p1 = NULL;
int* p2 = 0;

NULL实际是一个宏,在传统的C头文件(stddef.h)中,可以看到如下代码:

#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif

可以看到,NULL可能被定义为字面常量0或者被定义为无类型指针(void*)的常量。不论采取何
种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦,就比如:

程序本意是想通过f(NULL)调用指针版本的f(int*)函数,但是由于NULL被定义成0,因此与程序的
初衷相悖。
在C++98中,字面常量0既可以是一个整形数字,也可以是无类型的指针(void*)常量,但是编译器
默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void
*)0。

所以在C++11中,就引入了nullptr指针空值,作为一个新关键字引入到C++体系之中。同时,为了提高代码的健壮性,后续使用指针空值时建议最好使用nullptr

结语

到这里,C++的入门部分总算是结束了。本篇博客讲到了C++相比于C新增的内容:函数重载,可以编写同名函数,但需要参数类型有区分度;讲到了引用,提供一种取别名的方式减少指针的使用,以及与传值相比的效率优越性;内联函数,用空间换时间,但是否内联取决于编译器本身;auto关键字,简化我们声明函数类型的过程,减少错误发生;指针空值nullptr,替换NULL提高程序的健壮性。

感谢大家的支持,博主后续会产出更多有意思的内容!♥

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

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

相关文章

golang grpc和protobuf的版本降级问题(version4 -> version3)

最后更新于2024年3月28日 10:57:52 简中没查到类似的文章。一点小事闹麻了&#xff0c;搞了一天&#xff0c;特意发出来造福大家。 所谓的版本就是下面这个东西proto.ProtoPackageIsVersion4或者proto.ProtoPackageIsVersion3&#xff1a; 目的 为了适配旧代码&#xff0c…

C语言之位段

1.位段的声明 位段的声明和结构是类似的&#xff0c;有两个不同&#xff1a; 1.位段的成员必须是 int、unsigned int 或signed int 。 2.位段的成员名后边有一个冒号和一个数字。 比如&#xff1a; struct A {int _a:2;int _b:5;int _c:10;int _d:30; }; A 就是一个位段类型…

阿里云服务器安装SSL证书不起作用的解决方案

阿里云服务器安装SSL证书不起作用的解决方案 在阿里云安装SSL证书后&#xff0c;访问无效&#xff0c;各种检查证书安装没有问题。忽然想到阿里云默认连80端口都没开启&#xff0c;443端口应该也没开启。 登录阿里云控制台 - 云服务器 ECS - 网络与安全 - 安全组 - 管理规则 - …

【Redis】Redis 内存管理,Redis事务,bigkey和hotkey

目录 Redis 内存管理 缓存数据设置过期时间&#xff1f; Redis 是如何判断数据是否过期的呢&#xff1f; 过期删除策略 内存淘汰机制 主从模式下对过期键的处理&#xff1f; LRU和LFU的区别 Redis事务 定义和原理 Redis 事务的注意点&#xff1f; 为什么不支持回滚&a…

C++析构函数

当对象的生存期结束时&#xff0c;系统就会自动执行析构函数清除其数据成员所分配的内存空间。 析构函数的定义格式为&#xff1a; &#xff5e;类名();//没有返回值,没有参数 注&#xff1a; (1&#xff09;析构函数名是由“&#xff5e;”加类名组成的。 &#xff08;2&#…

[HackMyVM]靶场Flossy

难度:Medium kali:192.168.56.104 靶机:192.168.56.142 端口扫描 ┌──(root㉿kali2)-[~/Desktop] └─# nmap 192.168.56.142 Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-04-01 21:01 CST Nmap scan report for 192.168.56.142 Host is up (0.00018s latency).…

linux shell命令(进程管理、用户管理)

一、进程的概念 主要有两点&#xff1a; 1.进程是一个实体。每一个进程都有它自己的地址空间&#xff0c;一般情况下&#xff0c;包括文本区域&#xff08;text region&#xff09;、数据区域&#xff08;data region&#xff09;和堆栈&#xff08;stack region&#xff09;…

ASCII编码的全面介绍

title: ASCII编码的全面介绍 date: 2024/4/1 20:05:52 updated: 2024/4/1 20:05:52 tags: ASCII定义编码原理编码表结构扩展编码应用场景优势与局限安全考量 1. ASCII编码的定义和历史 ASCII&#xff08;American Standard Code for Information Interchange&#xff09;是一…

Linux安装JDK及配置环境变量保姆级教程

文章目录 前言一、JDK下载一、Linux安装JDK及配置环境变量1.创建JDK的安装目录2.上传下载好的JDK安装包3.解压缩4.配置环境变量&#xff08;根据自己jdk的实际安装位置进行配置&#xff09;5.加载配置文件6.查看java版本&#xff08;此时jdk就已将安装完成&#xff09; 前言 JD…

基于SSM+Jsp+Mysql的固定资产管理系统

开发语言&#xff1a;Java框架&#xff1a;ssm技术&#xff1a;JSPJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包…

嵌入式网络硬件方案

一. 简介 本文来了解一下嵌入式有些网络中&#xff0c;涉及的网络硬件方案。 注意&#xff1a;本文说明的是有些网络。 提起网络&#xff0c;我们一般想到的硬件就是“网卡”&#xff0c;“网卡”这个概念最早从电脑领域传出来&#xff0c;顾名思义就是能上网的卡。在电脑领…

UE4_动画基础_ 瞄准偏移1D(Aim Offset Blend Space 1D)

瞄准偏移1D基本上可以完成角色的向左看向右看或者向上看向下看&#xff0c;像混合空间1D一样只有一个轴向可用。 操作步骤&#xff1a; 1、新建第三人称模板项目。 2、右键——动画——瞄准偏移1D 选取骨骼 双击打开 3、瞄准偏移混合的是姿势&#xff0c;我们需要创建姿势。 …

ALPHA开发板上PHY网络芯片LAN8720

一. 简介 正点原子的开发板 ALPHA开发板&#xff0c;有线网络硬件方案所使用的也是最常用的一种方案&#xff0c;IMX6ULL芯片内部是自带 MAC网络芯片的&#xff0c;所以&#xff0c;也就是采用 "SOC内部集成网络MAC外设 PHY网络芯片方案"。 ALPHA开发板使用的PHY网…

编程新手必看,Pycham开发工具使用及项目创建(3)

介绍&#xff1a;PyCharm是一款由JetBrains开发的专业Python集成开发环境&#xff08;IDE&#xff09;。 PyCharm为Python开发者提供了一整套工具&#xff0c;以提高编程效率和改善代码质量。以下是其主要特点和功能&#xff1a; 代码编辑与智能提示&#xff1a;具备高级代码编…

Java 堆外内存及调优

文章目录 直接内存简介为什么DirectByteBuffer可以优化 IO 性能 直接内存的分配直接内存的回收直接内存跟踪与诊断 直接内存简介 直接内存(Direct Memory) 并不是虚拟机运行时数据区的一部分&#xff0c;并非Java虚拟机规范中定义的内存区域。但是这部分内存的频繁使用&#x…

Vue.js高效前端开发(增删查)

效果图 代码&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title></head><body><div id"app"><span>ID</span><input type"text" name"…

javaWeb项目-家政服务管理系统功能介绍

项目关键技术 开发工具&#xff1a;IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架&#xff1a;ssm、Springboot 前端&#xff1a;Vue、ElementUI 关键技术&#xff1a;springboot、SSM、vue、MYSQL、MAVEN 数据库工具&#xff1a;Navicat、SQLyog 1、B/S结构简介 B/S…

unity学习(80)--disposed object

1.在正常运行的过程中&#xff0c;客户端崩溃&#xff0c;原因就是某个对象null或者被disposed了 2.找了找&#xff0c;发现socket确实调用过一次close 3.把close去掉修改为如下&#xff0c;客户端不再崩溃&#xff0c;虽然还有异常。

如何快速生成视频二维吗?视频用二维码播放的方法

视频的二维码如何制作会更加简单呢&#xff1f;通过扫码播放视频的方式现在越来越多&#xff0c;很多小伙伴也喜欢用这种方式来将视频分享给其他人。将视频储存到云端储存之后&#xff0c;通过扫描二维码在手机上浏览器视频&#xff0c;更加的方便快捷。 现在视频生成二维码可…

什么是ISP住宅IP?相比于普通IP它的优势是什么?

什么是ISP住宅IP&#xff1f; ISP住宅IP是指由互联网服务提供商&#xff08;ISP&#xff09;分配给住宅用户的IP地址。它是用户在家庭网络环境中连接互联网的标识符&#xff0c;通常用于上网浏览、数据传输等活动。ISP住宅IP可以是动态分配的&#xff0c;即每次连接时都可能会…