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

目录

1.函数缺省参数

1.1 缺省参数概念

1.2 缺省参数分类

2.函数重载

2.1 函数重载概念

2.2 C++支持函数重载的原理


1.函数缺省参数

1.1 缺省参数概念

缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实
参则采用该形参的缺省值,否则使用指定的实参,有点备胎的意思。

void Func(int a = 0)
{
    cout << a << endl;
}
int main()
{
    Func(); // 没有传参时,使用参数的默认值 ,打印0
    Func(10); // 传参时,使用指定的实参 , 打印10
    return 0;
}

1.2 缺省参数分类

1.全缺省参数

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

int main()
{
	Func();
	
	//显示传参,必须从左往右连续显示传参,
	Func(1);
	Func(1, 2);
	Func(1, 2, 3); 
	return 0;
}

2.半缺省参数

半缺省,缺省部分,必须从右往左,给缺省值
void Func(int a, int b = 20, int c = 30)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl << endl;
} 

int main()
{
	//没有缺省的必须传
	Func(1);
	Func(1, 2);
	Func(1, 2, 3);
}

注意:

  1. 半缺省参数必须从右往左依次来给出,不能间隔着给
  2. 缺省参数不能在函数声明和定义中同时出现,C++规定声明给,定义不给。错误示例:
    //a.h
    void Func(int a = 10);
    // a.cpp
    void Func(int a = 20)
    {}
    // 注意:如果声明与定义位置同时出现,恰巧两个位置提供的值不同,
    //那编译器就无法确定到底该用那个缺省值
  3. 缺省值必须是常量或者全局变量
  4. C语言不支持(编译器不支持)

函数缺省参数的应用:

在使用某种数据结构例如栈在申请空间时,如果我们知道需要使用多少空间时,可以直接把空间大小传入,不用多次realloc调整空间,示例:

------------------------Stack.h------------
#include<iostream.h>
#include<stdlib.h>
namespace s
{
	typedef struct Stack
	{
		int* a;
		int top;
		int capacity;
	}Stack;

	//不允许声明和定义同时给缺省参数     C++规定 声明给 定义不给
	void StackInit(Stack* ps, int n = 4);//缺省值为4

	void StackPush(Stack* ps, int x);
}
-----------------------Stack.cpp-------------
#include"Stack.h"

void s::StackInit(Stack* ps, int n)
{
	ps->a = (int*)malloc(sizeof(int) * n);
	if (ps->a == NULL)
	{
		perror("malloc fail");
		return;
	}
	ps->top = 0;
	ps->capacity = n;
}
void s::StackPush(Stack* ps, int x)
{
	if (ps->top == ps->capacity)
	{
		int* ptr = (int*)realloc(ps->a, sizeof(int) * ps->capacity * 2);
		if (ptr == NULL)
		{
			perror("realloc fail");
			return;
		}
		ps->capacity *= 2;
	}
	ps->a[ps->top] = x;
	ps->top++;
}
----------------------test.cpp--------------
#include"Stack.h"
using namespace s;
int main()
{
	//知道要插入多少个
	Stack st1;
	StackInit(&st1, 10);
	for (int i = 0; i < 10; i++)
	{
		StackPush(&st1,i);
	}
	Stack st2;
	StackInit(&st2, 100);
	for (int i = 0; i < 100; i++)
	{
		StackPush(&st2,i);
	}
	//不知道要插入多少个时
	Stack st3;
	StackInit(&st3);

	return 0;
}

在头文件中定义函数时:

在头文件中定义函数,当这个头文件被多个.cpp文件包含时,会发生链接错误,重复定义,可以在函数前加static改变链接属性,由外部链接改为内部链接,只能在当前文件使用。

2.函数重载

自然语言中,一个词可以有多重含义,人们可以通过上下文来判断该词真实的含义,即该词被重载了。

比如:以前有一个笑话,国有两个体育项目大家根本不用看,也不用担心。一个是乒乓球,一个是男足。前者是"谁也赢不了! ",后者是"谁也赢不了!”

2.1 函数重载概念

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

#include<iostream>
using namespace std;

//1.参数类型不同
int Add(int left, int right)
{
	cout << "int Add(int left, int right)" << endl;
	return left + right;
}

double Add(double left, double right)
{
	cout << "double Add(double left, double right)" << endl;
	return left + right;
}

