C++入门(二)

前言

我们上一期介绍了什么是C++,命名空间、输入输出、以及缺省参数。本期我们来继续介绍C++的入门知识!

本期内容介绍

函数重载

引用

内联函数

auto关键字

范围for

指针空值nullptr

目录

前言

本期内容介绍

一、函数重载

什么是函数重载?

C++支持函数重载的原理

二、引用

什么是引用?

引用的特性

常引用

再谈临时变量

引用的使用场景

1、做参数

2、做返回值

引用和传值的效率比较

传参和传引用比较

值返回和引用返回比较

引用和指针的区别

三、内联函数

什么是内联函数?

内联函数的特性

宏的优缺点

四、auto关键字(C++11)

auto关键字介绍

auto使用细节

五、范围for(C++11)

范围for语法

范围for的使用条件

六、指针空值nullptr(C++11)


一、函数重载

什么是函数重载?

函数重载:同一作用域中函数名相同、形参列表不同的函数。形参列表不同是指:参数的个数、类型、顺序不同! 函数重载对于函数的返回值没有要求!

OK,举个栗子:

void Add(int x, int y)
{cout << "haha" << endl;
}int Add(int a)
{return a + 10;
}double Add(int x, double d)
{return x + d;
}double Add(double d, int x)
{return x + d;
}

C++支持函数重载的原理

首先C语言是不支持函数重载的,而C++是支持函数重载的!

C语言不支持的原因可以从两方面解释。

1、历史角度:设计C语言的祖师爷当时觉得设计同名函数会导致误会,而且那时候没有碰到相应的场景,因此没有设计同名函数!

2、链接角度:C语言在生成可执行程序前需要编译链接,链接时去合并符号表和符号表的重定位时,是直接去拿函数名去找的(C语言没有同名函数)。

C++支持函数重载的原因是,C++会对重载的函数在编译时进行特殊的符号修饰,等到链接去合并符号表和符号表的重定位的时候,实际上是根据不同的函数名(进行修饰过的)进行找的!

OK,这里大概串一下编译链接的过程:

OK,再来分别看看C语言和C++的编译后的函数名的区别,由于windows函数修饰规则较为复杂所以我这里以Linux为例(add函数为例):

先通过C语言的编译器gcc进行编译到汇编停止(为了待会看符号表)

再来看看C++的:

通过C++的编译器g++进行编译到汇编停止(C++也是生成.o):

显然C语言生成的符号表的函数名和C++是不一样的,C语言就是纯函数名而C++是修饰过的函数名!这里再来介绍一下C++的函数名的修饰规则(Linux下)!

—Z + 函数名的字符个数 + 函数名 + 参数类型的首字符

所以上面C++编译后的符号表中的add是_Z3addii,因为它的函数名有三个字符,函数参数类型都是int所以是ii

二、引用

什么是引用?

