一、缺省参数(理解成 默认参数)
1、缺省参数概念
大白话:字面理解,我们使用一个函数时,需要传递形参;当使用这个缺省参数,你不传递参数也行,函数会默认使用制定好的 缺省参数,可以理解成 默认参数
概念:缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实 参则采用该形参的缺省值,否则使用指定的实参。
void Func(int a = 0)
{cout << a << endl;
}
int main()
{Func(); // 没有传参时,使用参数的默认值:int a = 0Func(10); // 传参时,使用指定的实参:a = 10return 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 = 5return 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,不会跳跃的给到 creturn 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 的 AddAdd(c, d); // 对应 double 的 Addreturn 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++用修饰后的函数名去寻找
如果深入的问细节,就回答上面学过的细节过程就好