//2.参数个数不同
void func()
{
	cout << "func()" << endl;
}

void func(int x)
{
	cout << "func = " << x << endl;
}

// 3、参数类型顺序不同
void f(int a, char b)
{
    cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
    cout << "f(char b, int a)" << endl;
}

int main()
{
    //函数重载,可以自动匹配 , 如何实现自动匹配,函数名修饰规则,下面会讲
    Add(10, 20);
    Add(10.1, 20.2);
    //cout自动匹配类型本质也是函数重载
    f();
    f(10);
    f(10, 'a');
    f('a', 10);
    return 0;
}

注意:

  1. 顺序不同不是指的形参的名字顺序不同,而是参数类型顺序不同
    //func1构不成函数重载,会重定义
    void func1(int x, double y)
    {
    	//...
    }
    void func1(int y, double x)
    	//...
    }
    //func2构成函数重载
    void func2(int x, double y)
    {
    	//...
    }
    void func2(double x, int y)
    {
    	//...
    }
  2. 只有返回值不同构不成函数重载

不同作用域构不成函数重载,把命名空间展开也可以构成

//不同作用域构不成函数重载,但是展开作用域后构成
namespace s1
{
	void func(int x)
	{
		cout << "func = " << x << endl;
	}
}
namespace s2
{
	void func(double x)
	{
		cout << "func = " << x << endl;
	}
}
using namespace s1;
using namespace s2;

命名空间名称相同合并,也构成函数重载

//命名空间相同合并,构成函数重载
namespace s1
{
	void func(int x)
	{
		cout << "func = " << x << endl;
	}
}
namespace s1
{
	void func(double x)
	{
		cout << "func = " << x << endl;
	}
}

函数重载遇上函数缺省参数

//构成重载 参数个数不同
//调用时发生问题,调用存在歧义
void func(int x)
{
	cout << "void func(int x)" << endl;
}
void func(int x,int b = 1)
{
	cout << "void func(int x,int b = 1) " << endl;
}

int main()
{
	func(1, 2);//可以调用
	//调用时发生问题,调用存在歧义,与重载没有关系
	//func(1);
	return 0;
}

调用时发生问题,调用存在歧义,但会发生重载。

2.2 C++支持函数重载的原理

为什么C++支持函数重载,而C语言不支持函数重载呢?

在C/C++中,一个程序要运行起来,需要经历翻译:编译、链接。而编译又有三个阶段:预处理,编译,汇编

程序翻译阶段:

 程序编译阶段:

 1. 实际项目通常是由多个头文件和多个源文件构成,编译是分隔开的。【当前a.cpp中调用了b.cpp中定义的Add函数时】,编译后链接前,a.o的目标文件中没有Add的函数地址,因为Add是在b.cpp中定义的,所以Add的地址在b.o中。那么怎么办呢?

2. 所以链接阶段就是专门处理这种问题,链接器看到a.o调用Add,但是没有Add的地址,就会到b.o的符号表中找Add的地址,然后链接到一起,即合并符号表,符号表上有每个函数名以及对应的地址

3. 那么链接时,面对Add函数,链接接器会使用哪个名字去找呢?在C++中,编译器都有自己的函数名修饰规则,让重载的函数区别开来,是用修饰后的名字放到符号表而C语言是直接使用函数名做符号表的名字,没有进行修饰,所以重载会冲突

4. 由于Windows下vs的修饰规则过于复杂,而Linux下g++的修饰规则简单易懂,下面我们使用了g++演示这个修饰后的名字。

5. 通过下面我们可以看出gcc的函数修饰后名字不变。而g++的函数修饰后变成【_Z+函数长度+函数名+类型首字母

示例:

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

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

调用函数转到汇编:

可以发现是使用了 call 指令跳转到 func 函数第一行的地址。函数的地址本质可以认为是函数第一句指令的地址,只有声明没有定义会找不到,生不成地址。下面是只有声明没有定义的报错:

可以发现第一个函数名已经被修饰为:(?func@@YAXHN@Z),int被修饰成H,double被修饰成N。这里重载参数顺序不同,所以被修饰后的两个函数名也不同

我们来看一Linux下g++的修饰规则:

 一个是<_Z4funcid>一个是<_Z4funcdi> ,函数名修饰规则是:【_Z + 函数名字符个数 + 函数名 + 类型首字母】int 是 i ,double 是 d,指针类型前面加p,int * 是 pi,可以发现:

函数名修饰规则本质是把参数类型带进名字了,类型不同,个数不同,顺序不同,修饰的名称就不同。所以返回值不同,不能构成函数重载,因为函数名修饰规则里没有返回值。

结论:在linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参数类型信息添加到修改后的名字中。 

这里就有一个问题了,如果把返回值带入函数名修饰规则,能不能构成重载?

答案是不能,你会发现在调用时,就不知道调用谁,示例:

void func(int x, double y)
{
	//...
}
int func(int x, double y)
{
	//...
    return 1;
}
//不会构成函数重载
int main()
{
	func(1, 1.1);//不知道到底调用哪一个
    func(1, 1.1);
	return 0;
}

结论: 

  1.  通过这里就理解了C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。
  2. 如果两个函数函数名和参数是一样的,返回值不同是不构成重载的,因为调用时编译器没办法区分

 本篇结束。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/82095.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用Spring Initializr方式构建Spring Boot项目

除了可以使用Maven方式构建Spring Boot项目外&#xff0c;还可以通过Spring Initializr方式快速构建Spring Boot项目。从本质上说&#xff0c;Spring lnitializr是一个Web应用&#xff0c;它提供了一个基本的项目结构&#xff0c;能够帮助我们快速构建一个基础的Spring Boot项目…

JVM性能分析-jstat工具观察gc频率

jstat jstat是java自带的工具&#xff0c;在bin目录下 用法 语法&#xff1a;jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]] [kqkyyj-2 bin]$ jstat -help Usage: jstat -help|-optionsjstat -<option> [-t] [-h&l…

岩土工程安全监测隧道中使用振弦采集仪注意要点?

岩土工程安全监测隧道中使用振弦采集仪注意要点&#xff1f; 岩土工程的安全监测是非常重要的&#xff0c;它可以帮助工程师及时发现可能存在的问题&#xff0c;并及时解决&#xff0c;保障施工进度以及施工质量&#xff0c;保障工程的安全运行。其中&#xff0c;振弦采集仪是…

【Ubuntu】简洁高效企业级日志平台后起之秀Graylog

简介 Graylog 是一个用于集中式日志管理的开源平台。在现代数据驱动的环境中&#xff0c;我们需要处理来自各种设备、应用程序和操作系统的大量数据。Graylog提供了一种方法来聚合、组织和理解所有这些数据。它的核心功能包括流式标记、实时搜索、仪表板可视化、告警触发、内容…

Windows安装 Elasticsearch 教程

下载地址 Past Releases of Elastic Stack Software | Elastic 解压 解压完的样子 进入BIN目录 D:\Develop\elasticsearch\elasticsearch-7.12.0\bin 按住shift 鼠标右键 打开 powershell 窗口 查看ES版本 .\elasticsearch.bat --version 出现问题了 警告&#xff1a;不赞成…

jenkins pipeline方式一键部署github项目

上篇&#xff1a;jenkins一键部署github项目 该篇使用jenkins pipeline-script一键部署&#xff0c;且介绍pipeline-scm jenkins环境配置 前言&#xff1a;按照上篇创建pipeline任务&#xff0c;结果报mvn&#xff0c;jdk环境不存在&#xff0c;就很疑惑&#xff0c;然后配置全…

【Apollo】Apollo 8.0系统下载指南

作者简介&#xff1a; 辭七七&#xff0c;目前大一&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; 七七的闲谈 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f…

stm32_ADC电源、通道、工作模式

0、ADC功能框图 1、ADC的电源 1.1、工作电源 VSSAVSS&#xff0c;VDDAVDD&#xff0c;简单来说&#xff0c;通常stm32是3.3V&#xff0c;ADC的工作电源也是3.3V&#xff1b; 1.2、参考电压 VREF和VREF-并不一定引出&#xff0c;取决于封装&#xff0c;如果没有引出则VREF连接到…

小程序前台Boot后台校园卡资金管理系统java web学校进销存食堂挂失jsp源代码

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 小程序前台Boot后台校园卡资金管理系统 系统有2权限&…

回归预测 | MATLAB实现GWO-SVM灰狼优化算法优化支持向量机多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现GWO-SVM灰狼优化算法优化支持向量机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现GWO-SVM灰狼优化算法优化支持向量机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09;效果一览基…