引用(&)不是重新定义一个变量,而是给已经存在变量起一个别名,而是引用变量和被引用的变量共用一块空间!(语法上编译器不会为引用变量开空间

语法:类型& 引用变量(对象)名 = 引用实体

OK,举个例子:我们小时候看西游记的时候,孙悟空又称齐天大圣,齐天大圣就是孙悟空的别名即引用!

#include <iostream>
using namespace std;int main()
{int a = 10;int& b = a;cout << a << "--" << b << endl;b++;cout << a << "--" << b << endl;return 0;
}

这里b就是a的引用,他是a的别名,和a共用一块空间!既然他和a共用一块空间的话对b++,a也会+1,他们的地址应该也是一样的!

但我们刚刚上面说是语法上引用不开空间,那底层呢?底层会!引用的底层就是指针(汇编实现)!我们跳到反汇编看看:

我们平时学习时认为引用是不开空间的但也要知道底层编译器是开了的,它的大小是4/8个字节!

注意:引用类型必须和引用实体同类型的

不能这么乱搞!!!

引用的特性

1、引用在定义时必须初始化

2、一个变量可以有多个引用(就像孙悟空别名是齐天大圣、弼马温、猴子等)

3、引用一旦引用了一个实体,就不能在引用其他实体了

1、引用在定义时必须初始化

int main()
{int a = 3;int& b; return 0;
}

2、一个变量可以有多个引用

int main()
{int a = 3;int& b = a; int& c = a;int& d = c;return 0;
}

3、引用一旦引用了一个实体,就不能在引用其他实体了

int main()
{int x = 10;int y = 20;int& c = x;cout << x << endl;cout << c << endl;c = y;//此处不是改变了引用实体,而是把y赋值给了c引用的实体cout << c << endl;cout << y << endl;cout << &x << endl;cout << &c << endl;cout << &y << endl;return 0;
}

这里通过打印地址验证了引用一旦引用一个实体就不在改变!

常引用

这里的常引用实际上是权限的问题!

权限可以缩小、可以平移,但权限不能放大!

权限不能放大

int main()
{const int a = 10;//const 修饰的常变量具有常量的属性-->不可修改int& b = a;//权限放大return 0;
}

我们知道被const修饰的变量具有常量的属性--->不可修改,下面用非const 修饰的引用b去引用a,此时的b是可以修改的即放大了权限

解决办法-->缩小引用的权限让引用也被const修饰(权限可以平移):

权限可以缩小

int main()
{int x = 10;const int& y = x;//权限缩小return 0;
}

赋值是不涉及权问题

int main()
{const int x = 100;int c = x;//赋值return 0;
}

说道这里我又想到了一个小知识点 ---- 临时变量,我们顺便来介绍一下:

再谈临时变量

我们以前在C语言函数返回值的时候介绍过,函数返回会创建临时变量,临时的中间变量具有常量属性!当时没有细细的介绍只是一笔带过了!其实这条性质不只是返回值有效,对于类型转换(强转、隐式类型转换、整型提升、截断等)都是有效的!

OK,举个栗子:

int main()
{int a = 3;double d = a;return 0;
}

这里是典型的隐式类型转换,int转换为double ,其实他并不是直接把a(int)给转换为d(double)的,而是产生了中间变量的!这个变量是编译器产生并且默默处理的,无法调试看到!我画个图解释一下:

还有就是我们当时在模拟实现内存函数(memcpy、memmove、movecmp)的时候说过:强转具有临时性不能被修改!原因就是这!

OK,验证一下:

int main()
{int a = 10;double d = 9.9;int* pa = &a;double* pd = &d;(int*)(&d) = nullptr;return 0;
}

引用的使用场景

1、做参数

我们以前在用C语言交换两个整数时是不是得传指针呀,原因是形参是实参的一份临时拷贝,改变形参不改变实参,要改变实参必须传地址!今天有了引用就不用了,引用是别名,就可以直接修改了!

以前版本:

void Swap(int* x, int* y)
{int tmp = *x;*x = *y;*y = tmp;
}int main()
{int a = 3, b = 5;printf("交换前: a = %d  b = %d\n", a, b);Swap(&a, &b);printf("交换后: a = %d  b = %d\n", a, b);return 0;
}

引用做参数版本:

void Swap(int& x, int& y)
{int tmp = x;x = y;y = tmp;
}int main()
{int a = 3, b = 5;printf("交换前: a = %d  b = %d\n", a, b);Swap(a, b);printf("交换后: a = %d  b = %d\n", a, b);return 0;
}

介绍到这里,我们可以在实现数据结构的时候如果不带头的话必须得要二级指针的地方,有点不方便,今天介绍了引用后我们可以不用传二级了,直接把以及传过去行参用一个一级指针的引用接收即可!

没使用引用前:

typedef struct SLNode
{int data;struct SLNode* next;
}SLNode;//头插
void SLPushBack(SLNode** head)
{}SLNode* head = NULL;
SLPushBack(&head);

使用引用之后:

typedef struct SLNode
{int data;struct SLNode* next;
}SLNode, *SLLink;//头插
void SLPushBack(SLNode*& head)
{}SLLink head = NULL;
SLPushBack(head);

是不是看起来好点了,而且操作也会简单一点!使用引用的这种方式是很多教材上的样例!而且还在书皮上写着" 数据结构与算法 "C语言实现???我也寻思C语言没有引用啊???我当时一开始看到学校的教材时直接傻眼了!(自以为链表学的还行结果上机操作不来,当时还没学C++....)!我个人觉得第二种这种教材的写法应该告诉读者要提前学习C++引用。不然对新手极其不友好!!!!

2、做返回值
int& Count()
{static int n = 0;n++;return n;
}
int g_val = 0;
int& Count(int n)
{g_val += n;return g_val;
}

这两种写法是没有问题的!全局变量和静态变量都存在于静态区,他们的生命周期都是整个工程的生命周期。所以这里可以直接返回,因为出了作用域引用的实体(对象)还存在!

思考一下,下面这段代码对吗?能这样写吗?

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

先看结果:

你要是在公司写这样的代码出来,你的领导200%会找到你对你进行“鼓励+问候”的!!!你肯定想我第二次内接受呀为什么是7?这段代码问题实际是返回栈空间的地址,和我们C语言指针那一期介绍的那个一样!OK,我来画个图解释一下为什么会这样!

注意:引用是否做返回值,取决于出了作用域后引用的实体是否存在,如果存在可以用引用做返回值,否则用传值!

引用和传值的效率比较

我们以前讨论传值传参和传址传参哪个优时?我们的结论是:在修改参数的值时、参数大小较大时,传址是更好的原因是1、改变必须得要指针2、较大的参数如果传值时会产生拷贝,效率会降低!如果是传指针只是4/8个字节!!其实在适当的场景下引用的效率也是远远地高于传值的!我们来看看!

以结构体为例:

struct A 
{int a[100000];char b;double c;
};
传参和传引用比较

这样一个结构体,它的大小是不是很大呀!如果传值过去的话形参就会产生拷贝导致效率很低!传引用的话,形参是实参别名,语法上不开空间!效率会高很多!

OK,我们来验证一下:

void TestA1(struct A a1){}
void TestA2(struct A& a2) {}int main()
{struct A a1;size_t begin1 = clock();for(int i = 0; i < 10000; i++)TestA1(a1);size_t end1 = clock();cout << "传值消耗的时间: " << end1 - begin1 << endl;size_t begin2 = clock();for (int i = 0; i < 10000; i++)TestA2(a1);size_t end2 = clock();cout << "传引用消耗的时间: " << end2 - begin2 << endl;return 0;
}

值返回和引用返回比较

一般的值返回是会形成一个拷贝的(大一点的是形成拷贝在放在被调函数的栈帧上,较小的会放在寄存器eax里面。这里我们在函数栈帧介绍过,就不在说了)然后返回,实际上我们看到的返回值在出了作用域后就会销毁!如果是出了作用域对象还在的话,用引用返回的确比较好的多!但一定注意出了作用域实体得存在!

struct A 
{int a[100000];char b;double c;
};
struct A a1;A TestA1() {return a1;
}
A& TestA2() {return a1;
}int main()
{size_t begin1 = clock();for(int i = 0; i < 10000; i++)TestA1();size_t end1 = clock();cout << "值返回消耗的时间: " << end1 - begin1 << endl;size_t begin2 = clock();for (int i = 0; i < 10000; i++)TestA2();size_t end2 = clock();cout << "引用返回消耗的时间: " << end2 - begin2 << endl;return 0;
}

总结:上述测试说明了值和引用在传参、返回值上的效率有很大的不同,如果较大的参数建议用引用传!一定注意的是:是否用引用返回时,取决于引用的实体等出了作用域还在!否则出问题!

引用和指针的区别

在语法上引用就是一个别名,没有独立空间,他和引用的实体共用一块空间!但在底层上是开了的,底层是用汇编语言通过指针实现的!

语法上没开空间:

int main()
{int a = 10;int& b = a;cout << &a << endl;cout << &b << endl;return 0;
}

底层开了空间:


int main()
{int a = 10;int& b = a;int* p = &a;return 0;
}

主要区别:

1、引用语法上是定义一个变量的别名,指针存储着一个变量的地址

2、引用在定义时必须初始化,指针没有要求

3、引用在引用了一个实体后就不能在引用别的实体了,指针可以在任何时候指向同类型的实体

4、引用没有空引用,指针有空指针

5、sizeof下的含义不同:引用的结果为引用实体类型的大小,指针的结果为4/8(x86和x64)

6、引用自加引用的实体会加一,指针自加指针会偏移一个指向类型的字节大小

7、引用没有多级,指针有多级

8、访问实体的方式不同:引用不需要显示处理编译器会处理,指针需要显示解引用

9、引用相较于指针更安全

三、内联函数

什么是内联函数?

被inline修饰的函数叫做内联函数!在编译时C++编译器会在调用内联函数的地方直接展开而不在建立函数栈帧了!(这提升了程序的运行效率)

OK,举个栗子:

不是内联函数(有函数栈帧):

int Add(int x, int y)
{return x + y;
}

内联函数(不会创建函数栈帧):

inline int Add(int x, int y)
{return x + y;
}

由于小编的是VS2019在这块优化的有点大,调试不来,我们在VS2013上看看:

内联函数的特性

1、C语言不支持内联函数

2、内联(inline)是一种典型的空间换时间的做法,如果编译器把函数当做内联来处理,在编译阶段会直接在原地展开而不去调用(call)。

这样做的优点是:少了栈帧开销,程序的运行效率提高了,缺点是:直接展开如果调用的地方过多会使目标文件(可执行程序)变大!

OK,举个栗子:

3、inline对于编译器而言只是一个建议,不同的编译器对于inline的实现不同!但一般是较小规模的函数(一般不超过10行,具体各个编译器不同)、非递归、频繁调用的函数编译器会采用inline修饰即在调用地展开,否则编译器会直接忽略掉inline的特性

下面是《C++prime》第五版关于inline 的建议:

OK,举个栗子(上面那个已经验证过内联了,我们来个编译器忽略的):

inline int fun(int n)
{int x = 10;int q = 100;int w = 1;int a = 3;int z = 2;int c = 21;int v = 5;int f = 6;int g = 7;int u = 9;return x + 10;
}

我个人觉得这应该是防止程序员乱搞的一种机制!不然编译器不制止的话,有的给你到处无脑inline,程序就会很大很大!有了这个机制后就相当于是程序员向编译器发出请求,编译器觉得不合理了就可以拒绝!

4、inline是不建议申明和定义分开的,如果分离会在链接时找不到inline函数!因为inline是在调用地直接展开的没有地址,在编译完之后进行链接符号表合并的时候就找不到inline!

C++中的内联的作用实际上是为了解决C语言的宏的缺陷的!我们顺便来回忆一下宏的优缺点!

宏的优缺点

优点:

1、增强了代码的复用性

2、提高性能

缺点:

1、不方便调试(在预处理阶段就已经替换了)

2、导致代码的可读性差、可维护性差、容易误用

3、没有类型检查

别的不说,基本功不扎实的写一个简单的加法的宏都会写错!下面是典型的例子(看看有没有你的影子)。

错误版本:

//#define ADD(int x, int y) return x + y;
//#define ADD(x, y) return x + y;
//#define ADD(x, y) x + y;
//#define ADD(x, y) (x + y)
//#define ADD(x, y) (x) + (y)

下面这三个还是能理解的,上面那两个是真的无语的那种!!!有的伙伴看不出下面三种哪里不对,我来分别用一个例子演示:

#define ADD(x, y) x + y
int main()
{int ret = ADD(2 , 3) * 5; // 替换后--> 2 + 3 * 5return 0;
}
#define ADD(x, y) (x + y)
int main()
{int ret = ADD(2 | 1, 1 & 3); // 替换后--> 2 | 1 + 1 & 3return 0;
}

这里+的优先级比&|高得多,与我们的期望值计算结果不一样!

#define ADD(x, y) (x) + (y)
int main()
{int ret = ADD(2,3) * 6; // 替换后--> (2) + (3) * 6;return 0;
}

下面这个也是替换后的优先级有问题!下面我们来看看正确的写法

#define ADD(x, y) ((x) + (y))

C++的内联函数实际上是很看不起宏的,它的语法细节太多了,稍不注意就会出错!所以C++是通过内联函数替代宏、用const 和 enum来替代常量。这就完美了解决了C语言宏的缺陷!内联可以调试,有类型检查,可读性强!

四、auto关键字(C++11)

随着学习的深入,程序变得越来越复杂,程序设计的类型也越来越复杂!这就会导致两个问题的产生:1、类型难于拼写 2、含义不明确导致出错

OK,举个栗子:

#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();return 0;
}

