【C++修炼之路 第一章:入门】缺省参数 和 函数重载

一、缺省参数(理解成 默认参数)

1、缺省参数概念

大白话:字面理解,我们使用一个函数时,需要传递形参;当使用这个缺省参数,你不传递参数也行,函数会默认使用制定好的 缺省参数,可以理解成 默认参数

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

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

	return 0;
}



2、缺省参数分类

全缺省参数(全部设置成 缺省参数)
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;
}

半缺省参数(部分设置)
void Func(int a, int b = 10, int c = 20)
{
    cout<<"a = "<< a << endl;
    cout<<"b = "<< b <<endl;
    cout<<"c = "<< c <<endl;
}
int main
{
    Func(5); // a = 5
    return 0;
}



3、注意事项

1、半缺省参数必须从右往左依次来给出,不能间隔着传

比如:变量 a,c 都设置成缺省参数,你只想要传 变量 b 的形参,是不可以的

#include<iostream>
using namespace std;

void Func(int a = 10, int b, int c = 30)
{
	cout << a << endl;
	cout << b << endl;
	cout << c << endl;
}
int main()
{
	Func(  , 20,  );  // 这个是错误的
	return 0;
}


什么叫做 半缺省参数必须从右往左 缺省?

举例:

void Func(int a, int b, int c = 30)
void Func(int a, int b = 20, int c = 30)

设置缺省只能 从右边开始向左设置,因为人为传递形参时,计算机默认 从左往右依次匹配

void Func(int a, int b = 20, int c = 30)
int main{
	Func(10, 15); // 10 一定是给到 a;15 一定给到 b,不会跳跃的给到 c
	return 0;
}


2、 缺省参数不能在函数声明和定义中同时出现

3、缺省值必须是常量或者全局变量

4、C语言不支持(编译器不支持)



二、函数重载(一词多义;同一作用域可以同名,满足重载规则)

引言:

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

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

1、概念

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


C++ 中,默认会自动匹配对应不同的 (参数个数 / 类型 / 类型顺序),实现重载的效果

// 1、参数类型不同:同名但类型不同
int Add(int a, int b)
{
    cout << "int Add(int a, int b)" << endl;
    return a + b;
}

double Add(double a, double b)
{
    cout << "double Add(double a, double b)" << endl;
    return a + b;
}
int main()
{
    int a = 10, b = 20;
    double c = 1.1, d = 1.2;
    Add(a, b); // 对应 int 的 Add
    Add(c, d);  // 对应 double 的 Add
    return 0;
}
///



// 2、参数个数不同
void f()
{
    cout << "f()" << endl;
}
void f(int a)
{
    cout << "f(int a)" << 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()
{
    // 2、参数个数不同
    f();
    f(10);
    
    // 3、参数类型顺序不同
    f(10, 'a');
    f('a', 10);
    return 0;
}

同一作用域,构成重载关系(使用同名函数,自动匹配)

不同作用域,不是重载关系(两个命名空间展开属于不同域,同名函数不能自动匹配,会产生歧义)

  • 不是重载关系的情况下,你使用两个同名的函数
  • 会报错:error C2668 : 对重载函数的调用不明确
  • 注意:你使用高版本的编译器时,编译器可能会帮你优化代码,当你写下面的代码,可能不报错,会正常运行
  • 编译器不知道你想用的是哪一个
// 不同作用域,不是重载关系

namespace bit1
{
	int Add(int a, int b)
	{
		cout << "int Add(int a, int b)" << endl;
		return a + b;
	}
}

namespace bit2
{
	double Add(double a, double b)
	{
		cout << "double Add(double a, double b)" << endl;
		return a + b;
	}
}

using namespace bit1;
using namespace bit2;


int main()
{
	int a = 10, b = 20;
	double c = 1.1, d = 1.2;
    // 调用歧义:不知道你想要哪一个
	Add(a, b); 
	Add(c, d);  
	return 0;
}

2、(难点:面试题)C++支持函数重载的原理–名字修饰(name Mangling)

为什么 C语言不支持重载?

注意:以下的讲解 是建立在 一个项目工程中 函数的定义和声明分开放在不同的文件 的前提下

函数的声明 写在 Head.h 中

函数的定义 写在 Head.cpp 中


(1)先回顾一下 程序的编译链接

先明白两点

1、调用一个函数,需要声明,通过声明找到定义,实现函数的调用执行

2、在汇编底层来看,只有 函数的定义 才会存有 该函数的地址,我们要获取这个函数的地址才能执行这个函数的一系列操作
举例:我们要使用一个数组,是不是要先获取这个数组的首地址,是一个道理


假设我们有这么一个项目包含以下文件:

Stack.h Stack.cpp Main.cpp

Stack.h 和 Test.cpp 存放着 函数的声明

Stack.cpp 存放着 函数的 定义

因此,根据图中显示,在预处理和编译阶段,各种文件生成路径如下

Stack.h 和 Test.cpp --> Test.i --> Test.s --> Test.o :只含有函数声明,不能直接执行函数

Stack.h 和 Stack.cpp --> Stack.i --> Stack.s --> Stack.o :含有函数定义

在最后的链接阶段:Test.o 和 Stack.o 链接在一起,在此阶段,通过 Test.o 里面的函数声明,去 Stack.o 找到对应的 函数定义,找到函数地址(类似一个符号表),找到地址就能执行这个函数了


外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传




(2)重点就在最后的 链接阶段

在最后的链接阶段:需要通过 Test.o 里面的函数声明,去 Stack.o 找到对应的 函数定义,找到函数地址(类似一个符号表)

在 C语言中,是直接通过 函数的名字 去找函数地址的

在 C++ 中,是通过修饰后的函数名字 去找函数地址的

一个函数只有一个名字,只能一个名字对应一个函数,因此C语言没有重载功能

C++ 通过修饰规则,将几个同名的函数名字,修饰成不同的 符号码,在通过符号码,去对应寻找函数地址,实现重载的功能


(3)演示一个例子

再次简单回顾一下前面的知识:

Head.h 和 Test.cpp 合并成 Test.obj :只含有函数声明(C语言中为函数名字,C++ 中为修饰后的函数名字)(.o 文件也叫 .obj 文件)

Head.h 和 Head.cpp 合并成 Head.obj :含有 函数定义

我们要通过 Test.obj 中的函数声明 找到 Head.obj 中的函数定义(对应找到函数地址)


当我们 将 Head.cpp 中的 函数定义全部注释掉(如下图),则最后生成的 Head.obj 文件中就没有 对应的函数地址了

则 Test.obj 中的函数声明 就找不到对应的函数地址,就会报错:LNK2019:无法解析的外部符号

在这里插入图片描述

【LNK2019:无法解析的外部符号】这个无法解析的外部符号,就是 编译器处理 C++代码产生的 ”修饰后的函数名字“

可以发现 修饰后的函数名字 变得不同了

在这里插入图片描述

因而能够一一对应各自的函数,实现 C++的函数重载功能


(4)而如何修饰 相同的函数名呢?

通过不同的 (参数个数 / 类型 / 类型顺序),对应一个符号表(这个就是底层的对应规则,和ASCLII码表一样,有着一一对应关系,暂不讨论过)

由此产生不同的 修饰后 的函数名

(拓展:不同的编译器中,含有不同的 函数名修饰规则)


(5)面试时怎么回答?一般简单回答一下,别人就知道你会了

直接说:在链接阶段,C语言直接用函数名去寻找函数地址,C++用修饰后的函数名去寻找
如果深入的问细节,就回答上面学过的细节过程就好

  • 28
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值