Linux 进程间通信——消息队列

一、消息队列的原理 消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。每个数据块都被认为含有一个类型&#xff0c;接收进程可以独立接收含有不同类型值得数据库。 消息实际上是一个数据块&#xff0c;这个数据块是一个结构体&#xff0c;结构体由自己命名。消…

奇舞周刊第503期:图解串一串 webpack 的历史和核心功能

记得点击文章末尾的“ 阅读原文 ”查看哟~ 下面先一起看下本期周刊 摘要 吧~ 奇舞推荐 ■ ■ ■ 图解串一串 webpack 的历史和核心功能 提到打包工具&#xff0c;可能你会首先想到 webpack。那没有 webpack 之前&#xff0c;都是怎么打包的呢&#xff1f;webpack 都有哪些功能&…

网络编程套接字(1)

文章目录 网络编程套接字(1)1. 预备知识1.1 源IP与目的IP1.2 认识端口号1.3 理解 "端口号" 和 "进程ID"1.4 源端口号和目的端口号1.5 认识TCP协议和UDP协议(1) TCP(2) UDP 1.6 网络字节序 2. socket编程接口2.1 socket 常见API2.2 sockaddr结构 网络编程套…

matplotlib绘制位置-时序甘特图

文章目录 1 前言2 知识点2.1 matplotlib.pyplot.barh2.2 matplotlib.legend的handles参数 3 代码实现4 绘制效果5 总结参考 1 前言 这篇文章的目的是&#xff0c;总结记录一次使用matplotlib绘制时序甘特图的经历。之所以要绘制这个时序甘特图&#xff0c;是因为22年数模研赛C…

【⑫MySQL | 约束(二)】外键 | 默认值 | 检查约束 — 综合案例

前言 ✨欢迎来到小K的MySQL专栏&#xff0c;本节将为大家带来MySQL外键 | 默认值 | 检查约束 以及综合案例的分享✨ 目录 前言6. 外键约束(FOREIGN KEY,FK)7. 默认值约束和检查约束8. 综合实战总结 6. 外键约束(FOREIGN KEY,FK) 前面介绍的完整性约束都是在单表中进行设置&…

JavaScript 快速入门手册

本篇文章学习&#xff1a; 菜鸟教程、尚硅谷。 JavaScript 快速入门手册 &#x1f4af; 前言&#xff1a; 本人目前算是一个Java程序员&#xff0c;但是目前环境… ε(ο&#xff40;*))) 一言难尽啊&#xff0c;blog也好久好久没有更新了&#xff0c;一部分工作原因吧(外包真…

重新认识小米

被镁光灯聚焦的企业&#xff0c;总是会被贴上各种标签。 8月14日&#xff0c;小米科技创始人雷军以“成长”为主题的年度演讲&#xff0c;刷遍社交网络。提到小米&#xff0c;你首先想到什么&#xff1f;手机发烧友、极致性价比&#xff0c;还是最年轻的500强&#xff1f; 这…

八大排序算法 - Java实现

冒泡排序 排序原理&#xff1a; 比较相邻的元素。如果前一个元素比后一个元素大&#xff0c;就交换这两个元素的位置。对每一对相邻元素做同样的工作&#xff0c;从开始第一对元素到结尾的最后一对元素。最终最后位置的元素就是最大值 代码实现&#xff1a; import java.uti…

uniapp从入门到精通(全网保姆式教程)~ 别再说你不会开发小程序了

目录 一、介绍 二、环境搭建&#xff08;hello world&#xff09; 2.1 下载HBuilderX 2.2 下载微信开发者工具 2.3 创建uniapp项目 2.4 在浏览器运行 2.5 在微信开发者工具运行 2.6 在手机上运行 三、项目基本目录结构 四、开发规范概述 五、全局配置文件&#xff0…

大数据面试题:Spark的任务执行流程

面试题来源&#xff1a; 《大数据面试题 V4.0》 大数据面试题V3.0&#xff0c;523道题&#xff0c;679页&#xff0c;46w字 可回答&#xff1a;1&#xff09;Spark的工作流程&#xff1f;2&#xff09;Spark的调度流程&#xff1b;3&#xff09;Spark的任务调度原理&#xf…