C++学习第三课 缺省函数、函数重载与引用
C++学习第一课:C++学习须知
C++学习第二课:命名空间域
C++学习第三课:缺省函数与函数重载
文章目录
前言
本篇文章是C++入门的第二篇,延续了上一篇的内容继续谈c++对于c的改进部分:缺省函数、函数重载以及引用的内容
一、C语言的第二个不足:缺省参数(默认参数)的使用
当函数只有一个形参时
void Func(int a = 0)
{
cout<<a<<endl;
}
int main()
{
Func(); // 没有传参时,使用参数的默认值
Func(10); // 传参时,使用指定的实参
return 0;
}
当函数有两个及以上的形参时:全缺省函数(上)半缺省函数(下)
void Func(int a = 10, int b = 20, int c = 30)
{
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
cout<<"c = "<<c<<endl;
}
void Func(int a, int b = 10, int c = 20)
{
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
cout<<"c = "<<c<<endl;
}
1. 当函数有两个及以上形参时的传参规则
答:从左到右。因此!我们设置半缺省函数(默认函数)的时候应该从右往左设置默认参数
注1:不能指定传参
注2:不能跳着传参
2. 使用场景辨析:函数在定义和申明时是否都需要写缺省值
(1)不支持两个都给
答:因为怕不一致,那就出现听谁的的问题
(2)那是声明给还是定义给呢?
答:声明给
3. 缺省参数的主要用处—以栈为例
初始版:传统静态栈
struct Stack
{
int* a;
int top;
inr capacity;
};
void StackInit(struvt Stack* pst)
{
pst->a=(int *)malloc(sizeof(int)*4);
if(pst->a == NULL){
perror("malloc fail");
return;
}
pst->top=0;
pst->capacity=4;
}
int main()
{
struct Stack st;
StackInit(&st);
//插入100个数据
}
坏处:设死了,每次都只开辟一点点,每次都要扩容,即使你知道你要100个,效率太低
升级版:使用传参但不设置缺省参数
struct Stack
{
int* a;
int top;
inr capacity;
};
void StackInit(struvt Stack* pst,int defaultCapacity)
{
pst->a=(int *)malloc(sizeof(int)* defaultCapacity);
if(pst->a == NULL){
perror("malloc fail");
return;
}
pst->top=0;
pst->capacity=defaultCapacity;
}
int main()
{
struct Stack st;
StackInit(&st,100);
//插入100个数据
}
好处:如果已知栈需要插入多少数据,那么可以直接传参开辟所需空间,比较灵活
最终版:设置传参且设置缺省值
struct Stack
{
int* a;
int top;
inr capacity;
};
void StackInit(struvt Stack* pst,int defaultCapacity=4)
{
pst->a=(int *)malloc(sizeof(int)* defaultCapacity);
if(pst->a == NULL){
perror("malloc fail");
return;
}
pst->top=0;
pst->capacity=defaultCapacity;
}
int main()
{
struct Stack st1;
StackInit(&st1,100);
//插入100个数据
struct Stack st2;
StackInit(&st2);
//不知道要插入几个数据
}
好处:更灵活,即使不知道实际需要多少也有默认值进行开辟,特别是当你需要创建两个栈,一个知道需要多大,另一个不知道的时候,就可以分别处理,格外灵活
C语言解决这种情况的方法:define
struct Stack
{
int* a;
int top;
inr capacity;
};
#define DEFAULT_CAPACITY 100
void StackInit(struvt Stack* pst)
{
pst->a=(int *)malloc(sizeof(int)* DEFAULT_CAPACITY);
if(pst->a == NULL){
perror("malloc fail");
return;
}
pst->top=0;
pst->capacity=DEFAULT_CAPACITY;
}
int main()
{
struct Stack st1;
StackInit(&st1,100);
//插入100个数据
struct Stack st2;
StackInit(&st2);
//不知道要插入几个数据
}
分析:其实本质上也是设死了,无法满足不同需求
二、函数重载
粗略理解:函数重载类似于函数的不同词性的一词多义
1. 函数重载的定义
(1)文字定义:函数重载是函数的一种特殊情况,指在同一个作用域 中声明几个功能类似的同名函数,这些函数的形参列表中,或参数个数不同,或参数类型不同,或类型顺序不同,用于解决不同类似数据的类似问题,对于这类函数,使用时C++可以实现自动匹配
注:返回值不同是不构成重载的
构成重载的代码举例:参数类型不同(上)参数个数不同(中)参数类型顺序不同(下)
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;
}
void f()
{
cout << "f()" << endl;
}
void f(int a)
{
cout << "f(int a)" << endl;
}
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;
}
不构成重载的典型代码举例
void f(int a, char b)
{
cout << "f(int a,char b)" << endl;
}
void f(int b, char a)
{
cout << "f(int b, char a)" << endl;
}
由于函数重载而产生的歧义代码
void f()
{
cout << "f()" << endl;
}
void f(int a=10)
{
cout << "f(int a)" << endl;
}
int main()
{
f();//无法判断是调用哪个函数
return 0;
}
注:C语言不允许函数重载的出现
2. cpp是怎么支持函数重载的自动匹配的
注:前面说到的cout的自动识别也是一个道理,或者说也属于函数重载
(1)编译链接过程
- 预处理:头文件(.h 和 .cpp 文件)展开 + 宏替换 + 条件编译 + 去掉注释…(生成 .i 文件)
- 编译:检查语法+生产汇编代码(生成 .s 文件)
- 汇编:汇编代码转换.o为二进制机器码,让CPU能看懂(生成 .o 文件)
- 链接:把代码、汇编等文件合在一起,形成链接,生成可执行程序(生成 .exe 和 .out 文件)
是不是所有的函数都要去链接找
答:编译阶段有声明并且在后面没有明确的定义被编译才需要,声明了要去链接里面确认
(2)函数名修饰规则
答:前缀+函数名个数+函数名+参数类型首字母 ,如:_Z4funcid
注意:这个只是g++(Linux指令)的修饰规则,其他的并不一样
问题一:为什么cpp可以支持
答:在编译阶段,对于不同重载函数,编译时的函数名修饰规则导致生成的名字不一样,因此指向不同函数
问题二:为什么c不支持
答:因为c语言在编译的函数名修饰时,用的地址是直接函数名,太过直接导致无法区分重载
3. 引用(针对与C指针的优化)
注:首先明确,c++仍然支持C中的各类指针的玩法
(1)引用的概念:引用定义的不是一个新变量,而是给已经存在的变量取了一个别名,编译器不会为引用变量开辟空间,他和他所引用的变量是共用同一块内存空间的
引用代码举例
void TestRef()
{
int a = 10;
int& ra = a;//<====定义引用类型
printf("%p\n", &a);
printf("%p\n", &ra);
}
注1:共用了&这个符号,当用法不同,可以兼顾,注意区分即可
注2:引用是无法更改的,即你定义a是b的别名,a就不可能是别人的别名了
注3:一个变量可以有多个引用
注4:引用在定义时必须初始化
常见错误使用类型
void TestRef()
{
int a = 10;
// int& ra; // 该条语句编译时会出错
int& ra = a;
int& rra = a;
printf("%p %p %p\n", &a, &ra, &rra);
}
void TestConstRef()
{
const int a = 10;
//int& ra = a; // 该语句编译时会出错,a为常量
const int& ra = a;
// int& b = 10; // 该语句编译时会出错,b为常量
const int& b = 10;
double d = 12.34;
//int& rd = d; // 该语句编译时会出错,类型不同
const int& rd = d;
}
(2)引用的玩法
做参数
void Swap(int& left, int& right)
{
int temp = left;
left = right;
right = temp;
}
做返回值
int& Count()
{
static int n = 0;
n++;
// ...
return n;
}
应用
int& Add(int a, int b)
{
int c = a + b;
return c;
}
int main()
{
int& ret = Add(1, 2);//ret是c的引用,函数返回后这个内存位置被消耗,ret仍然保存了这个位置的引用,但是可能指向一个被重新分配或者覆盖的内存区域
Add(3, 4);//这一步返回时,ret访问的还是这个内存,因此结果是7
cout << "Add(1, 2) is :"<< ret <<endl;//注意:如果你这里多打印几次,结果将不会是7,因为ret是c的引用,和c共用一个内存空间,在add(3,4)结束之后,能指向一个被重新分配或者覆盖的内存区域而导致数值更改
return 0;
}```
传统代码:使用普通传参/指针
void Swap(int a,int b)
{
//这种写法输出的a、b和x、y没有直接关系
int temp = a;
int a= b;
int b = temp;
}
int main()
{
int x = 0;
int y = 1;
Swap(x,y);
return 0;
}
更新代码:使用引用
void Swap(int& a,int& b)
{
int temp = a;
int a= b;
int b = temp;
}
int main()
{
int x = 0;
int y = 1;
Swap(x,y);
return 0;
}
指针也是变量,因此也可以给指针弄别名(引用)
总结
以上是对缺省函数、函数重载、引用的基本知识,对于函数重载部分,存在着很多底层逻辑的分析以及Linux系统的使用,暂时没办法继续挖掘,时隔十天正式打卡第二篇,再接再厉!
标签:函数,int,void,C++,学习,重载,pst,cout From: https://blog.csdn.net/m0_74983085/article/details/142067999