1

C++入门知识详解(2)

 1 year ago
source link: https://blog.51cto.com/u_15466618/6851752
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

一、函数重载

1.什么是函数重载

重载的字面理解是重新载入的意思,也就是重新赋予某个事物新的含义,也就是所谓的一词多义。

而同名函数的形参个数或者形参类型或者形参顺序不同,那么就叫做函数重载。其中对函数的返回值并没有要求,也就是说函数返回值相同与不同不影响是否构成函数重载。

其实,函数重载就是对于函数的一词多义,两个或者多个函数的函数名相同,但是函数的功能却不同。

2.函数重载的几种情况

形参个数不同,构成重载。

同名函数,一个有参数,一个没有,构成函数重载;当把参数设置为缺省参数时,虽然依旧构成函数重载,但在无参调用函数时,会有歧义,导致编译错误。如下图

C++入门知识详解(2)_引用
C++入门知识详解(2)_函数重载_02

形参类型不同,构成重载。如下图

C++入门知识详解(2)_函数重载_03

形参顺序不同,构成重载。如下图

C++入门知识详解(2)_C++基础详解_04

返回值对函数重载无影响

函数名相同,函数参数顺序不同,构成重载,返回类型不同,依然构成重载。如下图

C++入门知识详解(2)_函数重载_05

3.C++为什么支持函数重载

3.1编译和链接

一个程序要想运行起来,必须经过编译和链接,每一个过程都有对应的功能。这里我们简单了解即可。

C++入门知识详解(2)_C++基础详解_06

通过编译过程,项目中的每个源文件都会在编译器的作用下生成对应的目标文件。也就是说在编译阶段,每个源文件之间是互不干扰的。而在链接阶段,会在链接器和生成的多个目标文件的作用下,生成一个可执行程序,也就是说,链接过程使得原本没有交集的源文件产生了交集。所谓的交集可以理解为,源文件之间彼此是互通共享的。如下图

C++入门知识详解(2)_C++基础详解_07

3.2链接的重要性

一个项目可能包含多个源文件和头文件,如下图

该项目包含两个源文件,一个头文件,其中重载函数的声明在头文件func.h当中,定义在源文件func.cpp中,源文件func.cpp和test.cpp都包含了头文件func.h。

C++入门知识详解(2)_函数重载_08
C++入门知识详解(2)_函数重载_09
C++入门知识详解(2)_C++基础详解_10

在上述项目的源文件test.cpp中,调用了在func.cpp中定义的函数,而test.cpp本身只包含了函数的声明(头文件)。那么函数的声明和定义在两个不同的源文件当中,该程序可以跑起来吗?答案是可以的,上面提到过,在链接的作用下,各个源文件之间是互通共享的,因此虽然调用函数的声明和定义不在一个源文件,但在链接的作用下,会找到是哪个源文件当中存在对应函数的定义。所以可以运行。如下图

C++入门知识详解(2)_函数重载_11

当我们把func.cpp中函数的定义注释后,因为没有函数定义,所以链接过程会因为找不到函数定义而无法运行。如下图

C++入门知识详解(2)_引用_12
C++入门知识详解(2)_引用_13

3.3函数名修饰规则

由前文所说,我们知道了在链接的作用下,可以找到不同源文件中的函数定义,以此成功通过调用函数,成功运行程序。

那么如果存在函数重载,出现同名函数的情况下,如何找到我们想要的重载函数呢?

这里需要了解函数名修饰规则。可以将其理解为,经过编译过程后,会将原本的函数名根据其独特的函数参数做出一定修改,而构成函数重载的必要条件就是函数参数不同(参数顺序,参数个数,参数类型),因为函数的参数不同,所以在对函数名进行修改时也会不同,以此可以区分两个同名函数,也就可以支持函数重载。

不同的编译器,函数名修饰规则不同。linux下的修饰规则较为简单明了,我们来看一下。

在linux下创建并使用vim写好func.h,func.cpp,test.cpp。如下图

C++入门知识详解(2)_函数重载_14
C++入门知识详解(2)_函数重载_15

使用makefile写入依赖关系。如下图

g++是C++的编译器,$@表示:左边的内容, $^表示:右边的内容

g++ -o $@ $^ 表示的意思就是,用g++编译器由func.cpp和test.cpp编译生成可执行程序test.exe。

虽然makefile中并没有func.h,但make指令执行时实际上会在当前路径下自己寻找,所以效果就相当于由func.h,func.cpp,test.cpp生成可执行程序test.exe

