C++基础知识-2

news2024/12/25 12:21:53

本期我们接着来讲C++的基础知识,没有看过的朋友可以先看看上一期

(16条消息) C++基础知识-----命名空间_KLZUQ的博客-CSDN博客

目录

4.缺省参数 

5.函数重载

6.引用

 7.内联函数

8.auto关键字(C++11)

 9. 基于范围的for循环(C++11)

10.指针空值nullptr(C++11)


4.缺省参数 

缺省参数是 声明或定义函数时 为函数的 参数指定一个缺省值 。在调用该函数时,如果没有指定实参则采用该 形参的缺省值,否则使用指定的实参。
我们可以在函数的形参部分给他指定一个数,此时我们调用函数,如果我们不传参就会使用默认的值,传参就会使用我们传入的参数

 

如果有多个缺省参数,我们也可以只传部分

我们传入的参数少于缺省参数,会从左往右依次传给缺省参数

另外,我们不能跳着传参

上面的缺省叫做全缺省,因为这些参数都是缺省参数,与之相对,还有半缺省

如果有一个参数不是缺省,那我们就必须至少传入一个参数

 缺省是从右往左的,即我们的缺省参数要从右边开始,而不能左边是缺省,右边不是

 这与上面传参相对应

我们来看一些缺省参数的应用

我们在C语言阶段定义栈时,我们当时默认给了栈的空间是4,如果一个人知道他的数据容量为100,那此时就要不断的扩容,非常麻烦,而我们这里给出缺省参数,就可以根据不同的需求来给定空间了,而一个人不知道他的数据容量,那就不需要传,用默认的即可

 

 我们用之前的写法将他们写在不同的文件里,然后进行编译

发现错误,缺省参数不能在声明和定义同时存在,这是害怕给的缺省参数不一致

正确的写法是声明给,定义不给,即在.h文件给缺省,.cpp里不给

5.函数重载

自然语言中,一个词可以有多重含义,人们可以通过上下文来判断该词真实的含义,即该词被重载了。
比如中国的兵乓球和男足,两个都是谁也赢不了,虽然是一句话,但意思却完全不同
函数重载: 是函数的一种特殊情况, C++ 允许在 同一作用域中 声明几个功能类似 的同名函数 ,这些同名函数 的形参列表 ( 参数个数 或 类型 或 类型顺序 ) 不同 ,常用来处理实现功能类似数据类型不同的问题。

 只看main函数里,大家可能认为我们调用的是同一个函数,在C语言里,这是不允许的,但C++里是可以的

判断函数是否重载,要严格的根据定义来判断,很多人以为和返回值类型也有关,其实是无关的

比如函数名相同,参数相同,返回值不同,也不构成重载

要注意参数的类型顺序不同也构成重载,而不是形参名不同

我们看一个特殊例子

这两个f构成重载吗?

我们严格按照定义来看,答案是构成的,编译也是可以通过的,但是问题是无参调用时存在歧义 

 

接下来我们来看一个问题,为什么C语言不支持重载,C++支持?C++是怎么支持的?

这里涉及到编译链接过程和函数名修饰规则,下面我们来详细讲解

假如,我们上面栈的例子,并加入这两个函数

 我们有3个文件,一个Stack.h,一个Stack.cpp,一个test.cpp

 C++的编译器和C的编译器走的是基本一致,但有些小细节的不同

编译链接的第一步是预处理,预处理要头文件展开

(不清楚编译链接等等作用的同学可以看我往期的内容)

(16条消息) 程序环境和预处理_KLZUQ的博客-CSDN博客

 还要进行宏替换,条件编译,去掉注释等等

预处理完后.h都展开了,就没有.h文件了,此时生成了Stack.i和test.i的文件

接下来是编译,编译时要检查语法,生成汇编代码,生成Stack.s和test.s

接下来是汇编,cpu是看不懂汇编指令的,汇编是符合指令,汇编将汇编代码转换为二进机器制码

生成Stack.o和tets.o文件

接下来是链接,生成可执行程序,在Windows下是.exe文件,在Linux下为a.out或其他名字

在此之前,上面的.i,.s,.o都是独立的,只有链接时才会合在一起(注意,不是合并,是链接)

我们转到反汇编,这里的call是调用其他函数,调用其他函数都会被转换为call加一个地址

