模板(Templates)在编程中是一个非常重要的概念,特别是在C++中。它是一种泛型编程的技术,允许程序员定义处理未知数据类型的函数或类。通过使用模板,程序员可以编写处理任意数据类型的代码,而不需要对每种数据类型都重写代码。
模板可以分为函数模板和类模板两种类型。函数模板允许程序员定义一个可以处理不同数据类型的函数,而类模板则允许程序员定义一个可以处理不同数据类型的类。
函数模板的一般形式如下:
template <typename T>
void functionName(T param) {
// 函数体
}
在这个例子中,T
是一个类型参数,表示函数可以接受任何类型的参数。在函数体中,T
可以被用作任何类型,包括变量声明、返回类型等。
类模板的一般形式如下:
template <typename T>
class ClassName {
// 类体
};
在这个例子中,T
是一个类型参数,表示类可以包含任何类型的成员变量或函数。在类体中,T
可以被用作任何类型,包括变量声明、返回类型等。
通过使用模板,程序员可以编写更加通用和可复用的代码,提高代码的利用率和可维护性。同时,模板还可以提高代码的安全性,因为类型检查在编译时进行,而不是在运行时进行。
接上一章继续:第十五章丨C++编程宝典:快速上手、深入进阶、挑战高级技巧,助你成为编程达人 – 菜鸟资源 (xiciw.com)
9. 模板(Templates)
泛型(Generic Programming)即是指具有在多种数据类型上皆可操作的含意。泛型编程的代表作品 STL 是一种高效、泛型、可交互操作的软件组件。
泛型编程最初诞生于 C++中,目的是为了实现 C++的 STL(标准模板库)。其语言支持机制就是模板(Templates)。模板的精神其实很简单:参数化类型。换句话说,把一个原本特定于某个类型的算法或类当中的类型信息抽掉,抽出来做成模板参数 T。
9.1. 函数模板
9.1.1. 函数重载实现的泛型
#include <iostream>
using namespace std;
void swap(int &a, int& b)
{
int t = a;
a = b;
b = t;
}
void swap(double &a,double b)
{
double t = a;
a = b;
b = t;
}
int main()
{
int ia = 10; int ib = 20;
swap(ia,ib);
cout<<ia<<ib<<endl;
double da = 10, db = 20;
swap(da,db);
cout<<da<<db<<endl;
return 0;
}
9.1.2. 函数模板的引入
语法格式
template<typename/class 类型参数表>
返回类型 函数模板名(函数参数列表)
{
函数模板定义体
}
template 是语义是模板的意思,尖括号中先写关键字 typename 或是 class ,后面跟一个类型 T,此类即是虚拟的类型。至于为什么用 T,用的人多了,也就是 T 了。
9.1.3. 函数模板的实例
调用过程是这样的,先将函数模板实再化为函数,然后再发生函数调用。
#include <iostream>
using namespace std;
template <typename T>
void Swap(T& a,T &b )
{
T t = a;
a = b;
b = t;
}
int main()
{
int ia = 10; int ib = 20;
Swap(ia,ib); //Swap<int>(ia,ib);
cout<<ia<<ib<<endl;
double da = 10, db = 20;
Swap(da,db); //Swap<double>(da,db);
cout<<da<<db<<endl;
string sa ="china"; string sb = "America";
Swap(sa,sb);
cout<<sa<<sb<<endl;
return 0;
}
9.1.4. 小结
函数模板,只适用于函数的参数个数相同而类型不同,且函数体相同的情况。如果个数不同,则不能用函数模板。
9.2. 类模板
9.2.1. 引例
Stack 类
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
using namespace std;
class Stack
{
public:
Stack(int size=1024);
~Stack();
bool isEmpty();
bool isFull();
void push(int data);
int pop();
private:
int* space;
int top;
};
Stack::Stack(int size)
{
space = new int[size];
top = 0;
}
Stack::~Stack()
{
delete []space;
}
bool Stack::isEmpty()
{
return top == 0;
}
bool Stack::isFull()
{
return top == 1024;
}
void Stack::push(int data)
{
space[top++] = data;
}
int Stack::pop()
{
return space[--top];
}
int main()
{
Stack s(100);
if(!s.isFull())
s.push(10);
if(!s.isFull())
s.push(20);
if(!s.isFull())
s.push(30);
if(!s.isFull())
s.push(40);
if(!s.isFull())
s.push(50);
while(!s.isEmpty())
cout<<s.pop()<<endl;
return 0;
}
上述栈,如果想模板化,可以 push 和 pop 不同的数据类型。主要由几个因素需要把控。栈中的空间元素类型,压入元素类型,弹出元素类型,三者保持一致即可。
9.2.2. 类模板语法
9.2.2.1. 类模板定义
template<typename T>
class Stack
{
}
9.2.2.2. 类内定义成员函数
template<typename T>
class Stack
{
public:
Stack(int size)
{
space = new T[size];
top = 0;
}
}
9.2.2.3. 类外定义函数
template<typename T>
void Stack<T>::push(T data)
{
space[top++] = data;
}
9.2.2.4. 类模板实例化为模板类
类模板是类的抽象,类是类模板的实例。
Stack<double> s(100);
9.2.3. 类模板实例
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
145
#include <string.h>
using namespace std;
template<typename T>
class Stack
{
public:
Stack(int size)
{
space = new T[size];
top = 0;
}
~Stack();
bool isEmpty();
bool isFull();
void push(T data);
T pop();
private:
T* space;
int top;
};
template<typename T>
Stack<T>::~Stack()
{
delete []space;
}
template<typename T>
bool Stack<T>::isEmpty()
{
return top == 0;
}
template<typename T>
bool Stack<T>::isFull()
{
return top == 1024;
}
template<typename T>
void Stack<T>::push(T data)
{
space[top++] = data;
}
template<typename T>
T Stack<T>::pop()
{
return space[--top];
}
int main()
{
Stack<double> s(100); //Stack<string> s(100);
if(!s.isFull())
s.push(10.3);
if(!s.isFull())
s.push(20);
if(!s.isFull())
s.push(30);
if(!s.isFull())
s.push(40);
if(!s.isFull())
s.push(50);
while(!s.isEmpty())
cout<<s.pop()<<endl;
return 0;
}
9.2.4. 练习
模拟 STL 中 vector 的用法,自己实现之。
#ifndef MYVECTOR_HPP
#define MYVECTOR_HPP
#include <iostream>
using namespace std;
template <typename T> class MyVector;
template <typename T> ostream & operator<<(ostream &out, const MyVector<T> &obj);
template <typename T>
class MyVector
{
public:
MyVector(int size = 0);
MyVector(const MyVector<T> &obj);
MyVector<T> & operator=( MyVector<T> &obj);
~MyVector();
T& operator[] (int index);
int getSize();
friend ostream & operator<< <T>(ostream &out, const MyVector<T> &obj);
protected:
T *m_space;
int m_len;
};
template <typename T>
int MyVector<T>::getSize()
{
return m_len;
}
template <typename T>
ostream & operator<< (ostream &out, const MyVector<T> &obj)
{
for (int i=0; i< obj.m_len; i++)
{
out << obj.m_space[i] << " ";
}
out << endl;
return out;
}
template <typename T>
MyVector<T>::MyVector(int size)
{
m_space = new T[size];
m_len = size;
}
template <typename T>
MyVector<T>::MyVector(const MyVector & obj)
{
m_len = obj.m_len;
m_space = new T[m_len];
for (int i=0; i<m_len; i++)
{
m_space[i] = obj.m_space[i];
}
}
template <typename T>
MyVector<T>::~MyVector() //析构函数
{
if (m_space != NULL)
{
delete [] m_space;
m_space = NULL;
m_len = 0;
}
}
template <typename T>
T& MyVector<T>::operator[] (int index)
{
return m_space[index];
}
template <typename T>
MyVector<T> & MyVector<T>::operator=( MyVector<T> &obj)
{
delete[] m_space;
m_space = NULL;
m_len = 0;
m_len = obj.m_len;
m_space = new T[m_len];
for (int i=0; i<m_len; i++)
{
m_space[i] = obj[i];
}
return *this;
}
#endif // MYVECTOR_HPP
测试代码:
#include <iostream>
#include "myvector.hpp"
using namespace std;
int main()
{
MyVector<int> a(10);
for(int i=0; i<10; i++)
{
a[i] = i+100;
}
MyVector<int> b(a);
cout<<b[2]<<b[1]<<endl;
MyVector<int> c;
c = a;
cout<<b[2]<<b[1]<<endl;
cout<<a;
return 0;
}
注:
《C++编程思想》第 15 章(第 300 页):
模板定义很特殊。由 template<…> 处理的任何东西都意味着编译器在当时不为它分配存储空间,它一直处于等待状态直到被一个模板实例告知。在编译器和连接器的某一处,有一机制能去掉指定模板的多重定义。所以为了容易使用,几乎总是在头文件中放置全部的模板声明和定义。