C++入门知识详解(2)_函数重载_16

在makefile写下依赖关系后,每次使用这条指令只需要输入make即可自动执行该语句,并且生成可执行程序 test.exe,使用./test.exe也可以运行该程序。如下图

C++入门知识详解(2)_引用_17

当准备工作完成后,就可以使用指令objdump -S test.exe (该指令会尽可能将源代码生成汇编代码)来查看是否存在函数名修饰的情况发生(根据参数对函数名做出修改)。如下图

C++入门知识详解(2)_引用_18

对于汇编代码我们无需理会,我们只要看到<_Z4funcii> 和<_Z4funcdd>即可

我们不难看出他们的共同点,都有_Z4和func,其中func是函数名,而_Z是函数修饰规则的一个默认前缀,数字4表示的是原本函数的长度

再看ii和dd,不难联想到,这其实就是函数参数类型的首字母。

也就是说,在c++编译器下,确实是存在函数名修饰规则的,这也就使得,即使是重载函数也会因为参数不同,导致在函数名修饰规则的作用下变得有所不同,也因此C++支持函数重载

3.4C语言为什么不支持函数重载

C语言没有函数名修饰规则来修饰同名函数,所以不支持函数重载

同样的方法,再来查看,可以发现,C语言编译器下,函数名依旧是其本身,没有任何的修饰和修改,所以C语言无法区分同名函数,也因此不支持函数重载

C++入门知识详解(2)_函数重载_19
C++入门知识详解(2)_C++基础详解_20
C++入门知识详解(2)_C++基础详解_21

1.什么是引用

1.1引用的概念

引用不是新定义一个变量,而是给已存在的变量取一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共同用一块内存空间。

1.2引用符号与语法

引用符号为&,在C语言中的取地址符号,在C++中,也是引用符号

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

1.3引用的使用要求

前言:引用类型必须和引用实体是同种类型的。如果不同,则会报错。如下图

C++入门知识详解(2)_引用_22

引用在定义时必须初始化。如下图

引用不初始化会报错,初始化则正常

C++入门知识详解(2)_函数重载_23
C++入门知识详解(2)_函数重载_24

一个变量可以有多个引用,但是一个引用只能引用一个实体。也就是说一个变量可以有多个别名,而一个别名只能对应一个实体。如下图

C++入门知识详解(2)_引用_25
C++入门知识详解(2)_函数重载_26

2.引用需要注意的地方

2.1关于引用

引用就相当于萧炎化名岩枭一样,无论是哪个名字,他们都是同一个人。

如下图,引用实体和几个引用变量的地址都是相同的,对引用实体做出改变时,引用变量也会改变

C++入门知识详解(2)_函数重载_27
C++入门知识详解(2)_引用_28

2.2引用的一些问题

1.引用还是赋值

前情提要:引用只能认定一个引用实体,不能更改成为另一个变量的别名。

在C++中,语句c=b,是将b的值赋值给c

C++入门知识详解(2)_引用_29

同时,牵一发而动全身,别名和引用实体实际上是同一个身份,因此a和c都会被更改。如下图

C++入门知识详解(2)_函数重载_30
2.不同类型能否被引用

在引用的使用要求中提过,引用实体的类型和引用变量的类型必须相同才能使用引用。

引用实体为double类型,引用变量是int类型时,确实不能引用,如下图

C++入门知识详解(2)_函数重载_31

在发生类型转换的时候,拷贝的是右值的临时变量。如下图

b拷贝给c,类型不同,使用发生类型转换,b会产生一个临时变量,而临时变量具有常性,常性可以理解为是被const修饰

C++入门知识详解(2)_函数重载_32

类型不同不能引用只是一部分原因,根本原因是引用时,权限不能放大

C++入门知识详解(2)_引用_33

上图中,引用实体和引用变量的类型不同时,并不能引用,当在引用变量的类型前添加const时,可以成功通过。这是因为,c引用b时,因为类型不同,会发生隐式类型转换,而隐式类型转换会产生一个具有常性的b的临时变量,而引用过程权限不能放大,只能平移或者缩小,因此添加const即可通过

2.3常引用

前情提要:在引用过程中,权限不能放大,只能平移或者缩小权限放大,如下图

C++入门知识详解(2)_C++基础详解_34

a有const修饰,只有读权限,拿b做引用时,b的类型确是int ,读写权限都有,因此不可以引用

权限平移,如下图

C++入门知识详解(2)_引用_35