函数又是一串指令,调用函数的本质又会跳转到 jmp 指令

 最终是为了执行这个函数,要建立栈帧

在我们的编译阶段,这里是拿不到这个地址的(tets.i->test.s)

我们包含.h文件,但.h里是声明,地址在Stack.i里

我们举个例子,我们要去买房子需要50w元,我们差10w元,这时我们想起了下铺的好兄弟,我们告诉他,好兄弟告诉我们没问题,这10w到时候他会帮助你

声明相当于一个承诺,即好兄弟答应你,但此时你并没有拿到钱,有了承诺后,我们就敢去买房子了,我们的编译器也是这样的

 

这也是为什么这里可以通过,为什么缺省参数要在声明时给定 ,声明的时候就可以拿到,进行检查,但是我们此时并没有拿到地址,也就是我们没有拿到那10w块钱,不过我们是敢去买房子的,交个定金

此时我们回过头来看为什么两个.o文件是链接而不是合并,链接的意思是兑现承诺,找到定义

我们的.o都会生成符号表

比如Stack.o的符号表里就有StackInit:0x112233(地址),StackPush:0x112244,StackPush:0x112255

链接错误就是兑现承诺失败,正常情况下都能兑现成功

补充:我们将两个Push函数屏蔽

这是链接错误

但如果我们是少了一个分号,这是编译错误,编译错误走不到链接那一步

回过头来看我们的问题,为什么C语言不支持重载?因为C语言太直接了,直接使用了那个名字

在符号表里叫StackPush,这里的call也就直接用StackPush

那C++是怎么支持的呢?我们这里用Linux简单演示一下(后续我会出Linux的教材)

 这段代码我们完全按C语言去写

 我们编译后会报错,我们将代码写为正常代码,即屏蔽一个func函数

然后用gcc -o tetsc test.c

默认不指定会生成a.out,但此时我们指定生成testc

 接下来我们用objdump -S testc 这样一句指令

 我们就可以看到这些东西,我们来看,C语言是直接用的函数名,所以两个名字相同就冲突了

如果我们使用的是C++的话,我们将屏蔽的代码放出来,我们甚至可以把之前的Stack代码拿过来

使用g++ -o testcpp test.cpp指定生成testcpp

我们继续objdump -S testcpp

此时我们可以看到两个func的名字变了,C语言是直接是func,这里是函数名修饰规则

_Z是编译器规定的前缀,4是函数的长度,比如func是4个字母,后面一个是i,一个是id

是形参类型的缩写,即int,double

 这是栈的两个Push,只要参数不同,就会修饰的不同

如果我们自己开发一门语言,修改函数名修饰规则,是否可以让返回值类型与重载也相关呢?

答案是不可以

 返回值在调用的时候不会体现,所以编译时就报错了,存在调用歧义,走不到链接那一步

除非把语法也改了,我们上面代码返回值不写时知道默认调用哪个

补充:并不是所有的函数都要链接,我们直接有定义时就不需要,直接就有地址,举个例子就是我们借10w块钱后,好兄弟挂了电话后就把钱直接转给我们

Windows 下名字修饰规则

 我们把StackPush的定义屏蔽后编译

会出现这也的报错, ?StackPush@@YAXPAUStack@@H@Z,这些我们正常情况是很难看懂的,除非有文档对照,而g++的就很明了了,大家感兴趣可以去稍微了解一下

extern “C”
由于 C C++ 编译器对函数名字修饰规则的不同,在有些场景下可能就会出问题,比如:
1. C++ 中调用 C 语言实现的静态库或者动态库,反之亦然
2. 多人协同开发时,有些人擅长用 C 语言,有些人擅长用 C++
在这种混合模式下开发,由于 C C++ 编译器对函数名字修饰规则不同,可能就会导致链接失败,在该种场景 下,就需要使用extern "C" 在函数前加 extern "C" ,意思是告诉编译器,将该函数按照 C 语言规则来编译。

6.引用

我们在使用C语言时,经常会使用到指针,但是因为指针有时候非常难,所以C++引入了引用

