【C++学习】C++入门 | 缺省参数 | 函数重载 | 探究C++为什么能够支持函数重载

这篇具有很好参考价值的文章主要介绍了【C++学习】C++入门 | 缺省参数 | 函数重载 | 探究C++为什么能够支持函数重载。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

写在前面:

上一篇文章我介绍了C++该怎么学,什么是命名空间,以及C++的输入输出,

这里是传送门:http://t.csdn.cn/Oi6V8

这篇文章我们继续来学习C++的基础知识。

目录

写在前面:

1. 缺省参数

2. 函数重载

3. C++是如何支持函数重载的

写在最后:


1. 缺省参数

在学习C语言的时候,如果一个函数存在参数,

比如说这个函数:

void Func(int a) {}

我们在调用的时候就一定要给他传参,

而C++提供的缺省参数,能让我们对函数的传参更加灵活,

举个例子:

#include <iostream>
using namespace std;

void Func(int a = 10) {
	cout << a << endl;
}

int main()
{
	Func();  //没有传参的时候,使用参数的默认值
	Func(20);//传参的时候,使用指定的实参
	return 0;
}

我们给函数设置一个缺省值,这样在我们不给函数传参的时候,

函数的形参会自动使用缺省参数,而如果我们自己给函数传参,

函数形参使用的就是我们指定或者说传给他的值。

输出:

10 
20

那如果有多个函数参数的函数呢?

来看下一个例子: 

#include <iostream>
using namespace std;

void Func(int a = 10, int b = 20, int c = 30) {
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl;
}

int main()
{
	Func();
	return 0;
}

如果是这样的一个函数,

我们传参的时候能不能只传一部分呢?

来看例子:

#include <iostream>
using namespace std;

void Func(int a = 10, int b = 20, int c = 30) {
	cout << "a = " << a << " ";
	cout << "b = " << b << " ";
	cout << "c = " << c << endl;
}

int main()
{
	Func();
	Func(1);
	Func(1, 2);
	Func(1, 2, 3);
	return 0;
}

输出:

a = 10 b = 20 c = 30
a = 1 b = 20 c = 30
a = 1 b = 2 c = 30
a = 1 b = 2 c = 3

我们发现这样是可行的,

那如果我想要跳着传呢?

比如说这样传上面函数的参数:

Func(1, , 2);

这样是不行的,会报错,

实际上,我们只能按顺序来传,从左往右依次传参,其他都是不行的。

你可能会有疑问,为什么要这样设计,跳着传好像也没什么啊?

我也不知道为啥,因为C++祖师爷就是这么规定的,可能祖师爷不喜欢吧。

我们上述的缺省参数的用法其实叫全缺省,

我们还可以用半缺省,也就是一些参数给缺省值,一些参数不给,

举个例子:

#include <iostream>
using namespace std;
 
//半缺省
void Func(int a, int b = 20, int c = 30) {
	cout << "a = " << a << " ";
	cout << "b = " << b << " ";
	cout << "c = " << c << endl;
}

int main()
{
	Func(1);
	Func(1, 2);
	Func(1, 2, 3);
	return 0;
}

可别搞错了哦,半缺省只是一部分参数不使用缺省参数,

而不是真的一半的参数。

这里就有有一个规定,缺省也必须是连续的,

而且缺省必须是从右往左的缺省,不然就会报错:

比如说这样子,编译器就会报错:

void Func(int a = 10, int b, int c = 30) {}

这里要分清楚:

缺省,是要从右往左缺省

传参,是要从左往右传参

那为什么祖师爷要设置这样一个语法呢?

实际上,这个语法在一些场景还是非常有用的,

我们来看这样一个场景,

比如说我们要对一个栈进行初始化:

#include <iostream>
using namespace std;

struct Stack {
	int* a;
	int top;
	int capacity;
};

//初始化一个栈
void StackInit(struct Stack* ptr) {
	ptr->a = (int*)malloc(sizeof(int) * 4);
	if (ptr->a == nullptr) {
		perror("StackInit::malloc::fail");
		return;
	}

	ptr->top = 0;
	ptr->capacity = 4;
}