a,b的类型完全相同,都是const int,权限也都是可读不可写,可以引用

权限缩小,如下图

C++入门知识详解(2)_函数重载_36

a的权限是可读可写,但是引用b的权限是可读不可写,权限缩小,这是可以的

此时仍然可以对引用实体a进行写操作,但是不可以对b进行写操作。如下图

C++入门知识详解(2)_函数重载_37

可以理解为别名的身份没有本体混得好(权限缩小),所以处于别名身份时,会受到约束,而本体不会受到约束

2.4别名的别名

给某个引用实体起别名,再给这个别名起别名,新起的别名也是引用实体的别名,三者本质相同。如下图

C++入门知识详解(2)_C++基础详解_38

3.引用的用途

3.1引用做参数

3.1.1引用做参数的好处
1.输出型参数(形参的改变可以改变实参)

在C语言中,参数传递分为值传递和地址传递,当我们想要通过改变形参进而改变实参时,必须采用地址传递的方法。

而在C++中,可以使用引用做参数,此时引用不需要初始化。因为引用只是起别名,所以形参的改变也会改变实参。如下图

C++入门知识详解(2)_C++基础详解_39

其次,在不带头的单链表中对链表进行增删的时候,函数参数采用的都是二级指针,现在有了引用,可以直接使用引用做参数,来改变实参。

关于typedef struct SingleLinkedListNode{}*node;

node就是一个结构体指针,它的类型为struct SingleLinkedListNode*

C++入门知识详解(2)_函数重载_40
C++入门知识详解(2)_C++基础详解_41

我们只将头插函数的形参改为引用,查看结果即可。同时,头插函数也需要做出相应修改。因为链表头指针的类型为struct SingleLinkedListNode*,所以引用的类型也是struct SingleLinkedListNode*,也就是Node。如下图

C++入门知识详解(2)_函数重载_42
C++入门知识详解(2)_函数重载_43

函数调用更改为引用做形参的头插函数,和二级指针的效果一样。如下图

C++入门知识详解(2)_引用_44
2.提高效率(大对象)

使用引用做形参,一定程度可以提高效率。如下图

C++入门知识详解(2)_函数重载_45

3.2引用做返回值

3.2.1值作为返回值

值作为返回值.如下图

C++入门知识详解(2)_函数重载_46

可以看到,我们在函数func内创建了变量a,并且加上了static修饰,使得局部变量改为在静态区存储,因此变量a在func函数调用结束后也不会销毁。

实际上,值作为返回值,无论变量a是出了函数作用域就会被销毁的局部变量,还是不会被销毁的静态区变量,最后返回的都并不是a本身,而是a的一个临时拷贝的值,最后将这个拷贝的值赋值给ret。

3.2.2引用作为返回值的问题

引用作为返回值时,返回的就会是变量本身,而不是变量的临时拷贝。如如下图

C++入门知识详解(2)_C++基础详解_47

此时,变量a就必须加上static使其函数调用结束后不会被销毁。因为func函数返回的是变量a的别名,如果a是局部变量,函数调用结束后,func的函数栈帧也会被销毁,a也不复存在,也不会有引用一说。

但实际上,函数调用后函数栈帧空间更准确来说应该是归还。因此,如果函数调用结束后,如果没有调用别的函数,那么栈帧原本的栈帧空间不会被覆盖,也就是不会被清理,那么会正确返回变量的别名,如果栈帧已经被清理(覆盖),那么返回的将是一个随机值。

也因此,对局部变量进行引用返回时,是一件不靠谱且危险的事情,要慎用引用返回。如下图

C++入门知识详解(2)_引用_48
3.2.3用引用接收引用
C++入门知识详解(2)_函数重载_49

函数func的返回值是引用,接收引用的变量ret也是一个引用,此时是什么情况呢?其实就是上文提到的引用的引用

函数func返回的是变量a的引用,而ret是返回的引用的引用,也就是说,ret也是变量a的引用,ret和a共用同一空间。如下图

C++入门知识详解(2)_函数重载_50
3.2.4引用做返回值的好处

和引用做参数一样,引用做返回值时,也无需进行拷贝,尤其是返回对象较大时,因此也可以提升效率

3.2.5关于引用的小结

在任何情况下,都可以拿引用做参数。但是引用的使用需要慎重。

这是因为,引用做返回值时,如果引用的是局部变量,返回是可能会返回随机值,使用存在风险。

引用做返回值时,全局变量,静态变量,或者malloc出来的都可以引用返回,因为他们不会被立即销毁。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK