一、函数重载
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();//调用的是???歧义
答案:有歧义,解决方法:可以定义命名空间