int main()
{
	struct Stack st;
	StackInit(&st);
	//然后我们之后要对栈插入100个数据

	return 0;
}

如果我们明知道之后就要往栈里插入100个数据,

而我们初识化默认就是初始化大小是4个整形,

那之后插入数据的过程就会频繁扩容导致不必要的损耗,

如果我们多加一个参数:

#include <iostream>
using namespace std;

struct Stack {
	int* a;
	int top;
	int capacity;
};

//初始化一个栈
void StackInit(struct Stack* ptr, int defaultCapacity = 4) {
	ptr->a = (int*)malloc(sizeof(int) * defaultCapacity);
	if (ptr->a == nullptr) {
		perror("StackInit::malloc::fail");
		return;
	}

	ptr->top = 0;
	ptr->capacity = defaultCapacity;
}

int main()
{
	struct Stack st;
	StackInit(&st, 100);
	//然后我们之后要对栈插入100个数据

	return 0;
}

这样如果我们有需要就可以直接指定开辟空间大小,

不需要的时候不传第二个参数,也能自动使用默认的大小初始化。

这个问题就很好的解决了。

这里再补充一嘴,C语言的时候我们其实通常是这样解决这种问题的:

#include <iostream>
using namespace std;

#define DEFAULT_CAPACITY 100

struct Stack {
	int* a;
	int top;
	int capacity;
};

//初始化一个栈
void StackInit(struct Stack* ptr) {
	ptr->a = (int*)malloc(sizeof(int) * DEFAULT_CAPACITY);
	if (ptr->a == nullptr) {
		perror("StackInit::malloc::fail");
		return;
	}

	ptr->top = 0;
	ptr->capacity = DEFAULT_CAPACITY;
}

int main()
{
	struct Stack st;
	StackInit(&st);
	//然后我们之后要对栈插入100个数据

	return 0;
}

通过定义一个宏的形式,

这样如果要修改初识化大小,就只用修改宏定义就行,

但是这样是没有C++这种用法灵活,

如果我们要创建两个栈,一个容量100,一个容量4的时候,他就做不到了:

#include <iostream>
using namespace std;

struct Stack {
	int* a;
	int top;
	int capacity;
};

//初始化一个栈
void StackInit(struct Stack* ptr, int defaultCapacity = 4) {
	ptr->a = (int*)malloc(sizeof(int) * defaultCapacity);
	if (ptr->a == nullptr) {
		perror("StackInit::malloc::fail");
		return;
	}

	ptr->top = 0;
	ptr->capacity = defaultCapacity;
}

int main()
{
	struct Stack st1;
	StackInit(&st1, 100);
	//然后我们之后要对栈插入100个数据

	struct Stack st2;
	StackInit(&st2);

	return 0;
}

在这个场景下,使用C++就非常的舒适。

当然啦,我们也不能说C语言就不好,C语言也是有他自己独特的优势的。

这里还有一个细节要注意,

在使用缺省参数的时候,声明和定义不能都给缺省。

那是给声明还是给定义缺省值呢?

我就直接说了,只能给声明缺省值,

我来解释一下为什么,我们调用函数的时候,其实看到的就是声明,

如果需要传参就传参,如果有缺省值没传参,传的参数就自动变成缺省参数的值,

而定义不关心这些,定义只知道你一定要给我传两个参数,

所以我们只给声明缺省值。

#include <iostream>
using namespace std; 

struct Stack {
	int* a;
	int top;
	int capacity;
};

//声明给缺省
void StackInit(struct Stack* ptr, int defaultCapacity = 4);

int main()
{
	struct Stack st1;
	StackInit(&st1, 100);
	//然后我们之后要对栈插入100个数据

	struct Stack st2;
	StackInit(&st2);

	return 0;
}

//初始化一个栈
void StackInit(struct Stack* ptr, int defaultCapacity) {
	ptr->a = (int*)malloc(sizeof(int) * defaultCapacity);
	if (ptr->a == nullptr) {
		perror("StackInit::malloc::fail");
		return;
	}

	ptr->top = 0;
	ptr->capacity = defaultCapacity;
}