引用 不是新定义一个变量,而 是给已存在变量取了一个别名 ,编译器不会为引用变量开辟内存空间,它和它 引用的变量共用同一块内存空间。
比如: 李逵 ,在家称为 " 铁牛 " ,江湖上人称 " 黑旋风 "

 

 这里的b就是a的别名,&这个符号在C里是取地址(或者与),C++没有引入新的符号,而直接使用了之前的符号,使用引用如上图所示,a和b是指向同一块空间的

 另外,引用是不能这样写的

引用是别名,但不能不告诉是谁的别名,指针是可以不指向的

这个操作并不是将b改为c的别名,而是将c的值赋值给b,此时a也变成200

 

 引用是不能改变指向的(java等语言是可以改变指向的)

我们知道,这样写交换两个数是不能进行交换的,因为形参是实参的拷贝

 

 但是我们改成使用引用,就可以实现了,此时的形参是实参的别名,另外,引用是可以使用缺省的,我们后面会详细讲解

指针的交换也是可以的 

我们之前在完成链表时,使用的是二级指针,此时再回来看,我们就可以使用引用完成了,这也是很多书上写的,这些书都夹带了一些私货,这些代码都是需要使用C++才能跑起来的

 有些书甚至是这样写的

很多基础不好的同学就直接看懵了,为什么学链表连指针都没有?就是这个原因 

我们总结一下引用的特性

1. 引用在 定义时必须初始化
2. 一个变量可以有多个引用
3. 引用一旦引用一个实体,再不能引用其他实体

 下面我们来看看引用的使用场景

一个是做参数(输出型参数)

输入型参数是传给你用的,输出型参数是改变后也会影响外面

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

另外,C++是可以这样写结构体的

在C语言里,我们的next指针前还必须有struct,但是在C++里就不需要了,这是因为C++把结构体升级成了类,可以直接使用类名

引用做参数的第二层意义还有提高效率(大对象、深拷贝)

大对象顾名思义,就是sizeof时比较大的对象,比如

 这个对象有4w字节

#include<iostream>
using namespace std;

struct A { int a[10000]; };
void TestFunc1(A a) {}
void TestFunc2(A& a) {}
void TestRefAndValue()
{
	A a;
	// 以值作为函数参数
	size_t begin1 = clock();
	for (size_t i = 0; i < 10000; ++i)
		TestFunc1(a);
	size_t end1 = clock();
	// 以引用作为函数参数
	size_t begin2 = clock();
	for (size_t i = 0; i < 10000; ++i)
		TestFunc2(a);
	size_t end2 = clock();
	// 分别计算两个函数运行结束后的时间
	cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
	cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}

int main() {
	TestRefAndValue();
}

这段代码可以对比效率,我们来看运行结果

 引用不会开空间,形参就是实参的别名

当对象更大时,效率差距也更大

不过引用可以做到的事情,指针也可以做到,只是指针稍微麻烦点而已,并没有质的提升,另外,引用并不能完全替代指针,我们这里不做讨论

引用还能做返回值

我们先看这段代码

 这里的n并不能直接返回,会先生成一个临时变量,有可能会用寄存器代替(不一定是寄存器,数据量小的时候有可能),再将临时变量给ret,那这里为什么会生成一个临时变量呢?因为出了栈帧就销毁了,但是我们上面的代码n是加了static的,n是在静态区的,不会销毁,但返回n时任然会生成临时变量,这里不会做特殊处理,如果我们不想生成临时变量怎么办?所以就有了下面的内容

引用做返回值

 这里返回的是n的别名,n的别名,如果这里的n是大对象,或者是深拷贝时,就有很大的效率提升

#include<iostream>
using namespace std;
struct A { int a[10000]; };
A a;
// 值返回
A TestFunc1() { return a; }
// 引用返回
A& TestFunc2() { return a; }
void TestReturnByRefOrValue()
{
	// 以值作为函数的返回值类型
	size_t begin1 = clock();
	for (size_t i = 0; i < 100000; ++i)
		TestFunc1();
	size_t end1 = clock();
	// 以引用作为函数的返回值类型
	size_t begin2 = clock();
	for (size_t i = 0; i < 100000; ++i)
		TestFunc2();
	size_t end2 = clock();
	// 计算两个函数运算完成之后的时间
	cout << "TestFunc1 time:" << end1 - begin1 << endl;
	cout << "TestFunc2 time:" << end2 - begin2 << endl;
}
int main() {
	TestReturnByRefOrValue();
}

 这是效率对比代码,我们来看运行结果

 效果还是非常明显的