std::map<std::string, std::string> 这么长的类型就问你恶不恶心?

这里你肯定说可以用typedef 重命名,的确可以!

#include <string>
#include <map>typedef std::map<std::string, std::string> Map;
int main()
{Map m{ { "apple", "苹果" },{ "orange", "橙子" }, {"pear","梨"} };return 0;
}

但有时候typedef会把你坑死~!再来看个栗子:

typedef char* pstring;
int main()
{const pstring p1; const pstring* p2; return 0;
}

这样写对吗?是不是连你都控制不住了!OK,这里是p1有问题p2可以的!为什么p1不行p2可以呢?我来解释一下:先看运行结果:

这个就对基本功考察的很深了!而且很难控制和推导出类型!为此C++11引进了一个关键字:auto

关于auto有伙伴可能听过一些!没错他就是C语言中最没有存在感的关键字!在每个局部变量前都有一个隐藏的auto,我们一般不写,也可以显示的写出来:

#include <stdio.h>
int main()
{auto int a = 10;printf("%d\n", a);struct A{int b;};auto struct A b;return 0;
}

auto关键字介绍

在早期 C/C++ auto 的含义是:使用 auto 修饰的变量,是具有自动存储器的局部变量,但遗憾的是一直没有人去使用它! C++11 中,标准委员会赋予了 auto 全新的含义即: auto 不再是一个存储类型指示符,而是作为一 个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得到