2. 函数重载

什么是函数重载,我们来看一个例子:

#include <iostream>
using namespace std;

void add(int x, int y) {
	cout << "int" << endl;
}

void add(double x, double y) {
	cout << "double" << endl;
}

int main()
{
	add(1, 2);
	add(1.1, 2.2);
	return 0;
}

输出:

int
double

这个就是函数重载,

我们发现这两个函数函数名相同,但是参数类型却不同,

C语言是不允许同名函数的,而C++函数重载可以支持这个语法,

在函数调用的时候,能够根据你传的参数自动匹配类型。

补充:函数重载对函数返回值没有要求,也就是返回值不同是不构成重载的。

这里我直接总结出函数重载的规则,记住就行了:

1. 在同一个作用域

2. 函数名相同

3. 参数的类型不同或者类型的个数或者顺序不同

这个时候就出现了有趣的情况,

来看例子:

#include <iostream>
using namespace std;

void f() {
	cout << "f()" << endl;
}

void f(int a = 0) {
	cout << "f(int a = 0)" << endl;
}

int main()
{

	return 0;
}

你觉得这段代码构成重载吗?

答案是构成的,因为他符合重载的规则,是可以编译通过的,

但是,如果我们无参调用这个函数呢? 编译器就会直接报错:

f()

不能这样子调用,因为这样存在调用歧义。

其实函数重载就这一点点知识,已经讲完了,

但是,有一个问题,为什么C语言不支持重载,而C++能支持重载呢?

C++是怎么支持重载的呢?

其实是在编译链接的过程,函数名修饰规则有所不同。

3. C++是如何支持函数重载的

这里我需要先做的一个小的铺垫内容,

我们在进行函数调用的时候,调用函数的底层是怎么样的?

比如说这一段代码:

#include <stdio.h>

void f(int a) {
	printf("f(int a)\n");
}

void f(int a, double b) {
	printf("f(int a, double b)\n");
}

int main()
{
	f(1);
	f(1, 1.1);
	return 0;
}

来看汇编代码:

【C++学习】C++入门 | 缺省参数 | 函数重载 | 探究C++为什么能够支持函数重载,C++学习,c++,学习,开发语言

我们可以看到,汇编实际上是使用call 指令来调用函数的,

然后在调用 jump 指令跳转到函数的定义:

【C++学习】C++入门 | 缺省参数 | 函数重载 | 探究C++为什么能够支持函数重载,C++学习,c++,学习,开发语言 

这个时候我们就进入函数了:

【C++学习】C++入门 | 缺省参数 | 函数重载 | 探究C++为什么能够支持函数重载,C++学习,c++,学习,开发语言 那么了解函数是怎么调用之后,我们再继续探究函数重载, 

这里我采用gcc环境来进行演示,

先来看C语言,还是这段代码:

#include <stdio.h>

void f(int a) {
	printf("f(int a)\n");
}

void f(int a, double b) {
	printf("f(int a, double b)\n");
}

int main()
{
	f(1);
	f(1, 1.1);
	return 0;
}

这样子肯定是编译不通过的,

【C++学习】C++入门 | 缺省参数 | 函数重载 | 探究C++为什么能够支持函数重载,C++学习,c++,学习,开发语言

看到这个报错,conflicing types,其实就是函数名冲突了,

我们修改一下代码,让他能够编译通过:
 

#include <stdio.h>

void f(int a, double b) {
	printf("f(int a, double b)\n");
}

int main()
{
	f(1, 1.1);
	return 0;
}

来看看他的汇编代码是怎么样的:

【C++学习】C++入门 | 缺省参数 | 函数重载 | 探究C++为什么能够支持函数重载,C++学习,c++,学习,开发语言

 我们通过汇编可以看到,汇编代码中 call 的这个函数的函数名是 f 

跟我们设置的函数名是相同的,

要是我们定义了两个函数名相同的函数,那 call < f > ,究竟call 的是谁?