那我们后续都可以用引用返回吗?我们来看下面的一些例子

 这段代码其实就有很大的问题,如果我们在这里打印ret,结果是不确定的,这里是一个野指针的问题

我们用引用返回的是n的别名,但是这里n所在的空间已经销毁了,在这个空间里的数据是没有保证的,如果count函数结束,没有清理栈帧,那么ret的结果侥幸是正确的,如果清理了栈帧,ret的结果就是随机值

我们再看这段代码,这个写法的意思是,ret是个引用,返回的是n也是引用,就是我们上面说的多次取别名,ret就是n的别名

我们先打印他们的地址,是一样的,说明是同一块空间

为了更好的演示,我们给count函数加个参数,我们会看到这样的结果

 这里的11和21也有可能会是随机值,原理之前相同

 我们第二次不调用count,而是随便调用一个别的函数,这里都变成随机了,这就是因为后面函数的栈帧覆盖原来的栈帧,在同一个空间的位置,ret被覆盖了(不知道函数栈帧的同学建议去学习一下函数栈帧,后续会经常提到这个概念)

用引用做返回值是很危险的,但是上面我们用static修饰n时是不危险的,栈帧销毁后n仍然存在

简单总结,出了作用域,在栈帧里面就很危险,不在栈帧里就不危险,传值返回有两次拷贝,先拷贝给临时变量,再将临时变量拷贝到接收值里,传引用返回没有拷贝

基本任何场景都可以用引用传参,但是要谨慎用引用做返回值,出了函数作用域,对象不在了,就不能用引用返回,对象还在就可以用引用返回

可以用引用返回的我们来举些例子,比如static,全局的,malloc的等等

我们来看一个例子

 我们在C语言阶段,要完成这些内容,需要按照上面的方式这样做,有人可能觉得没什么,那我们再看看C++如何完成

是不是比起C语言来舒服了很多?我们用一个函数就可以完成这么多的功能

 这也就是引用做返回值的第二个能力,修改返回值+获取返回值

我们前面说过,在C++里结构体被升级成了类,所以在C++里,我们真的要实现上面的功能,还能更简单一点

这些我们后面都会详细讲解

我们再看一个常引用的问题

这里的b并不能成为a的引用,a自己都不能改变,b就更不可能变成a的别名改变a了

引用的过程中,权限是不能放大的

这个是正确的,这里是一个拷贝,d的改变不会影响c

 这里是可以的,引用的过程中,权限可以平移或者缩小,x自己本身可读可写,变成z是只读,这就是权限的缩小(此处缩小的是z的权限)

++x是可以的,但是++z是不行的 

这条语句是可以的,是权限的平移,都是不能修改的 

我们可以将a拷贝给b,这是类型转化问题,但是c不能做a的引用

但是我们加上const就可以了

在a拷贝给b时,也创建临时变量(发生类型转化都有临时变量),会先把a给临时变量,再把临时变量给b,下面a给c时,也是有临时变量的,临时变量为什么不可以给引用呢?因为临时变量具有常性,所以加上const就可以了

我们接着往下看

为什么这里的ret1可以接收,而ret2不可以接收呢?

因为这里返回的也是临时变量,临时变量具有常性,这里发生了权限放大

我们加上const就可以了 

这三个都是正确的,ret2是权限的平移,ret3是权限的缩小(记住引用做返回值不会产生临时变量) ,另外记住,相同类型不会产生临时变量,类型转换才会(即不同类型)

 if语句里,>两边的类型不同,使 i 发生类型提升,提升并不是将 i 本身提升,而是产生临时变量,

这个临时变量是double的,再用临时变量和 j 去比较

我们再看下一个问题

引用在语法层面上不开空间,ra是对a取别名,而pa是指针,需要开空间,存储a的地址

我们对ra改变和对*pa的改变,a都会跟着改变

 我们从底层来看,lea是取地址的意思,放到exa寄存器里

我们发现底层汇编指令实现的角度来看,引用是类似指针的方法实现的,也就是底层没有引用,只有指针

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

 7.内联函数

我们在调用函数时,是有很多消耗的,比如建立栈帧等等

