首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >C++入门必学:缺省参数与函数重载

C++入门必学:缺省参数与函数重载

作者头像
用户11831438
发布2025-12-30 13:45:11
发布2025-12-30 13:45:11
1940
举报
补充:

在io需求比较高的地方,如部分大量输入的竞赛中,加上以下代码可以提高C++IO效率

如果不想加上这三行代码,可以直接使用scanf和printf

正文开始:

一、缺省参数

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

重点:如果在调用函数时,给函数传参数,就用传的参数作为实参;如果不传参数,缺省值作为实参

1.1 全缺省

全缺省就是全部形参给缺省值

1.2 半缺省(部分缺省)

半缺省就是多个参数,部分参数给缺省值。

注意:C++规定半缺省参数必须从右往左依次连续缺省,不能间隔跳跃给缺省值,哪个参数想缺省,就往后放。

1.3 函数声明给缺省值

函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须函数声明给缺省

值。

1.4 缺省参数的运用

在前面数据结构的学习中,我们学习了顺序表,通过缺省参数的学习,我们可以对顺序表进行一点升级: 当我们已知要插入数据的个数时,我们可以使用缺省参数在初始化时可以直接申请已知数据个数的空间,这样就可以避免后期开空间

二、函数重载

C++支持在同一作用域中出现同名函数,但是要求这些同名函数的形参不同,可以是参数个数不同、参数类型不同、参数类型顺序不同。这样C++函数调用就表现出了多态行为,使用更灵活。但是C语言是不支持同⼀作用域中出现同名函数的

2.1 参数个数不同
2.2 参数类型不同
2.3 参数类型顺序不同

那函数的返回值类型不同的函数,这两个函数会发生函数重载吗?

答案是:返回值类型不同的函数不构成函数重载,调用函数时,编译器不知道调用哪一个,不支持返回值不同的函数重载。

接下来,我们来看一段代码:

上面这段代码构成函数重载吗?

上面这段代码是构成函数重载的,因为这两个函数所含的参数不同,一个有参数,一个没有参数,所以构成函数重载,但是当我们传参时会有问题

调用有参函数,并且给有参函数传参,是没有问题的

但是,调用两个函数,并都不传参,编译器不知道调用哪一个

三、引用--&

相信有很多小伙伴看到这个操作符都会感到有那么一点的亲切感,当我们在学习C语言的指针相关内容的时候,我们就经常使用到这个操作符 ,那在C++中为什么还要使用这个操作符为引用操作符呢?这是因为C++的先祖为了避免引入太多的操作符,会复用一些C语言中一些符号,比如前面的<<和>>,这里的取地址和引用使用的也是同一个符号,这就需要我们在使用的时候,要区分他们的使用方法,接下来我们来学习一下:

3.1 引用的概念

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

比如:水浒传中李逵,宋江叫"铁牛",江湖上⼈称"黑旋风";林冲,外号豹子头;

3.2 引用的定义

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

引用就相当于是取别名: 我们给已存的变量取个别名,编译器不会给别名开辟新的空间,变量和别名共同使用同一块空间。我们还可以给别名取别名,但是还是原来的那一块空间,改变别名就是改变变量。 注意:在语法层上编译器不会给别名开辟新的空间

我们可以这样理解:小时候,别人给你取一个外号叫“小黑”,然后又取了个外号叫“黑子”,你看,外号虽然多了,但是你这个人没有多一个。

代码语言:javascript
复制
#include<iostream>
using namespace std;
int main()
{
	int a = 0;
	// 引⽤:b和c是a的别名
	int& b = a;
	int& c = a;
	// 也可以给别名b取别名,d相当于还是a的别名
	int& d = b;
	++d;
	// 这⾥取地址我们看到是⼀样的,编译器不会为别名开辟空间
	cout << &a << endl;
	cout << &b << endl;
	cout << &c << endl;
	cout << &d << endl;
	return 0;
}
3.3 引用的特性
  • 引用在定义是必须初始化(在定义时就要确定是谁的)
  • 一个变量可以有多个引用(一个变量可以有多个别名)
  • 引用一旦引用一个实体,再不能引用其他实体

写到这里,会有很多小伙伴感觉到,引用,就这~~,一点难度都没有嘛!引用的难度不是在他的语法上,而是在他的使用上面,接下来我们来看一下,引用有哪些使用:

四、引用的使用

抛出一个问题?回顾一下C语言的知识,有没有小伙伴知道学习引用是为了解决什么问题?ok,答案是:指针。学习引用是准备在大部分场景下取替代指针,但是部分场景还是指针。

4.1 引用作形参

1、我们以前学习过交换函数:

代码语言:javascript
复制
#include<iostream>
using namespace std;
void swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}
int main()
{
	int a = 10;
	int b = 20;
	swap(&a, &b);
	return 0;
}

通过上面的学习,我们知道,引用其实是给参数取别名,改变别名就是改变参数本身,那我们是不是就可以用引用给实参取个别名,然后作为形参。代码如下:

代码语言:javascript
复制
#include<iostream>
using namespace std;
void swap(int& x, int& y)
{
	int tmp = x;
	x = y;
	y = tmp;
}
int main()
{
	int a = 10;
	int b = 20;
	swap(a, b);
	cout << a << " " << b << endl;
	return 0;
}

除此之外, 这两种写法也可以同时存在:

