一、默认参数
默认参数是C++中新引入的内容。
默认参数指的是,当函数调用中省略了实参时,函数会自动使用默认的参数。
要想使用默认参数,就必须得通过函数原型。因为编译器是通过函数原型爱了解函数所用的参数 数量,因此函数原型必须将可能得默认参数告诉程序。方法如下,left()函数的原型:
char* left(const char* str, int n=1);
上述函数是希望返回一个新的字符串,因此将返回类型设置为char*,也就是指向char的指针;
因为希望原始字符串保持不变,函数的第一个参数使用const限定符;
因为希望n的默认值为1,就讲n赋值1,如果调用函数left()省略了参数n,那函数就将n设置为1;
对于带参数列表的函数,必须从右往左添加默认值,也就是说,如果要为某个参数设置默认值,那你就得保证,它右边的所有参数都已经提供了默认值:
int harpo(int n, int m=4, int j=5); //合法的操作
int groucho(int k=1, int m=2, int n=3);//合法的操作
int chico(int n, int m=6, int j);//非法的操作,右边还有参数没有初始化
上面的harpo()函数原型允许调用该函数时,提供1个、2个、或3个默认参数
beeps = harpo(2); //相当于使用harpo(2, 4, 5)
beeps = harpo(1,8); //相当于使用harpo(1, 8, 5)
beeps = harpo(8,7,6); //没有使用默认参数
实参按从左到右的顺序一次被赋给相应的形参,不能跳过任何参数。
默认参数不算变成方面的重大突破,只是提供了一种便捷的方式。使用默认参数,可以减少要定义的析构函数。
程序实例1:
#include <iostream>
using namespace std;
const int ArSize = 80;
char* left(const char* str, int n = 1);
int main()
{
char sample[ArSize];
cout << "enter a string:\n";
cin.get(sample,ArSize);
char* ps = left(sample, 7);
cout << ps << endl;
delete [] ps;
ps = left(sample);
cout << ps << endl;
delete [] ps;
return 0;
}
char* left(const char* str, int n)
{
if(n < 0)
n = 0;
char* p = new char [n+1];
int i;
for(i=0; i<n && str[i]; i++)
{
p[i] = str[i];
}
while(i<=n)
{
p[i++] = '\0';
}
return p;
}
输出结果:
enter a string:
hello world
hello w
h
二、函数重载(Overload)
函数重载,是C++在C语言的基础上新增的功能。默认参数让程序能够使用不同数目的参数调用同一个函数,而函数重载能够使用多个同名的函数。术语“函数重载”指的计就是可以有多个同名的函数。函数重载就像就像一个动词有多重含义,比如root,可以是助威的意思,也可以是种植的意思,具体表示什么意思,就要看它的使用场景。
函数重载的关键就是函数的参数列表,参数列表页称为特征标。如果两个函数的参数数目和类型都相同,参数的排列顺序也想要,则它们的特征标是相同的,特征标与变量的名称无关。
C++允许定义名称相同的函数,是有前提的,函数的特征标要不一样。
重载函数的表现形式以及条件
以下三个条件,只要满足其中的一个就表示重载函数:
1、函数的参数个数不同;
2、函数参数的类型不同;
3、函数参数的顺序不同
程序实例2:函数名与不同的参数搭配时,函数的含义是不一样的,表示不同的函数
#include<stdio.h>
#include<string.h>
int func(int x)
{
return x;
}
int func(int a, int b)
{
return a + b;
}
int func(const char* s)
{
return strlen(s);
}
int main()
{
printf("%d\n", func(3)); //输出3
printf("%d\n", func(4,5));//输出9
printf("%d\n", func("helloworld!"));//输出11
return 0;
}
编译器调用重载函数的规则
1、将所有同名函数作为候选项;
2、尝试寻找可行的候选参数
精确匹配实参
通过默认参数能够匹配实参
通过默认类型转换匹配实参
3、匹配失败的情况
最终的候选函数有两个及以上,就编译失败;
没有候选函数,编译失败。
函数重载的注意事项
1、函数重载是由函数名和参数列表决定的
2、重载函数本质上是相互独立的不同函数;
3、重载函数的函数类型是不一样的;
4、函数的返回值不能作为函数重载的依据
函数重载的本质分析
程序实例3:函数重载的本质分析
#include<stdio.h>
#include<string.h>
//函数类型:int(int, int, int)
int add(int a, int b, int c)
{
return a + b + c;
}
//函数类型:int(int,int)
int add(int a, int b)
{
return a + b;
}
int main()
{
printf("%d\n", add(1, 2)); //调用int add(int a, int b)
printf("%p\n",(int(*)(int, int))add);//打印函数的入口地址
printf("\n");
printf("%d\n", add(1, 2, 3));//调用int add(int a, int b, int c)
printf("%p\n", (int(*)(int, int, int))add);//打印函数的入口地址
return 0;
}
输出结果:
3
00A513CF
6
00A513CA
从打印的函数地址来看,函数add(int a, int b, int c)与函数add(int a, int b)的地址是不一样的,表示不同的函数。
重载函数与函数指针
将重载函数的函数名赋值给函数指针时:
根据重载规则挑选与函数指针参数列表一致的候选者;
严格匹配候选者的函数类型和函数指针的函数类型;
程序实例4:根据函数指针的参数列表去对应重载函数
#include<stdio.h>
#include<string.h>
int func(int x)
{
return x;
}
int func(int a, int b)
{
return a + b;
}
int func(const char* s)
{
return strlen(s);
}
typedef int(*PFUNC) (int a);
int main()
{
int c = 0;
PFUNC p = func;
c = p(2); //函数指针去匹配函数
printf("c = %d\n",c);
return 0;
}
输出结果:
c = 2
从输出的结果知道p(2),相当于调用了函数int func(int x)。
什么时候使用函数重载
虽然函数重载很吸引人,但是不要滥用。只有当函数基本上执行相同的任务,但使用不同形式的数据时,才应该使用重载。
三、函数模板
函数模板也是C++新增的一项特性,函数模板==通用函数,有了函数模板就可以进行泛型定义函数了。
可参考数据结构学习:泛型编程(函数模板与类模板)_资深流水灯工程师的博客-CSDN博客
我们知道交换两个int型变量的值,可以使用下面的代码实现
void swap(int& a, int& b)
{
int temp;
temp = a;
a = b;
b = temp;
}
如果要交换两个float类型变量的值呢,改一下代码就是的
void swap(float& a, float& b)
{
float temp;
temp = a;
a = b;
b = temp;
}
如果要交换两个char呢,double呢都要这么改下去吗?也不是不可以,但是C++中有更简单的方法。那就是函数模板,使用函数模板,既可以节省时间,还更可靠。代码可以是这样的
template <typename T>
void swap(T& a, T& b)
{
T temp;
temp = a;
a = b;
b = temp;
}
需要使用关键字template和关键字typename,就像上面代码的写法,至于那个T,大家都是这么勇的,你也可以用其他你想用的字母或单词。我还是用T,这个T可以代表任何类型。
#include <iostream>
using namespace std;
template<typename T>
void exchange(T& a, T& b);
int main()
{
int i = 10;
int j = 20;
cout << "i = " << i << endl;
cout << "j = " << j << endl;
exchange(i,j);
cout << "i = " << i << endl;
cout << "j = " << j << endl;
double x = 24.5;
double y = 81.7;
cout << "x = " << x << endl;
cout << "y = " << y << endl;
exchange(x,y);
cout << "x = " << x << endl;
cout << "y = " << y << endl;
return 0;
}
template<typename T>
void exchange(T& a, T& b)
{
T temp;
temp = a;
a = b;
b = temp;
}
输出结果:
i = 10
j = 20
i = 20
j = 10
x = 24.5
y = 81.7
x = 81.7
y = 24.5
函数模板不能缩短可执行程序,上面的代码最终还是两个独立的函数定义。
模板重载
需要对多个不同类型使用同一种算法的时候,可以使用模板,但是并非所有的类型都是使用相同的算法,这就需要像重载函数一样重载模板。下面的程序实例新增一个交换模板,原先模板的特征标是(T&,T&),新模板的特征标是(T&, T&, n);
程序示例:
#include <iostream>
using namespace std;
template<typename T>
void exchange(T& a, T& b);
template<typename T>
void exchange(T a[], T b[], int n);
void show(int a[]);
const int Lim = 8;
int main()
{
int i = 10;
int j = 20;
cout << "i = " << i << endl;
cout << "j = " << j << endl;
exchange(i,j);
cout << "i = " << i << endl;
cout << "j = " << j << endl;
int d1[Lim] = {0,7,0,4,1,7,7,6};
int d2[Lim] = {0,7,2,0,1,9,6,9};
show(d1);
show(d2);
exchange(d1,d2,Lim);
show(d1);
show(d2);
return 0;
}
template<typename T>
void exchange(T& a, T& b)
{
T temp;
temp = a;
a = b;
b = temp;
}
template<typename T>
void exchange(T a[], T b[], int n)
{
T temp;
for(int i=0; i<n; i++)
{
temp = a[i];
a[i] = b[i];
b[i] = temp;
}
}
void show(int a[])
{
cout <<a[0] <<a[1]<<"/";
cout <<a[2] <<a[3]<<"/";
for(int i=4;i<Lim;i++)
cout << a[i];
cout << endl;
}
输出结果:
i = 10
j = 20
i = 20
j = 10
07/04/1776
07/20/1969
07/20/1969
07/04/1776
显示具体化
假设定义了下面的结构体
struct job
{
char name[40];
double salary;
int floor;
};
如果希望交换两个结构体的内容,可以使用下面的代码来完成
temp = a;
a = b;
b = temp;
由于C++允许将一个结构体赋值给另一个结构体,即使T是一个job结构体类型,上面的代码也是适用的。
假设要只想交换salary和floor成员,不交换name成员,则需要使用不同的代码,这时候就需要提供一个具体化函数定义,称为显示具体化,其中包含所需的代码,当编译器找到与函数调用匹配的具体化定义时,就使用该定义,而不寻找模板。
对于给定的函数名,可以有非模板函数、模板函数、显示具体化模板函数、以及它们的重载版本;
显示具体化的原型和定义以template<>开始,并通过名称来指出类型;
具体化优先于常规模板,非模板函数优先于具体化的原型;
#include <iostream>
using namespace std;
template<typename T>
void exchange(T& a, T& b);
struct job
{
char name[40];
double salary;
int floor;
};
template<> void exchange<job>(job &j1, job &j2);
void show(job& j);
int main()
{
cout.precision(2);
cout.setf(ios::fixed, ios::floatfield);
int i = 10, j =20;
cout << "i, j = " << i << ", " << j << ".\n";
exchange(i,j);//调用exchange(int&, int&)
cout << "after exchange i, j = " << i << ", " << j << ".\n";
job sue = {"susan", 73000.60, 7};
job sidney = {"sidney", 78060.70, 9};
cout << "before job exchange: \n";
show(sue);
show(sidney);
exchange(sue, sidney); //调用exchange(job&, job&)
cout << "after job exchange: \n";
show(sue);
show(sidney);
return 0;
}
template<typename T>
void exchange(T& a, T& b)
{
T temp;
temp = a;
a = b;
b = temp;
}
//显示具体化,如果exchange函数的参数是job类型就调用这个函数
template<> void exchange<job>(job &j1, job &j2)
{
double t1;
int t2;
t1 = j1.salary;
j1.salary = j2.salary;
j2.salary = t1;
t2 = j1.floor;
j1.floor = j2.floor;
j2.floor = t2;
}
void show(job& j)
{
cout << j.name << " : $" << j.salary << " on floor " << j.floor << endl;
}
输出结果:
i, j = 10, 20.
after exchange i, j = 20, 10.
before job exchange:
susan : $73000.60 on floor 7
sidney : $78060.70 on floor 9
after job exchange:
susan : $78060.70 on floor 9
sidney : $73000.60 on floor 7