缺省函数,函数重载,引用

缺省函数

缺省函数又分为全缺省函数和半缺省函数,这里的半缺省不是指缺一半的参数,而是部分缺省。

缺省函数可以理解为:在形参内,存在赋值的参数称为缺省,可以理解为这个参数是备胎。在实参调用时,假如这个有实参那么就以实参值为准,若没有实参,则是形参的赋值。

全缺省函数

在调用函数的时候,实参可以为空。也就是说,在定义这个函数的时候,这个函数的形参自己已经赋值了。

半缺省函数 

缺少部分参数 (必须从右往左连续缺省),而函数调用时的实参,必须从左往右传参。

因为假如形参存在缺省,那么那个缺省的形参可以不传值,如果没有缺省,那么实参必须传值。

假如形参的缺省不符号从右往左连续缺省,那么实参的传值就无法判断是传给哪个形参。

函数重载 

不同意C语言,C++可以定义多个函数名相同的函数。但是他们的参数有三不同,顺序不同or类型不同or数量不同。(只是返回值不同,不能构成函数重载)

程序运行的流程

概念很简单,有一个面试题:为什么函数重载在C语言不可以,但是C++却可以?

一个程序在编译到链接有四个过程:

1、预处理 -> 头文件的展开/宏替换/条件编译/去掉注释

2、编译  ->  检查语法,生成汇编代码

3、汇编  ->  汇编代码转成二进制的机器码

4、链接  -> 将两个目标文件链接到一起

一般来说一个项目会有三个文件:一个包含头文件,一个是函数的实现,一个是主函数的逻辑。

不妨令他们分别为list.h ;list.c ;test.c。(list.h中包含了头文件,函数的声明)list.c 和test.c 只需要包含list.h即可。

在预处理的过程中,会把list.c 和 test.c 变为 list.i 和 test.i。

编译过程会把 list.i 和 test.i 转化为list.s 和 test.s

汇编过程会把list.s 和 test.s 转化为 list.o 和 test.o

最后把这两个文件链接到一起。

test.c -> test.o的过程,首先会把头文件展开,这个主函数里面有调用函数的指令,但是在编译的时候并不会执行,之后检查是否有语法错误,在预处理的时候,就已经把头文件展开了,也就相当于这个主函数前面有函数的声明。也就不会报错。再到汇编,转化为二进制的机器码,这个时候调用这个函数也是不需要访问地址的,也没有拿到这个函数的地址,因为这个函数的定义的地方在list.c中,两个文件还没有链接,所以找不到。

两个文件变为汇编之后,会有一个符号表,这个符号表内会有两个文件中的函数名以及这个函数的地址(函数的定义是有多行代码的,这个地址指的是这个函数定义的代码中的第一行代码的地址)。test.o中有:mian:0x32321412       list.o中有:add:0x35637182(假如有一个add函数)

然后通过链接,把两个文件链接到一起,生成一个可执行程序。然后将在编译过程中,把没有访问函数地址的地方,去其他文件中寻找这个函数的地址。如果没找到就报错(链接错误)

而为什么C语言不支持重载,C++支持重载的原因就在这。c++有一个函数名修饰规则。

C语言(在Linux中gcc之后)函数名并没有变,而c++(g++之后)函数名变了,变为【_z+函数长度+函数名+类型首字母】。比如一个函数的声明是:int add(int a, int b)。g++之后变成_z3addii。假如参数是引用,比如int& a。这个类型的首字母和int的一样,为i。假如是int* a。那么就为pi。

注意这个名字修饰规则是在linux下的,在windows下比较复杂,这里不与讲述。

假如是C语言,它汇编之后,函数名没有被修饰,那么在调用的时候就不知道该调用哪个了。而c++引用了这个名字修饰规则,不同的参数 ,不同的顺序,就会有不同的函数名,所以可以找到自己该访问的函数地址。

那么还有一个问题:既然C++和C的函数名修饰不同,那么我如何写一个函数,既可以让C++使用,也可以让C使用呢?在函数声明前加上extern "C"

一个例子 Google写了一个函数,tcmalloc()。这函数C和C++都可以使用。这个函数的声明是这样的:extern "C" void* tcmalloc(size_t n)。假如是没有extern "C"的化,在汇编之后的符号表会是这样的:0x地址 :_z8tcmallocui。C++能找到这个地址,C语言是按照tcmalloc去找,显然找不到。那么加上extern "C"之后,这个符号表就变成了:tcmalloc。C就能找到,而C++看见这个函数声明前有extern "C",那它也按照C的方式去找。当然,tcmalloc不可以重载。