举个栗子:

int main()
{int a = 6;cout << a << endl;auto x = 10;cout << x << endl;auto d = 6.6;cout << d << endl;auto c = 'a';cout << c << endl;return 0;
}

这样写可以吗?

int main()
{auto a;cout << a << endl;return 0;
}

肯定不行的!为什么呢?人家说的很明确:

auto声明的变量必须由编译器在编译时期推导而得到。换句话说用auto声明变量时,它的底层编译器会根据右边的类型确定auto的实际类型的!这里啥都不给让编译器推到啥???

那我们平时怎么知道auto申明的变量或返回的类型是什么类型呢?这里介绍一个typeid关键字来查看!如下:

typeid(查看对象名).name()

char Test()
{return 'a';
}int main()
{auto d = 6.6;cout << d << endl;cout << typeid(d).name() << endl;auto ret = Test();cout << ret << endl;cout << typeid(ret).name() << endl;return 0;
}

总结:

在使用auto定义变量时必须初始化 ,因为在编译期间编译器要根据初始化的内容去推导auto的实际类型!所以auto并不是一种类型的声明,他是一种类型的 占位符 ,在编译期间编译器会将他替换成实际的类型!

auto使用细节

1、auto声明指针变量时,auto和auto*没有区别,但auto声明引用类型时则必须加&

