手表商城网站建设,工商注册官方网站,装修公司怎么拉客户,百度一下下载安装本专栏内容为#xff1a;C学习专栏#xff0c;分为初阶和进阶两部分。 通过本专栏的深入学习#xff0c;你可以了解并掌握C。 #x1f493;博主csdn个人主页#xff1a;小小unicorn ⏩专栏分类#xff1a;C #x1f69a;代码仓库#xff1a;小小unicorn的代码仓库… 本专栏内容为C学习专栏分为初阶和进阶两部分。 通过本专栏的深入学习你可以了解并掌握C。 博主csdn个人主页小小unicorn ⏩专栏分类C 代码仓库小小unicorn的代码仓库 关注我带你学习编程知识 C模版初阶 泛型编程函数模版函数模版的概念函数模版的格式函数模版的原理函数模版的实例化隐式实例化显式实例化 函数模版的匹配原则 类摸版类模版的定义方式类模版的实例化 泛型编程
回顾一下如果让你编写一个函数用于对两个数的交换。在C语言中我们会用到以下这样
// 交换两个整型
void Swapi(int* p1, int* p2)
{int tmp *p1;*p1 *p2;*p2 tmp;
}
// 交换两个双精度浮点型
void Swapd(double* p1, double* p2)
{double tmp *p1;*p1 *p2;*p2 tmp;
}但是C语言不支持函数重载所以用于交换不同类型变量的函数的函数名是不能相同的并且传参形式必须是址传递不能是值传递。 而在学习了C的函数重载和引用后我们又会用如下方法实现两个数的交换
// 交换两个整型
void Swap(int x, int y)
{int tmp x;x y;y tmp;
}
// 交换两个双精度浮点型
void Swap(double x, double y)
{double tmp x;x y;y tmp;
}C的函数重载使得用于交换不同类型变量的函数可以拥有相同的函数名并且传参使用引用传参使得代码看起来不那么晦涩难懂。
使用函数重载虽然可以实现但是有一下几个不好的地方
重载的函数仅仅是类型不同代码复用率比较低只要有新类型出现时就需要用户自己增加对应的函 数代码的可维护性比较低一个出错可能所有的重载均出错
那我们能否告诉编译器一个模子让编译器根据不同的类型利用该模子来生成相应的代码呢
跟浇筑原理一样如果在C中也能够存在这样一个模具通过给这个模具中填充不同材料(类型)来获得不同材料的铸件 (即生成具体类型的代码那将会节省许多头发。 巧的是前人早已将树栽好我们只需在此乘凉。
泛型编程编写与类型无关的通用代码是代码复用的一种手段。模板是泛型编程的基础。
函数模版
函数模版的概念
函数模板代表了一个函数家族该函数模板与类型无关在使用时被参数化根据实参类型产生函数的特定类型版本。
函数模版的格式
templatetypename T1, typename T2,......,typename Tn
返回值类型 函数名(参数列表){}举个例子
templatetypename T
void Swap(T x, T y)
{T tmp x;x y;y tmp;
}注意typename是用来定义模板参数关键字也可以使用class(但是切记不能使用struct代替class)
函数模版的原理
那么函数模板的底层原理是什么呢大家都知道瓦特改良蒸汽机人类开始了工业革命解放了生产力。机器生产淘汰掉了很多手工产品。其本质就是将重复的工作交给了机器去完成。 有人给出了论调懒人创造世界 懒不是傻懒如果你想少干就要想出懒的方法。要懒出风格懒出境界。
函数模板是一个蓝图它本身并不是函数是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器 在编译器编译阶段对于模板函数的使用编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如当用double类型使用函数模板时编译器通过对实参类型的推演将T确定为double类型然后产生一份专门处理double类型的代码对于字符类型也是如此。编译器终究是我一个人承担了所有
函数模版的实例化
用不同类型的参数使用函数模板时称为函数模板的实例化。模板参数实例化分为隐式实例化和显式实例化
隐式实例化
隐式实例化让编译器根据实参推演模板参数的实际类型: 例如
templatetypename T
T Add(const T x, const T y)
{return x y;
}
int main()
{int a 10, b 20;int c Add(a, b); //编译器根据实参a和b推演出模板参数为int类型cout c endl;return 0;
}特别注意使用模板时编译器一般不会进行类型转换操作。所以以下代码将不能通过编译
int a 10;
double b 1.1;
int c Add(a, b);因为在编译期间编译器根据实参推演模板参数的实际类型时根据实参a将T推演为int根据实参b将T推演为double但是模板参数列表中只有一个T编译器无法确定此处应该将T确定为int还是double。 此时我们有两种处理方式第一种就是我们在传参时将b强制转换为int类型第二种就是使用下面说到的显示实例化。
显式实例化
显示实例化在函数名后的中指定模板参数的实际类型 例如
templatetypename T
T Add(const T x, const T y)
{return x y;
}
int main()
{int a 10;double b 1.1;int c Addint(a, b); //指定模板参数的实际类型为intcout c endl;return 0;
}注意使用显示实例化时如果传入的参数类型与模板参数类型不匹配编译器会尝试进行隐式类型转换如果无法转换成功则编译器将会报错。
函数模版的匹配原则
一、一个非模板函数可以和一个同名的函数模板同时存在而且该函数模板还可以被实例化为这个非模板函数 例如
//专门用于int类型加法的非模板函数
int Add(const int x, const int y)
{return x y;
}
//通用类型加法的函数模板
templatetypename T
T Add(const T x, const T y)
{return x y;
}
int main()
{int a 10, b 20;int c Add(a, b); //调用非模板函数编译器不需要实例化int d Addint(a, b); //调用编译器实例化的Add函数return 0;
}二、对于非模板函数和同名的函数模板如果其他条件都相同在调用时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数那么选择模板
//专门用于int类型加法的非模板函数
int Add(const int x, const int y)
{return x y;
}
//通用类型加法的函数模板
templatetypename T1, typename T2
T1 Add(const T1 x, const T2 y)
{return x y;
}
int main()
{int a Add(10, 20); //与非模板函数完全匹配不需要函数模板实例化int b Add(2.2, 2); //函数模板可以生成更加匹配的版本编译器会根据实参生成更加匹配的Add函数return 0;
}三、模板函数不允许自动类型转换但普通函数可以进行自动类型转换
templatetypename T
T Add(const T x, const T y)
{return x y;
}
int main()
{int a Add(2, 2.2); //模板函数不允许自动类型转换不能通过编译return 0;
}因为模板函数不允许自动类型转换所以不会将2自动转换为2.0或是将2.2自动转换为2。
类摸版
类模版的定义方式
templateclass T1, class T2, ..., class Tn
class 类模板名
{
// 类内成员定义
};例如
templateclass T
class Score
{
public:void Print(){cout 数学: _Math endl;cout 语文: _Chinese endl;cout 英语: _English endl;}
private:T _Math;T _Chinese;T _English;
};注意类模板中的成员函数若是放在类外定义时需要加模板参数列表。
templateclass T
class Score
{
public:void Print();
private:T _Math;T _Chinese;T _English;
};
//类模板中的成员函数在类外定义需要加模板参数列表
templateclass T
void ScoreT::Print()
{cout 数学: _Math endl;cout 语文: _Chinese endl;cout 英语: _English endl;
}除此之外类模板不支持分离编译即声明在xxx.h文件中而定义却在xxx.cpp文件中。
类模版的实例化
类模板实例化与函数模板实例化不同类模板实例化需要在类模板名字后面根然后将实例化的类型放在中即可。 //Score不是真正的类Scoreint和Scoredouble才是真正的类Scoreint s1;Scoredouble s2;注意类模板名字不是真正的类而实例化的结果才是真正的类。