C++:缺省参数|函数重载|引用|const引用

Harper·Lee 2024-07-14 12:05:02 阅读 90

欢迎来到Harper·Lee的学习笔记!

博主主页传送门:Harper·Lee的博客主页

想要一起进步的uu可以来后台找我哦!

一、缺省参数

1.1 缺省参数的定义

缺省参数:是声明或定义函数时为函数的参数指定⼀个缺省值。在调用该函数时,如果没有指定实参则采⽤该形参的缺省值,否则使用指定的实参。(有些地方把缺省参数叫做默认参数)

<code>#include <iostream>

using namespace std;

void Func(int a = 0)//行参的后面赋一个常量值或者全局变量值,指定参数值

{

cout << a << endl;

}

int main()

{

Func(); // 没有传参时,使⽤参数的默认值

Func(10); // 传参时,使⽤指定的实参

return 0;

}

1.2 缺省参数的分类

1.2.1 全缺省参数

全缺省:全部的行参参数都给缺省值。

#include <iostream>

using namespace std;

// 全缺省

void Func1(int a = 10, int b = 20, int c = 30)//每个参数都有缺省值

{

cout << "a = " << a << endl;

cout << "b = " << b << endl;

cout << "c = " << c << endl << endl;

}

int main()

{

Func1();//不传参数,10,20,30

Func1(1);//传一个参数,1,20,30

Func1(1, 2, 3);//1,2,3

//Func1( ,1, );这种形式不可以,因为是本贾尼规定的

return 0;

}

运行结果:

image.png

1.2.2 半缺省参数

半缺省:部分形参给缺省值。C++规定半缺省参数必须从右往左依次连续缺省,不能间隔跳跃给缺省值。

<code>#include <iostream>

using namespace std;

// 半缺省

void Func2(int a, int b = 10, int c = 20)//半缺省即部分缺省

{

cout << "a = " << a << endl;

cout << "b = " << b << endl;

cout << "c = " << c << endl << endl;

}

int main()

{

//Func2();//不传参数会报错

Func2(100);//

Func2(100, 200);

Func2(100,200,300);

return 0;

}

运行结果:

image.png

1.2.3 缺省参数的应用

缺省参数的应用:比如在创建栈的时候,经常会出现初始化的时候考虑扩容, 不确定要扩多大的空间,因此可以使用缺省参数:不确定扩多大空间,可以通过半缺省使用原先的缺省值,确定扩容的空间时,就直接空间容量作为传入参数。初始化栈的时候就扩容了,效率很高。

缺省参数就像现实中的备胎舔狗一样,别人需要它是才会想到它。

1.2.4 缺省参数的注意点

带缺省参数的函数调用:C++规定必须从左到右依次给实参,不能跳跃给实参。

<code>#include <iostream>

using namespace std;

void Func2(int a, int b = 10, int c = 20)

{

cout << "a = " << a << endl;

cout << "b = " << b << endl;

cout << "c = " << c << endl << endl;

}

int main()

{

Func2(100, ,300);//error

return 0;

}

缺省值必须是常量或者全局变量。C语言编译器不支持缺省。函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须函数声明给缺省值。

//test.h

void Func1(int a = 10);//声明

// test.cpp

void Func1(int a = 20)//定义

{ }//error

//如果生命与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该用那个缺省值。

//test.h

void Func2(int a = 10);//声明中赋缺省值

// test.cpp

void Func2()//定义中没有赋缺省值

{ }//right

二、函数重载

2.1 函数重载的概念

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

好处:同一个函数可以使用不同类型的数据。

2.2 函数重载的分类

2.2.1 参数类型不同

#include<iostream>

using namespace std;

// 1、参数类型不同

int Add(int a, int b)

{

return a + b;

}

double Add(double a, double b)

{

return a + b;

}

int main()

{

cout << Add(1, 2) << endl;

cout << Add(1.1, 2.2) << endl;

return 0;

}

运行结果:

image.png

2.2.2 参数个数不同

<code>// 2、参数个数不同

#include<iostream>

using namespace std;//展开std

void f()

{

cout << "f()" << endl;

}

void f(int a)

{

cout << "f(int a)" << endl;

}

int main()

{

f();//调用的是没有参数的f函数

f(1);//调用的是有参数f函数

return 0;

}

运行结果:

image.png

2.2.3 参数类型顺序不同

<code>// 3、参数类型顺序不同

#include<iostream>

using namespace std;//展开std

void f1(int a, double b)

{

cout << "f1(int a, double b)" << endl;

}

void f1(double a, int b)

{

cout << "f1(double a, int b)" << endl;

}

int main()

{

f1(1, 2.0);

f1(1.0, 2);

return 0;

}

