一、函数模板
定义形式:
template <typename type1,typename type2,...> return-type func-name(parameter list) { // 函数的主体 }
说明:
- type1和type2…是占位符,用来泛指任意类型,如T,K…。
- template和typename是关键字;
函数模板的调用:
- 显示类型调用
- 自动类型推导
例子:
#include <iostream> using namespace std; // 开始泛型编程 template <typename T> void myswap(T &a,T &b){ T c = 0; c = a; a = b; b = c; } void testInt(){ int a = 1; int b = 2; // 显示调用函数模板 myswap<int>(a,b); cout << "a = " <<a<< " b = "<<b<<endl; // 自动类型退导 myswap(a,b); cout << "a = " <<a<< " b = "<<b<<endl; } void testChar(){ char a = 'a'; char b = 'b'; // 显示调用函数模板 myswap<char>(a,b); cout << "a = " <<a<< " b = "<<b<<endl; // 自动类型退导 myswap(a,b); cout << "a = " <<a<< " b = "<<b<<endl; } int main01(){ testInt(); testChar(); }
函数模板强化:对字符数组或int数组进行排序
// 对字符数组 int数组进行排序 #include <iostream> using namespace std; template <typename T,typename K> void mySort(T *arr,K size){ T tmp; for(int i=0;i<size;i++){ for(int j = i+1;j<size;j++){ if(arr[i]>arr[j]){ tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; } } } } template <typename T,typename K> void printArr(T *arr,K size){ for(int i=0;i<size;i++){ cout << arr[i] << " "; } cout << endl; } void testIntArr(){ int arr[] = {12,34,5,7,91,23,45,11,8}; int size = sizeof(arr)/sizeof(*arr); cout << "排序前..." <<endl; printArr(arr,size); mySort<int,int>(arr,size); cout << "排序后..." <<endl; printArr(arr,size); } void testCharArr(){ char arr[] = {'b','a','e','c','d','g','h'}; int size = sizeof(arr)/sizeof(*arr); cout << "排序前..." <<endl; printArr(arr,size); mySort<char,int>(arr,size); cout << "排序后..." <<endl; printArr(arr,size); } int main02(){ testIntArr(); testCharArr(); }
函数模板遇上函数重载
函数模板函数的调用(本质:类型参数化):将严格的按照类型进行匹配,不会进行自动类型转换;
普通函数的调用可能会进行隐式类型转换。
例子:
#include <iostream> using namespace std; template <typename T> void func(T a,T b){ cout << "模板函数调用..." <<endl; } void func(int a,char b){ cout << "普通函数调用..." <<endl; } int main(){ int a = 10; char b = 'b'; func(a,b); // 调用普通函数 func(b,a); // 调用普通函数 func(a,a); // 调用模板函数 }
函数模板和普通函数在一起的调用规则:
- 函数模板不允许自动类型转化;
普通函数能够进行自动类型转化(如果精确匹配到);
函数模板可以像普通函数一样被重载;
- c++编译器优先考虑普通函数;
- 如果模板函数可以产生一个更好的匹配,那么选择模板函数;
- 可以通过空模板实参列表的语法限定编译器只通过模板匹配。
例子:
#include <iostream> using namespace std; int Max(int a,int b){ cout << "普通函数--Max(int a,int b)..." <<endl; return a > b? a:b; } template <typename T> T Max(T a,T b){ cout << "模板函数--T Max(T a,T b)..." <<endl; return a > b? a:b; } template <typename T,typename K> T Max(T a,K b){ cout << "模板函数--T Max(T a,K b)..." <<endl; return a > b? a:b; } template <typename T> T Max(T a,T b,T c){ cout << "模板函数--int Max(T a,T b,T c)..." <<endl; return a > b? (a>c?a:c):(b>c?b:c); } int main(){ int a = 10; int b=7; char c = 'c'; int max = Max(a,b); // 调用普通函数 cout << "max = " << max <<endl; max = Max(a,c); // 调用模板函数,产生了更好的匹配 cout << "max = " << max <<endl; c = Max<char>('a','b'); // 显示调用函数模板 cout << "c = " << c <<endl; c = Max<char>('a','b','c'); // 显示调用函数模板--重载 cout << "c = " << c <<endl; }
c++编译器模板机制剖析:
- 原理:c++编译器在我们调用的时候添加了额外的函数(相应类型的函数);
- 编译器并不是把模板函数处理成能够处理任意类型的函数;
- 编译器从模板函数通过具体类型产生不同的函数;
- 编译器会对模板函数进行二次编译:
- 在声明的地方对模板代码本身进行编译;
- 在调用的地方对参替数换后的代码进行编译。
二、 类模板
定义形式
template <typename type1,typename T2,...> class class-name { . . . }
类模板做函数参数(需要类型具体化)
例子:
#include <iostream> using namespace std; template <typename T> // 模板类,本身是一个抽象类 class TemplateC{ private: T a; public: TemplateC (T a){ this->a = a; } void print(){ cout << "a = " << a <<endl;; } }; void useTemplateC(TemplateC<int> &c){ c.print(); } int main(){ TemplateC<int> c1(10),c2(20),c3(30); useTemplateC(c1); useTemplateC(c2); useTemplateC(c3); return 0; }
类模板派生子类(普通类和类模板)
代码:
#include <iostream> using namespace std; template <typename T> // 模板类,本身是一个抽象类 class TemplateC{ protected: T a; public: TemplateC (T a){ this->a = a; } void print(){ cout << "a = " << a <<endl;; } }; // 必须给定泛型类型,因为c++编译器要知道如何分配内存 class B : public TemplateC<int>{ private: int b; public: B(int a,int b) : TemplateC(a){ this->b = b; } void printB(){ cout << "a = " <<a<<endl; cout << "b = "<<b<<endl; } }; int main(){ B b(10,20); b.printB(); }
类模板的几种写法
所有类成员函数都写在类内部(推荐)
代码:
#include <iostream> using namespace std; template <typename T,typename K> class Complex{ // 友元函数的函数体也可以写在类外部 friend ostream & operator<<(ostream &out,Complex &c){ out << c.a << "+" << c.b << "j" <<endl; return out; } public: Complex(T a,K b){ this->a = a; this->b = b; } void print(){ cout << a << "+" << b << "j" <<endl; } // 重载加法运算符 Complex operator+(Complex c2){ Complex tmp(this->a+c2.a,this->b+c2.b); return tmp; } private: T a; K b; }; int main(){ Complex<int,int> c1(1,2),c2(1,3); c1.print(); Complex<int,int> c3 = c1 + c2; // 需要重载<<操作符,重载<< >>只能用友元函数 cout << c3 <<endl; }
所有类成员函数都写在类外部
#include <iostream> using namespace std; template <typename T,typename K> class Complex{ // 友元函数的函数体也可以写在类外部 friend ostream & operator<<(ostream &out,Complex<T,K> &c); public: Complex(T a,K b); void print(); // 重载加法运算符 Complex operator+(Complex c2); private: T a; K b; }; template <typename T,typename K> Complex<T,K>::Complex(T a,K b){ this->a = a; this->b = b; } template <typename T,typename K> void Complex<T,K>::print(){ cout << a << "+" << b << "j" <<endl; } template <typename T,typename K> Complex<T,K> Complex<T,K>::operator+(Complex c2){ Complex tmp(this->a+c2.a,this->b+c2.b); return tmp; } template <typename T,typename K> ostream & operator<<(ostream &out,Complex<T,K> &c){ out << c.a << "+" << c.b << "j" <<endl; return out; } int main(){ Complex<int,int> c1(1,2),c2(1,3); c1.print(); Complex<int,int> c3 = c1 + c2; // 需要重载<<操作符,重载<< >>只能用友元函数 cout << c3 <<endl; }
所有类成员函数都写在类内部(类的声明(h)和实现(cpp)分开)(推荐)
Complex.h
#pragma once // 保证该文件只会被包含一次 #include <iostream> using namespace std; template <typename T,typename K> class Complex{ // 友元函数的函数体也可以写在类外部 friend ostream & operator<<(ostream &out,Complex<T,K> &c); public: Complex(T a,K b); void print(); // 重载加法运算符 Complex operator+(Complex c2); private: T a; K b; };
Complex.cpp
#include "Complex.h" template <typename T,typename K> Complex<T,K>::Complex(T a,K b){ this->a = a; this->b = b; } template <typename T,typename K> void Complex<T,K>::print(){ cout << a << "+" << b << "j" <<endl; } template <typename T,typename K> Complex<T,K> Complex<T,K>::operator+(Complex c2){ Complex tmp(this->a+c2.a,this->b+c2.b); return tmp; } template <typename T,typename K> ostream & operator<<(ostream &out,Complex<T,K> &c){ out << c.a << "+" << c.b << "j" <<endl; return out; }
测试代码
#include <iostream> #include "Complex.h" using namespace std; int main(){ Complex<int,int> c1(1,2),c2(1,3); c1.print(); Complex<int,int> c3 = c1 + c2; // 需要重载<<操作符,重载<< >>只能用友元函数 cout << c3 <<endl; }