假如我们这里有一个函数,我们要频繁的调用它,就会频繁的建立栈帧

 我们在C语言时,可以用宏来解决,大家还记得如何写宏吗?

 如果大家忘记的话,可以看看我往期的博客

(23条消息) 程序环境和预处理_KLZUQ的博客-CSDN博客

 宏的优势是不需要建立栈帧,提高调用效率,有可维护性的优点

缺点是复杂,容易出错,比如这里可能就有很多人忘记怎么写的,可读性差,不能调试等等

所以在C++里给出了内联函数来解决这个问题,内联函数非常简单,只需在我们的正常函数前加一个关键字就行

 这个关键字就是inline,内联函数不需要建立栈帧,可读性好,不复杂,可以调试,那我们是否可以将所有的函数改成内联函数呢?

答案是不行的,内联和宏一样,是会展开的,适用场景都是短小的,频繁调用的

我们假设有一个函数func,编译好后的指令有50行,我们有一个项目,有一万个位置调用这个函数,如果func不是内联,合计10000+50条指令(我们之前看到函数调用都是call指令),但如果是内联,那就是10000*50条指令了,是非常恐怖的,会使可执行程序变大,如果我们是开发软件的话,会让安装包变大

由于害怕有人滥用inline,其实inline对于编译器仅仅只是一个建议,最终是否成为inline由编译器决定

一些比较长的函数都不会成为内联(一般编译器设置为5行或者10行),递归也不会成为内联

我们来看看成为内联是什么状态

我们发现,这里还是call,这是为什么呢? 

因为默认debug版本下,内联不会起作用,否则无法调试了,所以这里需要我们设置一下

 设置完后重新编译就行了

 此时我们再看就没有call add了

 下面的call调用的是别的东西,不是add,我们之后会讲

我们可以加长代码,干扰编译器,让add不再是内联 

我们接着往下看

此时我们编译就会报错,无法解析的外部符号,我们知道这是链接错误,这是什么情况?

有定义为什么找不到,这是因为main函数里编译后发现func是内联函数,是内联函数的话就需要展开,但是想展开却只有声明,那就只能call地址了,但是,内联函数是不会进符号表的,不会生成地址,因为内联函数是直接展开,所以不会进符号表

所以,内联函数的声明和定义不能分离

8.auto关键字(C++11)

auto是一颗糖果,我们先来简单看看它的用法

 auto可以根据右边的表达式来自动推导类型,比如上面的c和d的类型

我们可以使用typeid来打印类型

 我们来看个例子

这是以后我们要学习这样的代码,it前面的内容是不是很长?我们此时就可以使用auto

是不是就非常舒服了? 

甚至还有这样的代码

 这就是auto的作用

不过,auto不能作为函数的参数

auto不能直接用来声明数组 

为了避免与 C++98 中的 auto 发生混淆, C++11 只保留了 auto 作为类型指示符的用法
auto 在实际中最常见的优势用法就是 C++11 提供的新式 for 循环,还有 lambda 表达式等进行配合使用。
我们再来看一些东西

auto可以根据右边表达式自动推导类型,但是上面的b,就指定必须是指针类型,c就指定了是引用 

比如这里的b,如果不是指针就会报错 

 9. 基于范围的for循环(C++11)

这是我们C语言时访问数组的方式 

这是C++访问数组的方式

这种方式叫做范围for,他会依次取数组中的数据赋值给e,自动迭代,自动判断结束

那我们可以用它修改数组数据吗?

答案是不可以,因为这是将数组里的值赋值给e,e的改变并不会影响数组

但是我们用引用就可以解决这个问题,此时的e就依次是数组里元素的别名,所以就可以修改了

而且名字不一定叫e,大家取喜欢的就行,并且前面的类型不用auto也可以,我们的数组本身就是int,用int也可以,但是我们推荐使用auto,因为数组变时,auto也会自己跟着变,而不用我们去下面改代码

 这样是错误的,因为参数array[ ]的本质还是指针,我们是不知道数组范围的

for 循环迭代的范围必须是确定的
对于数组而言,就是数组中第一个元素和最后一个元素的范围 ;对于类而言,应该提供 begin end 的方法,begin end 就是 for 循环迭代的范围。

10.指针空值nullptr(C++11)