运行结果:

image.png

2.2.4 函数构成重载但调用歧义

<code>// 下⾯两个函数构成重载,但是f()调⽤时,会报错,存在歧义,编译器不知道调⽤谁

#include<iostream>

using namespace std;//展开std

void f3()

{

cout << "f()" << endl;

}

void f3(int a = 10)//缺省参数

{

cout << "f(int a)" << endl;

}

int main()

{

f3();//error函数调用看的是行参,与缺省参数没有关系!因此发生歧义!!!

f3(2);//给了参数就明确说明是要调用第二个函数

return 0;

}

错误列表:

image.png

无参调用时,就没有办法进行区分了,因此不要写无参数函数和全缺省函数重载!!!最好的解决办法就是使用域隔离,此时两者没有在同一个作用域里,不再是函数重载了。

2.2.5 返回值不同不构成重载

<code>#include<iostream>

using namespace std;//展开std

//返回值不同不能作为重载条件,因为调用时也⽆法区分

void fxx()

{ }

int fxx()

{

return 0;

}

假设返回值不同可以构成重载,但是如何判定调用的是有参函数还是无参函数?所以返回值不同不能作为重载条件,因为调用时也无法区分。

三、引用

3.1 引用的概念

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

类型&引用别名=引用对象;

#include<iostream>

using namespace std;

int main()

{

int a = 0;

int& b = a;//引⽤:b是a的别名

int& c = b;//也可以给别名b取别名,c相当于还是a的别名

++c;//就是++b,也就是++a

cout << &a << endl;//&代表取地址

cout << &b << endl;

cout << &c << endl;

cout << a << endl;//&代表取地址

cout << b << endl;

cout << c << endl;

return 0;

}

运行结果:

image.png

3.2 引用的特性

3.2.1 引用定义时必须初始化

<code>int a = 1;

int& b = a;//right,引用b取别名,是a的别名

int& rb;//error,取别名,但是不知道是谁的别名

3.2.2 别名可以取别名

int a = 1;

int& b = a;//b是a的别名

int& c = b;//c是别名b的别名

3.2.3 一个变量可有多个引用

一个变量我们可以起多个别名

int a = 0;

// 引⽤:b和c是a的别名

int& b = a;

int& c = a;

3.2.4 引用一旦引用一个实体,就不能引用其他实体

引用只能给一个变量当别名,不能给多个变量当别名。

int a = 2;

int m = 3;

int& b = a;//这里b引用了实体a

int& b = m;//error,这里b又引用实体m,error

3.2.5 C++引用不能改变指向

C++的引用不能替代指针!!!

#include<iostream>

using namespace std;

int main()

{

int a = 1;

int& b = a;//引用b取别名,是a的别名

int d = 20;

b = d;//这是一个赋值,因为不能改变指向

printf("b = %d\n", b);

cout << &a << endl;

cout << &b << endl;

cout << &d << endl;

return 0;

}

运行结果:

image.png

3.3 引用的运用

3.3.1 作为函数参数

引用传参与指针传参功能相似,引用传参减少拷贝效率,改变引用对象,同时也可以改变被引用对象。

解决了C语言中行参无法影响实参的问题,也比指针传参相对更加方便。

<code>#include<iostream>

using namespace std;

//现在的写法

void Func(int& x)//引用作为函数参数,x是a的别名

{

int m = 20;

x = m;//x被改变,实体a也被改变(引用对象改变了,被引用对象也改变了)

cout << x << endl;

}

//以前的写法

void Func1(int* x)

{

int m = 20;

*x = m;

cout << x << endl;

}

int main()

{

int a = 2;

Func(a);

//Func1(&a);

return 0;

}

运行结果:

image.png

举个例子:引用可以不通过指针(地址),就可以作为函数参数,改变实参。

<code>#include<iostream>

using namespace std;

//C语言中指针类型的Swap函数

void Swap1(int* a, int* b)

{

int tmp = *a;

*a = *b;

*b = tmp;

}

//C++中引用形式的Swap函数

void Swap2(int& rx, int& ry)

{

int tmp = rx;

rx = ry;

ry = tmp;

}

int main()

{

int x = 2, y = 4;

Swap1(&x, &y);..是用来指针

cout << "x = " << x << "," << "y = " << y << endl;

int m = 3, n = 7;

Swap2(m, n);//没有使用指针

cout << "m = "<< m << "," << "n = " << n << endl;

return 0;

}

//分析:在Swap2函数中,rx和ry分别是m和n的别名,用的是同一块空间,

//因此rx和ry改变就相当于m和n的改变

运行结果:

image.png

3.3.2 作为函数返回值

引用作为返回值的场景相对复杂,可以减少拷贝效率,改变引用对象,同时也可以改变被引用对象。这里简单一提,类和对象中深入讨论。

image.png

???

错误示范:

<code>#include<iostream>

using namespace std;

int& Func()

{

int a = 0;

return a;//返回后,函数栈桢被销毁,返回引用,相当于返回一个野指针

}

前两条总结:引用传参和引用做返回值中 1. 减少拷贝提高效率;2. 改变引用对象时;3.改变被

引用对象。

3.4 引用和指针的区别

语法上:引用是给变量取别名,不开空间,它和它引用的对象是同一个空间。指针是存储一个变量的地址,需要开空间。初始化:引用在定义的时候需要初始化,指针定义的时候为了避免野指针,建议初始化。对象改变:引用在初始化时引用一个对象后,就不能再引用其他对象;指针可以不断地改变指向的对象。访问对象:引用可以直接访问对象,指针需要解引用才能访问对象。

#include<iostream>

using namespace std;

int main()

{

int a = 0;

int* p = &a;

*p = 1;//指针需要解引用才能访问对象

int& ra = a;

ra = 2;//引用可以直接访问对象

return 0;

}

安全性:指针容易出现空指针野指针,引用相对安全些。sizeof中含义不同:引用结果为引用类型的大小;指针始终是地址空间所占字节数(32位平台下4Byte,64位平台下8Byte)引用的安全是相对安全。

//错误引用

int& Func()

{

int a = 0;

return a;

}//返回一个类似野指针的东西,相当于是空引用

间接的一个空引用。

int* ptr = NULL;//地址是0

int& rb = *ptr;

//rb++;//error,程序异常结束,这里相当于空引用

转到反汇编:这里是内存,访问内存时

image.png

???不懂的指令:百度

嵌入式驱动开发比较要求底层像汇编之类的。

image.png

四、const引用

4.1 const引用示范及权限变化

由const修饰的对象不能像普通对象那样直接引用,需要使用const进行引用。const引用的权限变化:有权限平移、权限缩小,但是不能权限放大。const引用可以给常量取别名。临时对象:编译器需要⼀个空间暂存表达式的求值结果,这个空间就是临时创建的⼀个未命名的对象的空间。C++中把这个未命名对象叫做临时对象,并规定临时对象具有常性。表达式被存放在临时变量中,对临时对象引用取别名需要const修饰。

<code>int main()

{

const int a = 10;

int& ra = a; //error,权限放大

const int b = 20;

const int& rb = b; //right,权限平移

int c = 30;

const int& rc = c; //right,权限缩小

++c; //right

++rc; //error,c和rc是同一个地址不同的权限

const int& rd = 40; //right,const引用可以给常量取别名

int& re = (a + b); //error,C++规定临时对象具有常性,这里,权限放大,const修饰即可

const int& rf = (a + b);//right

int rg = (a + b);//拷贝,不存在权限放大

//a+b被存放在临时对象中,对临时对象引用取别名需要const修饰

return 0;

}

4.2 权限放大和赋值的区别

权限放大和缩小在指针和引用中才会有,复制拷贝并没有权限变化。

const int a = 10; //a:只读

int& ra = a;//error,ra可读可写,权限放大

const int b = 20;//b只读

int rb = b; //right,rb赋值拷贝,可读可写

//权限放大和缩小在指针和引用中才会有

4.3临时对象的产生

4.3.1 表达式结果产生临时对象

分析:a+b表达式的结果被存放在临时对象中,rf引用的对象是这个临时对象,但是临时对象具有常性,因此需要const修饰。

#include<iostream>

using namespace std;

int main()

{

int a = 10;

int b = 20;

const int& rf = (a + b);//right,

//a+b表达式的结果被存放在临时对象中,rf引用的对象是这个临时对象

//但是临时对象具有常性,因此需要const修饰

return 0;

}

4.3.2 隐式类型转换成临时对象(const修饰引用临时对象)

分析:rii 也是同理,引用的是临时对象,需要const修饰。

#include<iostream>

using namespace std;

int main()

{

double d = 12.34;

int i = d;//这里是通过了隐式类型转换,中间产生一个int临时对象存储d,i=12

int& ri = d; //error

const int& rii = d; //right,rii引用d,实际是引用的d的临时对象i

return 0;

}

4.3.3 提升、转换等会产生临时对象

4.4 const引用总结

const引用总结:可以引用const修饰的对象、也可以引用普通对象、还可以引用临时对象。临时对象的生命周期就与引用有关系,引用销毁,临时对象才会销毁。它的价值主要体现在函数传参里面。

喜欢的uu记得三连支持一下哦!

在这里插入图片描述



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。