内容均出自《C++ primer plus》,本文仅为个人理解总结所用。 若有不明欢迎站内私信交流。 若发现文中错漏之处,期待不吝赐教站内私信。
(一)函数重载(多态)
先上定义:
C++ Premer Plus (page276)
- 函数多态是C++在C语言的基础上新增的功能。
- 术语"多态":指的是有多种形式。
- 术语"函数重载":指的是可以有多个同名函数,因此对名称进行了重载。
- 两个术语是同一回事,但通常使用函数重载。
- 函数多态(函数重载)让程序能够使用多个同名的函数。
实例理解:
同名函数,程序如何区分需要使用哪个? ——根据特征标
- 函数重载的关键是函数的参数列表——也称为特征标
- 若参数数目/参数类型不同,则特征标也不同。
- tips: const限定符、 &引用符不为特征标
void print(const char* str, int width);
void print(double d, int width);
void print(long l, int width);
void print(int i, int width);
void print(const char *str);
使用print( )函数时,编译器将根据所采用的的用法使用有相应特征标的原型:
printf("Pancakes", 15);
printf("Syrup");
printf(1999.0, 10);
printf(1999, 12);
printf(1999L, 15);
- tips: 特别注意:const限定符、 &引用符不为特征标
(二)函数模板
先上定义
函数模板是通用的函数描述,使用泛型来定义函数,其中的泛型可用具体的类型(如int 或 double)替换。 通过类型作为参数传给模板,可使编译器生成该类型的函数。
使用场景分析:
- 假设我们需要交换两个int变量的值的函数,一般来说则直接撸:
void swap(int &a, int &b)
{
int tmp;
tmp = a;
a = b;
b = tmp;
}
- 现在我们新增了需求,要求增加一个交换double变量的值的函数,一般来说可以再加一个函数:
void swap(double &a, double &b)
{
double tmp;
tmp = a;
a = b;
b = tmp;
}
- 那如果需求又增加了呢?要求char,short,float,long都来一套?
- 方法一:都来一套也行。
- 方法二:
它们做一模一样的事情,只是参数类型不同。 C++允许以任意类型的方式来定义函数:函数模板;
函数模板建立
template <typename AnyType>
void swap(AnyType &a, AnyType &b)
{
AnyType tmp;
tmp = a;
a = b;
b = tmp;
}
对,就这么简单,这就是泛型基本概念。
Tips:
- 模板并不创建函数,而只是告诉编译器如何定义函数。
- 需要交换int数据时,编译器将按模板模式创建这样的函数,并用int 代替 AnyType。(实例化/隐式实例化)
- 同样,需要交换double 时,编译器将按模板模式创建这样的函数,并用double代替 AnyType。
- C++98 添加关键字typename之前,C++使用关键字class来创建模板。
template <class AnyType>
void swap(AnyType &a, AnyType &b)
{
AnyType tmp;
tmp = a;
a = b;
b = tmp;
}
模板显示实例化声明
建立模板之后,发现我其实只需要用short 和 int 两种,那我直接告诉编译器我需要这两个。 隐式实例化:声明模板,编译器自动创建 显示实例化: 声明模板,告诉编译器通过模板创建我需要的类型。 区别:隐式实例化,编译器发现函数需要使用,才创建实例。 显示实例化,不管有没有使用都创建实例。
template <typename AnyType>
void swap(AnyType &a, AnyType &b)
{
AnyType tmp;
tmp = a;
a = b;
b = tmp;
}
template void swap<int>(int, int);
(三)重载的模板
简单示例笔记
template <typename AnyType>
void swap(AnyType &a, AnyType &b)
{
AnyType tmp;
tmp = a;
a = b;
b = tmp;
}
template <typename AnyType>
void swap(AnyType a[], AnyType b[], int n)
{
AnyType tmp;
int i;
for(i = 0; i< n; i++){
tmp = a[i];
a[i] = b[i];
b[i] = tmp;
}
}
(四)模板具体化
理解完重载和模板函数之后,如果还不晕的话。 那么,继续。
假设有个需求: 众所周知,结构体是可以直接赋值的。 但是我只想交换结构体里面salary, floor 两个变量值 因此我希望调用swap如果传入的是job(指定类型)的话,执行指定处理。
struct job
{
char name;
double salary;
int floor;
};
template <> void swap<job>(job &j1, job &j2)
{
double salary;
salary = j1.salary;
j1.salary = j2.salary;
j2.salary = j1.salary;
int floor;
floor = j1.floor;
j1.floor = j2.floor;
j2.floor = j1.floor;
}
(五)要点总结
- 重载:同名函数,根据特征标(参数数目/参数类型)不同而匹配不同的处理函数。
- const限定符、&引用符,不计入特征标。
- 模板:定义函数模板,编译器生成对应不同数据类型的相同操作函数实例。
- 隐式实例化:定义模板,编译器自动创建
- 显示实例化: 定义模板,手动告诉编译器该创建哪个类型。
- 可重载模板 = 重载 + 模板
- 具体化:不使用模板生成函数,定义一个指定具体类型的全新函数。
- 区分实例化和具体化
实例化:使用模板,只需要声明,编译器就会生成指定类型的实例。 具体化:不使用模板,需要再编写函数定义。
补充:
- 以上均为同名函数,编译器会自动匹配,匹配规则十分细密,后续学习了以后再整理。
- 也可以编写代码,引导编译器匹配我们希望它匹配的函数。
template<typename T>
T lesser(T &a, T &b)
{
return a < b? a : b;
}
int lesser( int a, int b)
{
a = a< 0? -a : a;
b = b< 0? -b : b;
return a < b? a : b;
}
int m = 20;
int n = -30;
double x = 15.5;
double y = 25.9;
函数调用方式 | 编译器寻找匹配函数 |
---|
lesser(m , n) | int类型,匹配 int lesser( int a, int b) | lesser(x , y) | double类型,匹配模板,并且生成double实例 | lesser<>(m , n) | int类型,但调用处增加<> ,匹配模板,并且生成int实例 | lesser(m , n) | int类型,但调用处增加<int> ,匹配模板,并且生成int实例 |
|