我们先看一个问题

 我们看到,NULL和0都调用的是第一个f,而强转为int*后的NULL才会调用第二个f

NULL 实际是一个宏,在传统的 C 头文件 (stddef.h) 中,可以看到如下代码
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif

所以在C++11为了解决这个问题,引入了nullptr

 

1. 在使用 nullptr 表示指针空值时,不需要包含头文件,因为 nullptr C++11 作为新关键字引入的
2. C++11 中, sizeof(nullptr) sizeof((void*)0) 所占的字节数相同。
3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用 nullptr

以上就是本期的全部内容,希望大家可以有所收获

如有错误,还请指正 

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

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

相关文章

算法记录 | Day37 贪心算法

738.单调递增的数字 思路&#xff1a; 1.一旦出现strNum[i - 1] > strNum[i]的情况&#xff08;非单调递增&#xff09;&#xff0c;首先想让strNum[i - 1]–&#xff0c;然后strNum[i]给为9&#xff0c;这样这个整数就是89&#xff0c;即小于98的最大的单调递增整数。 2…

初始VUE

目录 什么是vue vue的特点 前置js基础知识 vue2 安装vue devtools 搭建开发环境 Vue对象的el及data写法 el的处理 data的处理 特点 什么是js表达式&#xff0c;js代码&#xff08;语句&#xff09; vue脚手架&#xff08;vue cli&#xff09; 使用说明 具体步骤 …

瑞吉外卖:软件开发基础和项目介绍

文章目录 软件开发基础软件开发流程角色分工软件环境 瑞吉外卖项目介绍项目介绍开发流程技术选型功能架构角色 软件开发基础 软件开发流程 需求分析&#xff1a;产品原型&#xff08;大体结构、页面、功能等&#xff09;和需求规格说明书设计&#xff1a;产品文档、UI界面设计…

计算机组成原理——第五章中央处理器(上)

半生风雨半生伤&#xff0c;半醉半醒半心凉 文章目录 前言5.1 CPU的功能和基本结构5.2 指令周期的数据流5.3.1 单总线结构5.3.2 专用通路结构 前言 之前我们就说过CPU主要包括两个部分&#xff0c;运算器和控制器&#xff0c;运算器主要是实现算数运算.逻辑运算&#xff0c; 运…

python正则表达式与re模块

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起探讨和分享Linux C/C/Python/Shell编程、机器人技术、机器学习、机器视觉、嵌入式AI相关领域的知识和技术。 python正则表达式与re模块 正则表达式元字符① . 通配符② ^③ $④ *⑤ ⑥ ?⑦ {}⑧ []⑨ \ 转义符⑩…

python 怎么使用pip进行包管理

包管理工具是用来对一些应用程序的包进行管理的工具&#xff0c;比如nodejs使用npm&#xff0c;yarn来进行包管理&#xff0c;linux使用apt来进行包管理。python包管理工具或许不如他们有名&#xff08;实际上pip的大名比前几位更响亮&#xff09;&#xff0c;但绝对比他们好用…

java超市会员积分管理系统

本课题要求采用B/S结构和主流的jsp技术&#xff0c;以java为开发语言&#xff0c;结合后台数据库mysql&#xff0c;并配合使用了MyEclipse等工具软件&#xff0c;完成超市会员管理系统的设计与后台管理功能开发。 该系统主要功能如下&#xff1a; 会员信息的管理&#xff1a;包…

MySQL数据库:数据库表的设计

一、ER实体关系图 1.概念 百度百科&#xff1a; ER图是用来描述某一组织(单位)的概念模型&#xff0c;提供了表示实体、属性和联系的方法。构成ER图的基本要素是实体、属性和关系。 ER图是用来描述某一组织(单位)的概念模型&#xff0c;提供了表示实体、属性和联系的方法。构…

asp.net房屋租赁管理系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio

一、源码特点 asp.net房屋租赁管理系统 是一套完善的web设计管理系统&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为vs2010&#xff0c;数据库为sqlserver2008&#xff0c;使用c#语言 开发 asp.net房屋租赁管理系统VS开发s…

如何搭建用户生命周期模型

如何搭建用户生命周期模型 摘要一、问题背景二、用户生命周期模型1、用户生命周期模型简介2、对用户生命周期认知的几点误区 三、如何搭建并应用用户生命周期模型1、使用场景2、搭建步骤&#xff08;1&#xff09;梳理用户成长路径&#xff08;2&#xff09;寻找驱动用户进入下…

