c/c++|c++泛型编程——模板


文章目录

  • 泛型编程
  • 函数模板
    • 函数模板的概念
    • 函数模板格式
    • 函数模板的原理
    • 函数模板实例化
      • 隐式实例化
      • 显示实例化
    • 模板参数匹配原则
  • 类模板
    • 类模板的定义
    • 类模板实例化
    • 类模板使用注意事项

泛型编程
在生活中我们有各种模板,PPT模板,编辑模板,工厂里浇筑的模板等等,这些模板是可以重复利用,比如我们可以用一个PPT模板编辑不同的文字内容。而C++中的模板和这些也有异曲同工之妙的
c/c++|c++泛型编程——模板
文章图片

【c/c++|c++泛型编程——模板】比如我们要实现不同类型的交换函数,按照常规思路,每有一种类型就需要重载对应类型的交换函数
#include using namespace std; //这里我们只写三种类型的 void Swap(char& a, char& b) { char temp = a; a = b; b = temp; }void Swap(int& a, int& b) { int temp = a; a = b; b = temp; } void Swap(double& a, double& b) { double temp = a; a = b; b = temp; }int main() { char c1 = '0', c2 = '9'; Swap(c1, c2); int i1 = 10, i2 = 20; Swap(i1, i2); double d1 = 2.0, d2 = 5.0; Swap(d1, d2); return 0; }

可以发现三个Swap函数有多处冗余,且每增加一个新的类型就是增加一个对应的函数,代码复用性低,且对自定义类型会更加麻烦。 C++根据这样的问题提出了模板,这就是C++的泛型编程,编写与类型无关的通用代码,编译器根据不同的类型,生成不同类型的代码,是代码复用的一种手段。
模板是泛型编程的基础也是精华。模板分函数模板和类模板
c/c++|c++泛型编程——模板
文章图片

函数模板 函数模板的概念
函数模板与类型无关,在使用时被参数化,是根据实参的类型产生函数的特定类型的模板
函数模板格式
template< class 形参名,class 形参名,......> 返回类型 函数名(参数列表) { 函数体 }

#include using namespace std; //template是用来声明模板的 //class是定义模板的关键字,也可以使用typename,但typename与class不等价 template void Swap(T& x1, T& x2) { T tmp = x1; x1 = x2; x2 = tmp; } int main() { char c1 = '0', c2 = '9'; Swap(c1, c2); int i1 = 10, i2 = 20; Swap(i1, i2); double d1 = 2.0, d2 = 5.0; Swap(d1, d2); return 0; }

函数模板的原理
函数模板是一个蓝图,它本身并不是函数,编译器需要根据传入的实参类型实例化对应类型的函数,第一个swap函数的实参是char类型,编译器就会将T变为对应的char,后面的int,double也是如此,所以还是会生成我们之前写的三个重载函数,只是将我们需要重复做的事交给编译器完成了
通过汇编代码发现,每次都会call不同的函数,所以编译器还是会生成不同类型的函数,只不过不需要我们完成了
c/c++|c++泛型编程——模板
文章图片
c/c++|c++泛型编程——模板
文章图片

函数模板实例化 函数模板实例化:根据实参推导出对应的类型,实例化出对应的函数
分为隐式实例化和显示实例化,上面我们实现的就是隐式实例化
隐式实例化
隐式实例化:编译器根据实参自动推导模板参数的实际类型
#include using namespace std; template //模板参数列表——参数类型 T Add(const T& x1, const T& x2) //函数参数列表——参数对象 { return x1 + x2; }int main() { int a = 5, b = 6; double c = 11.1, d = 12.2; cout << Add(a, b) << endl; //根据实参推出T是int cout << Add(c, d) << endl; //根据实参推出T是double //这里编译器就会报错,因为a与c的类型不一致,编译器无法确定T是int还是double cout << Add(a, c) << endl; return 0; }

c/c++|c++泛型编程——模板
文章图片

想要编译通过有三种方式:
  • 强制类型转换
cout << Add((double)a, c) << endl;

  • 设置两个T
template T1 Add(const T1& x1, const T2& x2)//返回类型就只能设置一个了 { return x1 + x2; }

  • 显示实例化
显示实例化
在函数名后的<>中指定模板参数的实际类型
#include using namespace std; template //模板参数列表——参数类型 T Add(const T& x1, const T& x2) //函数参数列表——参数对象 { return x1 + x2; }int main() { int a = 5, b = 6; double c = 11.1, d = 12.2; cout << Add(a, b) << endl; //根据实参推出T是int cout << Add(c, d) << endl; //根据实参推出T是double cout << Add(a, c) << endl; //直接将T设为double return 0; }

模板参数匹配原则 如果非模板函数与同名的模板函数同时存在,且调用时条件都相同,那么调用时会优先调用非模板函数
c/c++|c++泛型编程——模板
文章图片
如果模板能产生一个更好的匹配函数,那么调用时会优先选择模板
c/c++|c++泛型编程——模板
文章图片

类模板
需要注意的是,类模板是一个模板,模板类是由类模板实例化的具体类
类模板的定义
template class 类模板名 { // 类内成员定义 };

类模板实例化
这里我们简单实现一个栈的类模板
#include using namespace std; //对普通的类而言,类名就是类型 //而对类模板而言,stack是类名,stack才是类型 template class stack { public: stack(int capacity = 4) :_capacity(capacity) { T* _p = new int[capacity]; _top = 0; _capacity = capacity; } ~stack() { delete[] _p; _top = _capacity = 0; } private: T* _p; int _top; int _capacity; }; //类模板实例化与函数模板实例化不同,需要在类模板名字后跟<>指定类型 int main() { //类模板只支持显示实例化 stack s1; //定义一个int类型的栈 stack s2; //定义一个float类型的栈 stack s3; //定义一个double类型的栈 return 0; }

类模板使用注意事项 当我们在类模板中只声明了函数,而在类外面定义时,就需要注意了,在类模板外面定义的模板函数一定要加template
template class stack { public: stack(int capacity = 4) :_capacity(capacity) { T* _p = new int[capacity]; _top = 0; _capacity = capacity; } ~stack() { delete[] _p; _top = _capacity = 0; } //在类模板中声明push函数 void push(const T& x); private: T* _p; int _top; int _capacity; }; //错误写法 //void stack::push(const T& x) //{//} //类模板中函数放在类外进行定义时,需要加模板参数列表 //如果没有template,编译器就会认为这仅仅是全局的函数,无法确定它的类型 //template是用来声明push函数是一个模板函数,用来消除除歧义的 template //栈的类型为stack,而不是stack void stack::push(const T& x) { }

今天对泛型编程的介绍到这里就结束了,希望我的文章对你有所帮助,欢迎点赞 ,评论,关注,??收藏
c/c++|c++泛型编程——模板
文章图片

    推荐阅读