代码语言:javascript
复制
#include<iostream>
using namespace std;
void swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}
void swap(int& x, int& y)
{
	int tmp = x;
	x = y;
	y = tmp;
}
int main()
{
	int a = 10;
	int b = 20;
	swap(&a, &b);
	cout << a << " " << b << '\n';
	a = 10, b = 20;
	swap(a, b);
	cout << a << " " << b << endl;
	return 0;
}

2、指针的引用

最初的代码:

代码语言:javascript
复制
#include<iostream>
using namespace std;
void swap(int** x, int** y)
{
	int* tmp = *x;
	*x = *y;
	*y = tmp;
}
int main()
{
	int a = 10;
	int b = 20;
	int* pa = &a;
	int* pb = &b;
	swap(&pa, &pb);
	return 0;
}

我们看到最初我们在交换两个指针的时候,形参是创建了二级指针,回顾我们的学习历程,不难发现,有时候一级指针就已经很让我们头疼了,这时候又来了一个二级指针,岂不是让我们更头疼,这时候我们就应该要想到上面学习的引用。

代码语言:javascript
复制
#include<iostream>
using namespace std;
void swap(int*& x, int*& y)
{
	int* tmp = x;
	x = y;
	y = tmp;
}
int main()
{
	int a = 10;
	int b = 20;
	int* pa = &a;
	int* pb = &b;
	swap(pa, pb);
	return 0;
}

提醒:同一作用域并不能使用相同的名字,会出现命名冲突,但是不同作用域可以使用。

我们再来看一个指针的引用

当我们在数据结构中学习顺序表的时候,遇到的第一个问题就是下面红方框内的为什么要加上&?经过学习之后,我们知道形参的改变要影响实参,我们就要传地址

学习了引用之后,我们就可以不用传地址了:

说到这里,博主想起了一个数据结构书里面的一段代码,让我们一起看一下这段代码:

代码语言:javascript
复制
typedef struct SlistNode
{
	struct SlistNode* next;
	int val;
}SlNode,*PSLTNode;

其实上面这个结构体是这个意思:

这样我们就可以很好理解下面图片里面的代码了:

补充:

在上面中,我们说过引用是替代绝大多数的指针场景,那么那一小部分无法替代的场景是什么呢? 那就是链表,树等需要节点定义位置的只能使用指针,无法用引用替代 原因: C++中的引用无法改变指向

4.2 传引用返回

在前面的学习中,我们学习过传值返回,传指针返回,现在我们来看看传引用返回是怎么个事。

1、传值返回

2、传引用返回

上图中写到tmp(假设的)可能是10,也可能是随机值,这要看func这个函数桟帧是否完全销毁(后面会说到,这里先放着)。

这里补充一个东西: 越界其实是不一定报错的,越界是抽查的。

接下来,我们再来看一段代码:

有没有同学会感到很奇怪,我明明调用的是func2()这个函数,并且没有用x来接收,为什么x的值是456呢?

其实说实话,博主自己学到这里的时候也是很懵逼的,但是当我们学习了后面的一个知识点在回过头来看这个就很简单了。

再和大家说一个东西,其实上面这些传引用返回的写法都是错误的~~~~,也许会有很多小伙伴这时候已经在心里骂娘了,但是我想说的是,多学点错误可以避免我们自己犯这些错误。


真正的使用场景如下:

1、返回值不是局部对象

重点:局部对象作为返回对象,不能用引用返回,只有出了这个作用域,该返回对象还在,才可以用引用返回。

注意:如果使用局部对象作为返回对象,并且还使用引用返回,存在风险

2、修改引用对象

这样我们就不需要Modify这个方法了,直接一个SLat就解决了。

这两行代码是等价的——

这里一来我们既可以读,也可以修改顺序表对应位置的值,就完全不需要Modify了。

这是修改引用对象的例子。

补充(重要):

我们来看一段代码:

代码语言:javascript
复制
int main()
{
	int i = 0;
	// 语法引用不开空间,指针要开空间
	int& r1 = i;
	int* p = &i;

	r1++;
	(*p)++;

	return 0;
}

我们将上面的代码转换成汇编代码,我们可以清晰的发现两种颜色的方框内的指令一模一样,根据上面我们可以得出一个结论:

引用在语法层上不开辟空间,在底层上还是指针

ok,知道了上面的知识,我们可以来解决上面遗留下来的问题:

这是因为x是ret的别名,在底层上x存的是func1()函数中的ret的地址,func1()的函数桟帧销毁后,内存数据不会立即清零,空间并不是没有了,而是空间返还给操作系统,但是x在底层上存的是地址,所以可以通过x找到ret,func2()函数跟func1()函数所使用的空间正好相等,操作系统将func1()返回的空间又给了func2() ,第二次 cout << xx 仍然指向原来的位置,但该位置现在存储的是 y 的值(456),所以可能输出 456。

所以我们不能对返回值是局部对象的进行引用返回,这是错误的。

总结:引用在实践中主要是于引用传参和引用做返回值中减少拷贝提高效率和改变引用对象同时改变被引用对象。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-09-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 补充:
  • 一、缺省参数
    • 1.1 全缺省
    • 1.2 半缺省(部分缺省)
    • 1.3 函数声明给缺省值
    • 1.4 缺省参数的运用
  • 二、函数重载
    • 2.1 参数个数不同
    • 2.2 参数类型不同
    • 2.3 参数类型顺序不同
  • 三、引用--&
    • 3.1 引用的概念
    • 3.2 引用的定义
    • 3.3 引用的特性
  • 四、引用的使用
    • 4.1 引用作形参
    • 4.2 传引用返回
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档