C++——模板小结
模板
模板(Template)指C++程序设计设计语言中采用类型作为参数的程序设计,支持通用程序设计。C++ 的标准库提供许多有用的函数大多结合了模板的观念,如STL以及IO Stream。模板是C++支持参数化多态的工具,使用模板可以使用户为类或者函数声明一种一般模式,使得类中的某些数据成员或者成员函数的参数、返回值取得任意类型。
模板是一种对类型进行参数化的工具;
通常有两种形式:函数模板和类模板;
(一)函数模板
1 template <class 形参名,class 形参名,......> 2 返回类型 函数名(参数列表) 3 4 { 5 6 函数体 7 8 }
下面我们以一个交换变量的程序来展示函数模板的作用
1 #include<iostream> 2 using namespace std; 3 void Swap(int &a,int &b) 4 { 5 int t = a; 6 a = b; 7 b = t; 8 } 9 int main(int argc, char const *argv[]) 10 { 11 int a = 23,b = 45; 12 char c = 'a',d = 'b'; 13 Swap(a,b); 14 //Swap(c,d); //报错,因为我们只能交换int类型的两个变量 15 cout<<a<<" "<<b<<endl; 16 return 0; 17 }
这是我们普通的交换变量的程序,但它有局限,我们只能交换一种我们规定好的类型变量,换一种就不能交换了,当然我们也可以用重载的办法,但重载就意味着我们要写出包括所有情况的函数,这样工作量很大,有没有简单一点的办法呢?这时候我们就要用到模板了,用一个函数来实现所有的。
1 #include<iostream> 2 using namespace std; 3 void Swap(int &a,int &b) 4 { 5 int t = a; 6 a = b; 7 b = t; 8 } 9 template<class T> 10 void Swap(T &a,T &b) 11 { 12 T temp = a; 13 a = b; 14 b = temp; 15 } 16 int main(int argc, char const *argv[]) 17 { 18 int a = 23,b = 45; 19 char c = 'a',d = 'b'; 20 Swap(a,b); 21 cout<<a<<" "<<b<<endl; 22 Swap(c,d); 23 cout<<c<<" "<<d<<endl; 24 return 0; 25 }
看结果,我们可以看到,不仅是int型的,char型的也可以交换,当然我们还可以交换double型的,但大家注意,因为我们前面有写普通的交换函数,所有会默认调用普通的函数,原因是虽然编译器会帮我们做类型转换(T变成int),但能不转的时候编译器不会转,大家可以试试,那如果我们要指定用我们写的模板函数怎么办呢?
1 Swap<>(int a,int b)//这样就会指定调用模板函数
1 #include<iostream> 2 using namespace std; 3 void Swap(int &a,int &b) 4 { 5 cout<<"普通函数"<<endl; 6 int t = a; 7 a = b; 8 b = t; 9 } 10 template<class T> 11 void Swap(T &a,T &b) 12 { 13 cout<<"模板函数"<<endl; 14 T temp = a; 15 a = b; 16 b = temp; 17 } 18 int main(int argc, char const *argv[]) 19 { 20 int a = 23,b = 45; 21 char c = 'a',d = 'b'; 22 Swap(a,b); 23 cout<<a<<" "<<b<<endl; 24 Swap(c,d); 25 cout<<c<<" "<<d<<endl; 26 return 0; 27 }
看上面的运行结果我们可以发现,编译器调用的是普通函数,当我们指定要调用模板函数时
1 #include<iostream> 2 using namespace std; 3 void Swap(int &a,int &b) 4 { 5 cout<<"普通函数"<<endl; 6 int t = a; 7 a = b; 8 b = t; 9 } 10 template<class T> 11 void Swap(T &a,T &b) 12 { 13 cout<<"模板函数"<<endl; 14 T temp = a; 15 a = b; 16 b = temp; 17 } 18 int main(int argc, char const *argv[]) 19 { 20 int a = 23,b = 45; 21 char c = 'a',d = 'b'; 22 Swap<>(a,b); 23 cout<<a<<" "<<b<<endl; 24 Swap(c,d); 25 cout<<c<<" "<<d<<endl; 26 return 0; 27 }
我们可以看到,在Swap后面加<>之后就会指定调用模板函数。或者Swap<int>(a,b),这样编译器就不用去进行类型推导了。
(二)类模板
模板类格式如下:
1 template<class 形参名,class 形参名,…> 2 class 类名{ ... };
类模板的话,我们以一个实例来引出:
需求:
写一个各边长度的数组类Array
用于存放各种元素,个数未知
设计:
内部动态申请一个buffer
capacity:表示buffer的大小
size:表示buffer已经存放元素的个数
接口:
Size():
Capacity():最大容量
Clear():清空,使size为0,capacity不变
PushBack():添加一个元素。
问题:这个问题我们首先以double型的为例,代码如下:
1 #include<iostream> 2 using namespace std; 3 #include<string.h> 4 class Myarry 5 { 6 public: 7 Myarry(int capacity = 5) //初始的数组内存 8 { 9 m_buffer = new double[capacity]; //开辟一个大小为5的内存空间 10 m_size = 0; //初始size为0 11 m_capacity = capacity; //初始的大小为5 12 } 13 int Size() //已存元素的个数 14 { 15 return m_size; 16 } 17 int Capacity() //开辟的内存大小 18 { 19 return m_capacity; 20 } 21 void Clear() //清空数组 22 { 23 delete []m_buffer; //删除数组 24 m_size = 0; //元素个数清0 25 cout<<"Arrary cleared!"<<endl; 26 } 27 void PushBack(double val) //添加元素 28 { 29 if(m_size >= m_capacity) //如果数组已存的元素个数大于开辟的空间,就开辟内存 30 { 31 cout<<"Expand the Memory!"<<endl; 32 m_capacity = m_capacity + 2; //扩大两个元素的大小 33 double *buffer = new double[m_capacity]; //开辟一个新的空间 34 memcpy(buffer,m_buffer,m_capacity * sizeof(double)); //把旧的数组的元素拷贝一下 35 delete []m_buffer; //删除旧的数组 36 m_buffer = buffer; //让数组指针指向新数组 37 } 38 m_buffer[m_size++] = val; //把val的值添加到数组中,数组已存长度+1 39 } 40 private: 41 double *m_buffer; //数组指针 42 int m_capacity; //数组内存大小 43 int m_size; //数组已存元素个数 44 }; 45 int main(int argc, char const *argv[]) 46 { 47 Myarry a; //实例化出一个对象 48 cout<<"Capacity:"<<a.Capacity()<<endl; //输出初始数组的大小和已存元素个数 49 cout<<"Size:"<<a.Size()<<endl; 50 a.PushBack(2.3); //添加五个元素 51 a.PushBack(1.1); 52 a.PushBack(3.4); 53 a.PushBack(2.1); 54 a.PushBack(3.7); 55 cout<<"Capacity:"<<a.Capacity()<<endl; //输出现在数组的大小和已存元素个数 56 cout<<"Size:"<<a.Size()<<endl; 57 a.PushBack(4.5); //再添加一个元素,此时已经大于数组的内存大小,需要扩增内存 58 cout<<"Capacity:"<<a.Capacity()<<endl; //输出现在数组的大小和已存元素个数 59 cout<<"Size:"<<a.Size()<<endl; 60 a.Clear(); //清空数组 61 cout<<"Capacity:"<<a.Capacity()<<endl; //输出现在数组的大小和已存元素个数 62 cout<<"Size:"<<a.Size()<<endl; 63 return 0; 64 }
上面是我们用普通的类实现,但需求是要实现各种类型的,那我们怎么解决呢?这时候就要用到模板类了。
1 #include<iostream> 2 using namespace std; 3 #include<string.h> 4 template <class T> 5 class Myarry 6 { 7 public: 8 Myarry(int capacity = 5) //初始的数组内存 9 { 10 m_buffer = new T[capacity]; //开辟一个大小为5的内存空间 11 m_size = 0; //初始size为0 12 m_capacity = capacity; //初始的大小为5 13 } 14 int Size() //已存元素的个数 15 { 16 return m_size; 17 } 18 int Capacity() //开辟的内存大小 19 { 20 return m_capacity; 21 } 22 void Clear() //清空数组 23 { 24 delete []m_buffer; //删除数组 25 m_size = 0; //元素个数清0 26 cout<<"Arrary cleared!"<<endl; 27 } 28 void PushBack(T val) //添加元素 29 { 30 if(m_size >= m_capacity) //如果数组已存的元素个数大于开辟的空间,就开辟内存 31 { 32 cout<<"Expand the Memory!"<<endl; 33 m_capacity = m_capacity + 2; //扩大两个元素的大小 34 T *buffer = new T[m_capacity]; //开辟一个新的空间 35 memcpy(buffer,m_buffer,m_capacity * sizeof(T)); //把旧的数组的元素拷贝一下 36 delete []m_buffer; //删除旧的数组 37 m_buffer = buffer; //让数组指针指向新数组 38 } 39 m_buffer[m_size++] = val; //把val的值添加到数组中,数组已存长度+1 40 } 41 void printf() //输出数组中的元素 42 { 43 T *ptr = m_buffer; //定义一个T类型的指针和下面的变量n,用来遍历数组 44 int n = m_size; 45 for(int i = 0; i < n;i++) 46 { 47 cout<<ptr[i]<<" "; 48 } 49 cout<<endl; 50 } 51 private: 52 T *m_buffer; //数组指针 53 int m_capacity; //数组内存大小 54 int m_size; //数组已存元素个数 55 }; 56 int main(int argc, char const *argv[]) 57 { 58 Myarry<char> b; 59 b.PushBack('a'); //添加五个元素 60 b.PushBack('b'); 61 b.PushBack('z'); 62 b.PushBack('j'); 63 b.PushBack('f'); 64 cout<<"Capacity:"<<b.Capacity()<<endl; 65 cout<<"Size:"<<b.Size()<<endl; 66 b.PushBack('z'); 67 b.printf(); 68 b.Clear(); 69 cout<<"Capacity:"<<b.Capacity()<<endl; 70 cout<<"Size:"<<b.Size()<<endl; 71 return 0; 72 }