int main()
{int a = 10;auto p1 = &a;auto p2 = &a;cout << typeid(p1).name() << endl;cout << typeid(p2).name() << endl;auto& b = a;return 0;
}

2、在同一行定义多个变量时,多个变量的类型必须是一样的,否则会报错!原因是:编译器会只推导第一个类型,然后用这个类型去定义后面的其他变量!

int main()
{auto a = 10, b = 20;//这里不会报错auto d = 6.6, c = 'a';//这里会报做return 0;
}

3、auto不能做函数的参数

void Test(auto x)
{//...
}

4、auto不能声明数组

int main()
{auto a[] = { 1,2,3 };return 0;
}

auto其实还有其他用法例如配合范围for和lambda表达式使用!后期介绍到了再详解~!

五、范围for(C++11)

C++98 中如果要遍历一个数组,可以按照以下方式进行:
void TestFor()
{
int array[] = { 1, 2, 3, 4, 5 };
for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)array[i] *= 2;
for (int* p = array; p < array + sizeof(array)/ sizeof(array[0]); ++p)cout << *p << endl;
}
对于一个 有范围的集合 而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因
C++11 中引入了范围的 for 循环。

范围for语法

for 循环后的括号由冒号 分为两部分:第一部分是范 围内用于迭代的变量,第二部分则表示被迭代的范围
for( 集合每个元素的类型 变量名 :集合) {}
他这里实际上是从集合中拿出一个元素赋值给前面声明的变量,然后自动++,他会自动判断结束的!!
void TestFor()
{
int array[] = { 1, 2, 3, 4, 5 };
for(auto& e : array)e *= 2;
for(auto e : array)cout << e << " ";
return 0;
}
注意:与普通循环类似,可以用 continue 来结束本次循环,也可以用 break 来跳出整个循环

范围for的使用条件

1、for循环迭代的范围必须是确定的
对于数组而言,就是数组中第一个元素和最后一个元素的范围 ;对于类而言,应该提供
begin end 的方法, begin end 就是 for 循环迭代的范围。
来看看这段代码对吗?
void TestFor(int array[])
{for(auto& e : array)cout<< e <<endl;
}

答案是错的!我们上面说过!范围for迭代的范围必须是确定的!这里array是一个数组,但我们知道虽然他这么写看起来是数组,但他的本质是一个指针,arrasy的范围是不确定的!所以无法遍历!

