从C到Cpp——九、Cpp的强大函数特性(二)

一、默认参数

1--

Cpp程序中的函数可以在函数声明中提供默认参数。

#include <iostream>
using namespace std;

int myplus(int a,int b=2,int c=3);
int main(void)
{
	cout<<myplus(1);
	return 0;
} 

int myplus(int a,int b,int c)
{
	return a+b+c;
}

如上,得到的结果为6;需要注意,必须在函数声明而不是函数定义中提供默认参数

2--

Cpp提供的默认参数必须从右向左添加,在上述一段程序中,必须先为参数c提供默认参数,再为参数b提供默认参数。如果给b添加默认参数而不给c添加默认参数,是不允许的

3--

使用函数时,实参要按照从左往右的顺序依次赋给相应的形参,而不能跳过任何参数。下面的函数调用是不允许的

myplus(1, ,3);

正确的写法是

myplus(1,3);

这时会把3这个实参赋给形参b,而不是形参c!

二、函数重载

1--

Cpp允许定义多个相同名称的函数,前提是这些函数的参数数目和排列方式不能完全相同

以下几个函数都是可以同时存在的!

int myplus(int a,int b);
int myplus(int a,int b,int c);
int myplus(int a,double b);
int myplus(double b,int a); 

但下面这几对函数不可以同时存在!

int myplus(int a,int b);
int myplus(int b,int a);

不能以形参名称作为区分!

int myplus(int a,int b);
double myplus(int a,int b);
​

不能以返回值类型作为区分!

2--使用了重载特性的函数使用时要注意参数类型的匹配

如果参数类型不匹配,C++将拒绝调用并且报错!举例如下:

int myplus(int a,int b);
int myplus(double a,double b);

int main(void)
{    
    unsigned int a,b;
    myplus(a,b);
}

这种情况C++将拒绝调用myplus函数!

3--函数的形参不能和其引用重载!

double myfunc(int a);
double myfunc(int &a);

这是不可以的!因为两种myfunc函数在使用时没有区别!这个很好理解。

4--匹配函数时,不区分const和非const变量

#include <iostream>
using namespace std; 
int myplus(const int* a,const int* b);

int main(void)
{    
    const int a=2,b=3;
    cout<<myplus(&a,&b)<<endl;
    int m=2,n=3;
    cout<<myplus(&m,&n);
}

int myplus(const int* a,const int* b)
{
	return *a+*b; 
}
#include <iostream>
using namespace std; 
int myplus(const int a,const int b);

int main(void)
{    
    const int a=2,b=3;
    cout<<myplus(a,b)<<endl;
    int m=2,n=3;
    cout<<myplus(m,n);
}

int myplus(const int a,const int b)
{
	return a+b; 
}

这就是说,在函数中使用定义了const,使用函数时非const变量可以被正常传入。

但是,在函数中未定义const,使用函数时const变量不能被传入。

5--下面两个函数也被认为说重载

int myplus(const int* a,const int* b);
int myplus(int* a,int* b);

调用这个函数时,编译器将根据实参是否为const来决定具体用哪一个函数!示例如下:

#include <iostream>
using namespace std; 
int myplus(const int* a,const int* b);
int myplus(int* a,int* b);

int main(void)
{    
    const int a=2,b=3;
    cout<<myplus(&a,&b)<<endl;
    int m=2,n=3;
    cout<<myplus(&m,&n);
}

int myplus(int* a,int* b)
{
	return *a+*b; 
}

int myplus(const int* a,const int* b)
{
	return *a-*b; 
}

结果是-1和5。

三、函数模板

我们在定义swap函数时,会因为交换的变量类型不同而编写多个函数。函数模板这一特性这可以简化这一问题,只要编写一段泛型,就可以让编译器生成可以用于交换不同类型变量的函数,岂不美哉?

1--函数模板的定义方式:

template<typename ANYTYPE>
void FUNC(ANYTYPE &a,ANYTYPE &b)
{
    statement;
}

其中template和typename为关键字,ANYTYPE可以被替换成任意名称,FUNC为函数名称。

注意,typename关键字可以用class替换,两者在此处功能等价!

具体的使用方式示例如下:

#include <iostream>
using namespace std;

struct Student
{
	int grade;
	int age;
}; 

template <class T>
void myswap(T &a,T &b)
{
	T tmp=a;
	a=b;
	b=tmp;
} 

int main(void)
{
	int m=1,n=2;
	double p=1.2,q=2.1;
	Student a={100,20},b={90,21}; 
	myswap(m,n);
	myswap(p,q);
	myswap(a,b);
	cout<<"m="<<m<<endl;
	cout<<"n="<<n<<endl;
	cout<<"p="<<p<<endl;
	cout<<"q="<<q<<endl;
	cout<<"a.age="<<a.age<<endl;
	cout<<"b.age="<<b.age<<endl;
}

读者可以自己去运行一下,发现任何类型——整型,小数到结构甚至是类对象,都是可以由模板生成出堪用的交换函数的!