引用

定义

引用就是一个变量的别名,不开辟空间,也指向初始化的空间。

常引用

引用需要符合权限的大小。const的一个变量表示只读,这个时候该变量的引用需要使用常引用

const int a = 1;
const int& ra = a;

引用不可扩大变量的权限,且必须初始化。

隐式转换

int a = 1;
double d = a;

这样的是可以的,因为这个赋值包含一个隐式转换,就是创建一个临时变量double,临时变量具有常性,而且d的修改不会影响a。

int a = 1;
double& rd = a;

这样是不行的, 因为这的rd是一个引用,而临时变量具有常性,所以需要使用

const double& rd = a。 那么这样就不可以修改rd的值了。

引用和指针的区别

1、在概念上,引用定义一个变量的别名,指针存储一个变量的地址

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

3、引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体

4、没有NULL引用,但有NULL指针

5、在sizeof中含义不同:引用的结果为引用类型的大小,而指针始终是地址空间所占字节个数(32位平台下占4个字节)

6、引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小

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

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

9、引用比指针使用起来相对安全

引用做参数和返回值

假如是一般的参数传值和返回传值,都会产生一个拷贝变量,这样会影响效率。那么就诞生了引用

引用作为参数和返回值最大的一个作用就是提高效率。

int add1(int a, int b)
{
    int c = a + b;
    return c;    // const int& ret = add1(a,b)
}

int& add2(int& a, int& b)
{
    int c = a + b;
    return c;
}

int& add3(int& a, int& b)
{
    static int c = a + b; // 只运行一次
    return c;
}

对于add1来说,在传递参数时,会在函数的栈帧创建两个空间a,b. 在最后的返回的时候也会创建一个临时变量,该临时变量具有常性。所以假如在使用引用接收这个返回值的时候需要使用常引用。

对于add2来说,传引用作为参数,不需要开辟空间,但是返回的c,是在栈中创建了空间。返回引用也不需要创建临时变量,用一个引用接收返回值,但是但函数结束的时候,会对该函数的栈帧进行销毁,那么这个c变量的空间就被销毁了,假如再调用另一个函数的时候,该空间会被覆盖。具有风险。

对于add3来说,在add2的基础上给变量c定义为全局变量,扩大生命周期,变量c存储在静态区,那么在函数的栈帧销毁时,对它不产生影响。(注意:static int c = a + b, 这行代码是全局变量的定义,在整个程序的运行过程中,该行只运行一次)

总结:当一个函数要使用引用返回时,需要判断这个返回变量出了这个函数的作用域是否还存在,如果还存在就可以使用引用返回,否则就不安全。

函数使用引用的好处:少创建拷贝一个临时对象,提高效率。

  • 27
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在C++中,我们可以使用缺省构造函数来创建一个对象数组。 缺省构造函数是指在定义类时没有显式定义任何构造函数的情况下,默认生成的构造函数。它不带任何参数,并且没有函数体,其作用是为对象提供默认的初始化。 如果我们将一个类定义为数组的元素类型,可以使用缺省构造函数来创建一个对象数组。例如,假设我们有一个名为Person的类,没有任何构造函数的定义。我们可以通过以下方式创建一个Person对象数组: Person arr[5]; 上述代码将创建一个名为arr的Person对象数组,数组中包含了5个Person对象。由于缺省构造函数的存在,每个元素都会被默认初始化为该类的默认值。具体的初始化规则取决于成员变量的类型和定义。如果在Person类中定义了成员变量的默认值,那么每个元素都将被初始化为这些默认值。 如果我们想要自定义初始化每个元素,可以通过重载构造函数的方式来实现。这样,在创建对象数组时,我们也可以传递参数来初始化每个元素。例如,如果我们想要将数组中的每个Person对象的年龄初始化为25岁,可以定义一个带有参数的构造函数,并使用如下方式创建数组: Person arr[5] = { Person(25), Person(25), Person(25), Person(25), Person(25) }; 上述代码将创建一个Person对象数组,其中每个元素的年龄被初始化为25岁。 总结起来,通过使用缺省构造函数,我们可以方便地创建对象数组,并根据需要进行默认初始化或自定义初始化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值