2. 迭代的对象要实现++和==的操作。 (关于迭代器,以后会介绍,现在提一下,知道有这个东西就好了)

六、指针空值nullptr(C++11)

在良好的C/C++编程习惯中应在声明一个变量时给这个变量一个合适的初始值,否则可能出现不可预料的错误!例如指针不初始化。C语言中如果一个指针没有合适的指向的话我们一般会给他置NULL(NULL实际上本质就是0),但在C++中会出现一些问题!
NULL实际是一个宏,在传统的C头文件(stddef.h)中:
int main()
{int* p1 = NULL;return 0;
}

上面定义中我们看到了NULL被定义成了,字面常量0或被定义为无类型的指针的常量void*。这样定义会在成一些问题,看如下代码:

void Test(int)
{cout << "Test(int)" << endl;
}void Test(int*)
{cout << "Test(int*)" << endl;
}int main()
{Test(0);Test(NULL);return 0;
}
我们的本意是让第二个NULL走int*,但他的结果好像和我们的预期不一样呢!
此时要想让NULL调到Test(int*)必须进行强转!
这样写是不是有点麻烦呀!这其实是C++在这里的一个缺陷!后来C++11为了解决这个缺陷对这里打了补丁 ---> 用nullptr替换NULL
注意:
1. 在使用 nullptr 表示指针空值时,不需要包含头文件,因为 nullptr C++11 作为新关键字引入
2. C++11 中, sizeof(nullptr) sizeof((void*)0) 所占的字节数相同。
3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用 nullptr
OK,C++入门知识就介绍到这里,好兄弟,我们下期再见!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

排序算法之-选择

算法原理 在未排序的数列中找出最大&#xff08;或最小&#xff09;的元素&#xff0c;然后将其存入到已排序的数列起始位置&#xff0c;紧接着在剩余的未排序数列中继续查找最大&#xff08;或最小&#xff09;的元素&#xff0c;并将其放入到已排序的数列末尾&#xff0c;依…

Flink--简介

1、Apache Flink 是一个实时计算的框架和分布式处理引擎&#xff0c;用于在无边界喝有边界数据流上进行有状态的计算&#xff0c;并且能够在常见的集群上运行&#xff0c;并能以内存速度和任意规模进行计算。 有边界数据流&#xff1a;指的是有开始&#xff0c;也有结束&…

SpringMVC使用AOP监听方法推送数据

导入aop的maven依赖 <dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.6.12</version> </dependency>创建一个spring的XML文件编写aop配置 <?xml version"1.0" …

推荐一款功能强大的在线文件预览工具-kkFileView

程序员的公众号&#xff1a;源1024&#xff0c;获取更多资料&#xff0c;无加密无套路&#xff01; 最近整理了一波电子书籍资料&#xff0c;包含《Effective Java中文版 第2版》《深入JAVA虚拟机》&#xff0c;《重构改善既有代码设计》&#xff0c;《MySQL高性能-第3版》&…

【Unity】思考方式与构造 | 碰撞器/刚体/预设/组件

《Unity神技大人炼成记》第二章-思考方式与构造 Unity版本&#xff1a;2019.4.23f1c1 相关文章&#xff1a;第一章&#xff1a;开天辟地&#xff08;场景搭建-天空 山脉 草木 湖泊&#xff09; 粉色矩形是截图后添加&#xff0c;以便辨认操作位置有些步骤只是为了体现一些属性…

多维时序 | MATLAB实现TCN-selfAttention自注意力机制结合时间卷积神经网络多变量时间序列预测

多维时序 | MATLAB实现TCN-selfAttention自注意力机制结合时间卷积神经网络多变量时间序列预测 目录 多维时序 | MATLAB实现TCN-selfAttention自注意力机制结合时间卷积神经网络多变量时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 MATLAB实现TCN-s…

深度学习之基于Tensorflow人脸面部表情识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 基于Tensorflow的人脸面部表情识别系统是一种基于深度学习技术的图像处理应用&#xff0c;该系统主要通过人脸图像数…

Stable Diffusion源码调试(二)

Stable Diffusion源码调试&#xff08;二&#xff09; 个人模型主页&#xff1a;https://liblib.ai/userpage/369b11c9952245e28ea8d107ed9c2746/model Stable Diffusion版本&#xff1a;https://github.com/AUTOMATIC1111/stable-diffusion-webui/releases/tag/v1.4.1 分析S…

nacos应用——占用内存过多问题解决(JVM调优初步)

