一、函数重载
1、问题的引入
在实际开发中,有时候我们需要实现几个功能类似的函数,只是有些细节不同。例如希望交换两个变量的值,这两个变量有多种类型,可以是 int、float、char、bool 等,我们需要通过参数把变量的地址传入函数内部。在C语言中,程序员往往需要分别设计出三个不同名的函数,其函数原型与下面类似:
void swap1(int *a, int *b); //交换 int 变量的值
void swap2(float *a, float *b); //交换 float 变量的值
void swap3(char *a, char *b); //交换 char 变量的值
void swap4(bool *a, bool *b); //交换 bool 变量的值
那么在C++中,有没有一种方法,允许多个函数拥有相同的名字,只要它们的参数列表不同就可以呢。
答案:使用函数重载。
2、概念
用相同的函数名定义多个不同的功能称为函数重载。重载的函数根据参数的个数和类型进行区分,但不能单独根据返回类型进行区分。
3、例子
void swap(int *a,int *b)
{
*a = *a + *b;
*b = *a - *b;
*a = *a - *b;
}
void swap(float *a,float *b)
{
*a = *a + *b;
*b = *a - *b;
*a = *a - *b;
}
void swap(char *a,char *b)
{
*a = *a + *b;
*b = *a - *b;
*a = *a - *b;
}
void swap(bool *a,bool *b)
{
*a = *a + *b;
*b = *a - *b;
*a = *a - *b;
}
在函数调用的时候会根据不同的参数列表选择调用对应的函数
4、函数重载的规则
- 函数名称必须相同
- 参数列表必须不同(个数不同、类型不同、参数排列顺序不同等)
- 函数的返回类型可以相同也可以不相同。
- 仅仅返回类型不同不足以成为函数的重载
5、函数重载的作用 - 解决函数名字资源问题
- 函数调用的时候很方便,自动根据不同的参数调用不同函数(静态多态-编译时候多态)
6、注意
在c++中编译器会检查函数名称和参数列表, 在c语言中编译器只检查函数名称
7、函数重载原理
总结:返回值不一样不能实现函数重载
const参数能实现函数重载
二、函数默认参数(缺省实参)和占位参数
1、概念
在声明函数的时候可以赋予函数参数默认值。所谓默认值就是在调用时,可以不写某些参数的值,编译器会自动把默认值传递给调用语句中。
2、特点
- 如果函数的声明和定义是分开的,必须在声明的时候设置默认值,不可以在定义的时候设置默认值;
- 不能将实际值传递给引用类型的参数。可以将变量作引用类型参数的默认值,这时变量必须是已经声明且是全局变量
#include<iostream>
using namespace std;
int g_val = 10;
//默认值是在函数声明的时候进行设置
int func(int a=10);
//不能将实际值传递给引用类型的参数。可以将变量作引用类型参数的默认值,这时变量必须是已经声明且是全局变量
//int rfunc(int &a=10); --除非 const int &a=10
int rfunc(int &a=g_val);
int main()
{
func();
return 0;
}
//如果函数的定义和声明是分开的,那么函数定义的时候不能设置默认值,而是在声明的时候进行设置
int func(int a)
{
cout<<"a:"<<a<<endl;
}
int rfunc(int &a)
{
cout<<"a:"<<a<<endl;
}
- 若给某一参数设置了默认值,那么在参数表中其后所有的参数都必须也设置默认值
如果多参数默认,必须满足从右往左连续默认
void fun(int a, int b=9, int c=1); 允许
void fun(int a=1, int b, int c=2); 不允许
3、占位参数
占位参数:跟默认参数不同,在函数定义时,形参只写类型,不写形参变量名
语法:
返回值类型 函数名(type ) //type --- int char
{
}
占位参数的使用场景比较少,只在运算符重载中可以应用
void test(int) //占位参数
{
}
void test1(char) //占位参数
{
}
int main()
{
int b = 10;
test(b); //带占位参数的函数调用时,要传入对应类型的参数值
return
}
练习5:使用链表设计一个录入学生信息的函数(参数为学生信息)(输入信息有个学号,姓名,年龄,班级 参数顺序自定),结合函数重载和默认参数的特点,设计的时候使后期使用更方便
#include <iostream>
#include <cstring>
/* 练习2:设计一个录入学生信息的函数(参数就是学生信息)
(输入的时候有姓名、年龄、班级...),结合函数重载和默认参数的特点
设计出来的程序使用的时候 更加方便 */
using namespace std;
enum MODE
{
ADD, //增加
SHOW, //显示
EXIT //退出
};
struct student{
char name[256];
int age;
char classes[256];//班级
};
//单向非循环链表
typedef struct node{
struct student info;//数据域
struct node *next;//指针域
}Node_t;
//链表的头结点
typedef struct list{
Node_t *head;//数据的首结点
Node_t *tail;//数据的尾结点
int nodeNumber;//链表中结点的个数
}List_t;
List_t *create_list()
{
//1、申请一个头结点的内存空间
List_t *list = new List_t;
list->head = list->tail = NULL;
list->nodeNumber = 0;
return list;
}
void insert_nodeToList_tail(List_t *list,const char *name,const int age=21,const char *classes="gz2166")
{
if(list==NULL)
return ;
//1、新建数据结点
Node_t *newNode = new Node_t;
//2、初始化
strcpy(newNode->info.name,name);
newNode->info.age = age;
strcpy(newNode->info.classes,classes);
newNode->next = NULL;
//3、将新建的结点插入到链表中
if(list->head == NULL) //从无到有
{
list->head = newNode;
list->tail = newNode;
}
else{ //从少到多 ---尾插法
//当前尾结点的next指向新结点
list->tail->next = newNode;
//更新尾结点
list->tail = newNode;
}
list->nodeNumber++;
}
void print_allToList(List_t *list)
{
Node_t *p = list->head;
cout<<"姓名\t\t"<<"年龄\t"<<"班级\t"<<endl;
while(p)
{
cout<<p->info.name<<"\t\t"<<p->info.age<<"\t"<<p->info.classes<<"\t\t"<<endl;
p = p->next;
}
}
int main()
{
List_t *list = create_list();
insert_nodeToList_tail(list,"zhang3",22);
insert_nodeToList_tail(list,"li4");
insert_nodeToList_tail(list,"laowang",23,"gz2169");
print_allToList(list);
return 0;
}
四、函数重载与函数默认参数
思考:假如一个程序中有如下两个函数
void test()
{
}
void test(int x=10)
{
}
调用:
test(11);//调用 的是:void test(int x=10)
test();//调用的是???歧义
答案:有歧义,解决方法:可以定义命名空间