那就会出现函数命名的冲突问题,

但是这些只是我们现在的推测,接下来我们看看C++的汇编是怎么操作的:

还是这段代码:

#include <stdio.h>

void f(int a) {
	printf("f(int a)\n");
}

void f(int a, double b) {
	printf("f(int a, double b)\n");
}

int main()
{
	f(1);
	f(1, 1.1);
	return 0;
}

但是我们换成了C++的环境(C++兼容C语言)

【C++学习】C++入门 | 缺省参数 | 函数重载 | 探究C++为什么能够支持函数重载,C++学习,c++,学习,开发语言

看看我们发现了什么?

两个同样的函数,到了C++环境下编译出来的汇编代码,他的函数名怎么这么奇怪?

上面是带有两个函数参数的函数 f (int a, double b) 

我们再来看看那个带着一个函数参数的同名函数 f (int a):

【C++学习】C++入门 | 缺省参数 | 函数重载 | 探究C++为什么能够支持函数重载,C++学习,c++,学习,开发语言

发现了吗?

他们在C++代码中函数名是相同的,

但是到了汇编代码这里,函数名却不一样了,使用 call 指令调用的函数就不一样了,

这样就没有所谓的函数名冲突的问题了,

现在你应该大致理解为什么C语言不支持同名函数了,

C语言下编译出来的汇编代码的函数名是跟C语言代码写的函数名是相同的,

就自然不支持同名函数,而C++编译出来的汇编代码展现的函数名明显不相同。

我们再仔细看看:

 【C++学习】C++入门 | 缺省参数 | 函数重载 | 探究C++为什么能够支持函数重载,C++学习,c++,学习,开发语言

 可以看到他的命名规则还是有一点讲究的。

不过他的命名规则的细节我就不深究了,最重要的是理解函数重载的底层是怎么样的。

祖师爷创造这些语法还是有迹可循的,

同时我们也能感受到,真正设计一个语言还是非常困难的,

需要对各方面的知识有着深入的理解。

这里还是补充一嘴:学习C++的时候,多学底层还是非常重要的,

我们要做到:知其然,知其所以然,这样才能体现我们学习的优势,算是我的一些感想吧。

补充:

这个时候我们又能理解一个点,

函数重载为什么不能支持函数返回值不同呢?一定要返回值相同才能重载。

我们刚刚分析的汇编代码中的函数名修饰规则,

实际上汇编在调用函数的时候,就是通过call 指令查找函数的过程,

来看这段代码:

#include <stdio.h>

void func();
int func();

int main()
{
	func();
	return 0;
}

返回值在调用的时候不会体现,

也就是说我们在调用 func() 函数的时候,我们不知道调用的是哪一个,

如果参数不同,编译器至少知道这是两个不同的函数,

到 call 的时候才可能出现查找函数出现歧义,

而调用 func() 这个函数,编译器就不知道究竟想调用哪个函数,

所以在编译阶段就会直接报错,

所以就算把返回值加入函数名修饰规则,编译也走不到那一步。

来看一眼编译器是怎么说的:

【C++学习】C++入门 | 缺省参数 | 函数重载 | 探究C++为什么能够支持函数重载,C++学习,c++,学习,开发语言

 直接给你飘红了,还贴心的告诉你无法支持按返回类型区分的函数重载。

这里补充一句:

为什么我不在Windows下或者说VS下探究这个函数名修饰规则,

而是跑到了gcc/g++环境下去看呢?

因为VS他的函数名修饰规则比较复杂,没有g++那么清晰,

如果你感兴趣的话可以上网搜一下VS下的函数名修饰规则,这里就不展示了。

写在最后:

以上就是本篇文章的内容了,感谢你的阅读。

如果感到有所收获的话可以给博主点一个哦。

如果文章内容有遗漏或者错误的地方欢迎私信博主或者在评论区指出~文章来源地址https://www.toymoban.com/news/detail-547485.html