问题描述 最近搞了一台1年的阿里云服务器&#xff0c;安装了一下常用的MySQL&#xff0c;Redis&#xff0c;rabbitmq&#xff0c;minio&#xff0c;然后有安装了一下nacos&#xff0c;结果一启动nacos内存占用就很高&#xff0c;就比较限制我继续安装其他镜像或者启动别的服务…

b2b.ccb.com:443 需要你的凭据

忙活了一天&#xff0c;晚上回来准备查一下公户的最近的账单。因为昨天晚上熬夜重新做了电脑系统&#xff0c;就下载了建设银行的E路护航&#xff0c;一切安装就绪&#xff0c;准备进入企业网银时&#xff0c;被这些垃圾搞的系统及软件恶心到了&#xff0c;在此记录一下&#x…

CAN 协议常见面试题总结

0.讲一下CAN通讯的过程 第一段&#xff1a;需要发送的通讯设备&#xff0c;先发送一个显性电平0&#xff0c;告诉其他通讯设备&#xff0c;需要开始通讯。 第二段&#xff1a;就是发送仲裁段&#xff0c;其中包括ID帧和数据帧类型&#xff0c;告诉其他通讯设备&#xff0c;需…

Linux Vim批量注释和自定义注释

使用 Vim 编辑 Shell 脚本&#xff0c;在进行调试时&#xff0c;需要进行多行的注释&#xff0c;每次都要先切换到输入模式&#xff0c;在行首输入注释符"#"再退回命令模式&#xff0c;非常麻烦。连续行的注释其实可以用替换命令来完成。 换句话说&#xff0c;在指定…

每天一道算法题:17. 电话号码的字母组合

难度 中等 题目 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 示例 1&#xff1a; 输入&#xff1a;digits “23” …

计算机提示找不到concrt140.dll怎么办,分享4个有效的修复方法

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示或者系统崩溃的情况。其中&#xff0c;concrt140.dll是一个常见的错误提示&#xff0c;这个错误通常会导致某些应用程序无法正常运行。为了解决这个问题&#xff0c;我们需要采取一些修复措施。本文将介绍4个修复conc…

【Java 进阶篇】Java Session 原理及快速入门

大家好&#xff0c;欢迎来到本篇博客。今天&#xff0c;我们将探讨Java Web开发中一个重要而令人兴奋的概念&#xff0c;即Session&#xff08;会话&#xff09;。Session是一种在Web应用程序中跟踪用户状态和数据的机制。我们将深入了解Session的原理&#xff0c;并通过示例来…

Qt利用VCPKG和CMake和OpenCV和Tesseract实现中英文OCR

文章目录 1. 开发平台2. 下载文件2.1 下载安装 OpenCV 库2.2 下载安装 Tesseract-OCR库2.3 下载训练好的语言包 3. CMakeLists.txt 内容4. Main.cpp4.1 中英文混合OCR 5. 在Qt Creator 中设置 CMake vcpkg5.1 在初始化配置文件里修改5.2 在构建配置里修改 说明&#xff1a;在Q…

npm install:sill idealTree buildDeps

执行npm install&#xff0c;卡在 sill idealTree buildDeps PS D:\workspace-groovy\attendance-india-web> npm install -g cnpm --registryhttps://registry.npm.taobao.org [..................] / idealTree:node_global: sill idealTree buildDeps[.................…

Java代码如何对Excel文件进行zip压缩

1&#xff1a;新建 ZipUtils 工具类 package com.ly.cloud.datacollection.util;import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.net.URLEncoder; import ja…

【EI会议征稿】第四届计算机网络安全与软件工程国际学术会议(CNSSE 2024)

第四届计算机网络安全与软件工程国际学术会议&#xff08;CNSSE 2024&#xff09; 2024 4th International Conference on Computer Network Security and Software Engineering 第四届计算机网络安全与软件工程国际学术会议&#xff08;CNSSE 2024&#xff09;将于2024年2月…

Adobe Photoshop Elements 2024 v24.0 简体中文版 | 中文直装版

下载&#xff1a; http://dt1.8tupian.net/2/29913a53b500.pg3介绍&#xff1a;Photoshop Elements 2024(简称PSE即PS简化版)是一款定位在数码摄影领域的全新的图像处理软件&#xff0c;该软件包括了专业版的大多数特性&#xff0c;只有少量的简化选项&#xff0c;提供了调整颜…