【动力节点】springsecurity课程笔记6-13章

** 6 密码处理 6.1 为什么要加密&#xff1f; csdn 密码泄露事件 泄露事件经过&#xff1a;https://www.williamlong.info/archives/2933.html 泄露数据分析&#xff1a;https://blog.csdn.net/crazyhacking/article/details/10443849 6.2加密方案 密码加密一般使用散列函…

ATTCK v12版本战术实战研究——提权(二)

一、前言 前几期文章中&#xff0c;我们介绍ATT&CK 14项战术中提权战术&#xff08;二&#xff09;&#xff0c;包括提权前7项子技术。那么从前文中介绍的相关提权技术来开展测试&#xff0c;进行更深一步的分析。本文主要内容是介绍攻击者在运用提权技术时&#xff0c;在…

ZLMediaKit实现按需拉流时rtsp流地址不对addStreamProxy返回0,接口流id参数踩坑记录

场景 开源流媒体服务器ZLMediaKit在Windows上运行、配置、按需拉流拉取摄像头rtsp视频流)并使用http-flv网页播放: 开源流媒体服务器ZLMediaKit在Windows上运行、配置、按需拉流拉取摄像头rtsp视频流)并使用http-flv网页播放_霸道流氓气质的博客-CSDN博客 基于上面实现拉取视…

vue_02

文章目录 安装axios配置响应拦截器&#xff08;对响应做统一处理&#xff09;解决跨域问题登录token问题首先在Login.vue中添加下列代码然后在api.js中添加请求拦截器 页面加载动画的添加请求方式的全局配置在api.js中添加四种请求在main.js中配置全局 Login.vue完成代码 安装a…

前端实战(三):element-ui的二次封装

目录 二次封装 Switch 开关 原始效果 设计效果 实现步骤 在日常开发过程中&#xff0c;大多数项目主要以 vue 为主&#xff0c;并且现在很多公司仍在使用着 vue。但在使用element-ui组件时通常会遇到一些问题&#xff1a;如组件样式与设计不符合、组件不存在某个功能等等&a…

都已经那么卷了,用户还需要开源的 API 管理工具么

关于 API 管理工具&#xff0c;如今的市场已经把用户教育的差不多了&#xff0c;毫不夸张地说&#xff0c;如果我随机抽取一位幸运读者&#xff0c;他都能给我罗列出一二三四款大家耳熟能详的工具。可说到开源的 API 管理工具&#xff0c;大家又能知道多少呢&#xff1f; 我们是…

计算机网络复习题+答案

文章目录 导文题目一、单项选择题二、填空题三、判断改错题,判断下列命题正误,正确的在其题干后的括号内打“√”,错误的打“”,并改正。四、名词解释五、简答题六、应用题导文 计算机网络复习题 题目 一、单项选择题 在应用层协议中,主要用于IP地址自动配置的协议是: (…

一文讲清莱迪斯 LCMXO2-4000HC-4BG256I 可编程逻辑FPGA 特性及运用领域

一文讲清lattice莱迪斯深力科 LCMXO2-4000HC-4BG256I 可编程逻辑FPGA 特性及运用领域 适用于低成本的复杂系统控制和视频接口设计开发&#xff0c;满足了通信、计算、工业、消费电子和医疗市场所需的系统控制和接口应用。 瞬时启动&#xff0c;迅速实现控制——启动时间小于1m…

605. 种花问题

假设有一个很长的花坛&#xff0c;一部分地块种植了花&#xff0c;另一部分却没有。可是&#xff0c;花不能种植在相邻的地块上&#xff0c;它们会争夺水源&#xff0c;两者都会死去。 给你一个整数数组 flowerbed 表示花坛&#xff0c;由若干 0 和 1 组成&#xff0c;其中 0…

Mybatis核心组件简介

文章目录 前言一、Configuration二、MappedStatement三、SqlSession四、Executor五、StatementHandler六、ParameterHandler七、ResultSetHandler八、TypeHandler总结 前言 SqlSession是MyBatis提供的面向用户的操作数据库API。那么MyBatis底层是如何工作的呢&#xff1f;为了…