2--函数定义和声明若分开,都必须加上含有关键字的那一行!

#include <iostream>
using namespace std;

template <class T>
void myswap(T &a,T &b);

int main(void)
{
	int m=1,n=2;
	myswap(m,n);
	cout<<"m="<<m<<endl;
	cout<<"n="<<n<<endl;
}

template <class T>
void myswap(T &a,T &b)
{
	T tmp=a;
	a=b;
	b=tmp;
} 

如上一段代码template <class T>出现了两次!

3--并非所有的模板参数都必须是模板参数类型

下面这一段定义也是合法的!

#include <iostream>
using namespace std;

template <class T>
void myswap(T &a,T &b,int c);

int main(void)
{
	int m=1,n=2;
	myswap(m,n,3);
	cout<<"m="<<m<<endl;
	cout<<"n="<<n<<endl;
}

template <class T>
void myswap(T &a,T &b,int c)
{
	T tmp=a;
	a=b;
	b=tmp;
	cout<<c<<endl;
} 

4--重载的模板

模板也是可以重载的,要求是被重载的模板的函数特性标必须不同,也就是形参的类型和排列方式不能完全相同!

为了给数组也设置交换函数,我们应用了模板重载的性质:

#include <iostream>
using namespace std;

template <class T>
void myswap(T &a,T &b);

template <class T>
void myswap(T* a,T* b,int n);

int main(void)
{
	int m=1,n=2;
	myswap(m,n);
	cout<<"m="<<m<<endl;
	cout<<"n="<<n<<endl;
	int p[5]={1,2,3,4,5};
	int q[5]={6,7,8,9,10};
	myswap(p,q,5);
	cout<<"p[3]="<<p[3]<<endl;
	cout<<"q[3]="<<q[3]<<endl;
}

template <class T>
void myswap(T &a,T &b)
{
	T tmp=a;
	a=b;
	b=tmp;
} 

template <class T>
void myswap(T* a,T* b,int n)
{
	int i;
	for (i=0;i<n;i++)
	{
		T tmp=a[i];
		a[i]=b[i];
		b[i]=tmp;
	}
}

四、模板的实例化和具体化

1--实例化

在代码中包含函数模块本身并不会直接生成函数定义,函数模板只是一个用于生成函数定义的方案。只有通过模板的实例化,才能形成具体的、堪用的函数定义。

2--隐式实例化

使用模板函数时,编译器会进行隐式实例化,自动生成匹配类型的函数

#include <iostream>
using namespace std;

template <class T>
void myswap(T &a,T &b)
{
	T tmp=a;
	a=b;
	b=tmp;
} 

int main(void)
{
	int m=1,n=2;
	myswap(m,n);
	cout<<"m="<<m<<endl;
	cout<<"n="<<n<<endl;
}

对于上面这个片段,在定义myswap模板时并没有生成用于int的交换函数,在main函数中,编译器发现程序确确实实需要交换m和n这两个int型变量时,才生成了相应的函数。

所以,这个程序全程没有生成过用于double的交换函数!

3--显式实例化

那么,有没有提前把这个double类型交换函数定义出来的方法呢?有,这就是显式实例化!

具体方法如下,在函数定义后填上这么一行:

template void myswap<double>(double,double)

这样,double类型交换函数就被我们手动生成了!

4--具体化

已知myswap函数可以被用于结构体的交换,但如果我只想交换结构体的第一个元素呢?这时候应该怎么处理呢?

这时候就要引出具体化的概念了。具体化函数给予编译器一个优先于函数模板的选择,如果存在具体化定义,就不再使用函数模板,转而按照具体化函数来生成函数定义!

5--显式具体化的方法

在函数声明和定义前加上template <>这一段,并在参数列表前加上<具体化的类型>,就可以实现具体化函数。如果job是一个结构体的类型名,那么有这样的具体化方式:

template <> void myswap<job>(job& job&);

注意,这个<job>是可以省略的。下面这一段代码,就实现了结构体成员的部分交换。

#include <iostream>
using namespace std;

struct job
{
	int floor;
	int salary;
};

template <class T>
void myswap(T &a,T &b);

template <> void myswap(job &,job &);

int main(void)
{
	int m=1,n=2;
	myswap(m,n);
	cout<<"m="<<m<<endl;
	cout<<"n="<<n<<endl;
	job a={2,10000},b={5,20000};
	myswap(a,b);
	cout<<"a.floor="<<a.floor<<endl;
	cout<<"b.floor="<<b.floor;
}

template <class T>
void myswap(T &a,T &b)
{
	T tmp=a;
	a=b;
	b=tmp;
} 

template <> void myswap(job &a,job &b)
{
	int tmp=a.floor;
	a.floor=b.floor;
	b.floor=tmp;
}

具体化优先于函数模板,而非模板函数优先于具体化的原型和模板函数

6--要注意区分显式实例化和显式具体化!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值