到了这里,关于【C++学习】C++入门 | 缺省参数 | 函数重载 | 探究C++为什么能够支持函数重载的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++之入门之缺省参数&&函数重载&&引用

    重新拿起曾经尚未填的C++的坑,从现在我会继续更新C++的相关内容,希望各位能够支持 缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实 参则采用该形参的缺省值,否则使用指定的实参 (1)全缺省参数 (2)半缺省参数 (1)半

    2023年04月15日
    浏览(41)
  • C++入门 上(命名空间 缺省参数 函数重载)

    在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace的出现就是针对这种问题的。 定义命名空间

    2024年02月19日
    浏览(46)
  • 【C++初阶】一、入门知识讲解(C++关键字、命名空间、C++输入&输出、缺省参数、函数重载)

    ========================================================================= 相关代码gitee自取 : C语言学习日记: 加油努力 (gitee.com)  ========================================================================= 接上期 : 【数据结构初阶】十一、归并排序(比较排序)的讲解和实现 (递归版本 + 非递归版本 -- C语言实

    2024年02月05日
    浏览(51)
  • 【C++】缺省参数与函数重载

    👀樊梓慕: 个人主页  🎥 个人专栏: 《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C++》 🌝 每一个不曾起舞的日子,都是对生命的辜负 本篇文章博主将带你学习 缺省参数 与 函数重载 ,一些需要注意的点博主都会特别标注以便大家学习。 欢

    2024年02月08日
    浏览(34)
  • 【初识C++】(缺省参数和函数重载)

    缺省参数是在函数的声明中给定参数一个指定的值。 如果传参没有给定参数,那就按照声明中默认的缺省值,如果给定了参数,那就按照给定的参数值。 比如: 对于Func函数如果没有给定参数,那就按照默认的缺省值来赋值。如果给定了参数,那就按照给定的参数进行赋值。

    2024年02月01日
    浏览(36)
  • 【C++初阶】缺省参数与函数重载

        C++祖师爷在用C写代码的时候,就觉得有些地方用着很不方便,于是就在C++设计了缺省参数,在实现一些功能时,用这个就方便了许多。 缺省参数是声明或定义函数时为 函数的参数指定一个缺省值 。在调用该函数时:             a.如果没有指定实参则采用该形参的缺省

    2023年04月22日
    浏览(51)
  • 【C++初阶(二)】缺省参数&函数重载

    目录 前言 1. 缺省参数  1.1 什么是缺省参数  1.2 缺省参数的分类      1.2.1 全缺省参数  1.2.2 半缺省参数 2. 函数重载  2.1 什么是函数重载  2.2 缺省参数与函数重载  2.3 函数重载的使用  3. C++支持函数重载的原因  总结         在学习C语言时我们就会发现,C语言中存在的

    2024年02月07日
    浏览(35)
  • C++中命名空间、缺省参数、函数重载

    目录 1.命名空间 2.缺省参数 3.函数重载 在C++中定义命名空间我们需要用到namespace,后面跟上命名空间的名字,结构框架有点类似结构体(如图所示) 上面的代码我一一进行讲解: 1.我们先来说第三行和main函数中的代码意思,第三行代码的意思是展开std库的空间,std是

    2024年01月23日
    浏览(46)
  • 【C++】命名空间、缺省参数和函数重载

    在一个大型的项目中,不同成员写的程序中会不可避免的带来 命名冲突 的问题,这种情况下命名空间可以很好的解决这种命名冲突大的问题,它可以用来避免不同的库或模块中的名称(name)发生冲突。 名称可以是变量、函数、类、结构、枚举等等。此外,命名空间可以让我

    2024年02月17日
    浏览(44)
  • 【开懂C++】命名空间 函数重载 缺省参数

    在编写C语言代码时,当工程较大时,很容易产生变量命名冲突的情况——一般有两种冲突的情况 1.变量名与库中的函数名、冲突 。 2.工程模块化搭建时不同文件的命名冲突 。 而C++为了优化这一缺陷,提出了命名空间的概念。 命名空间使用的是 namespace ,命名空

    2023年04月18日
    浏览(90)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包