【C++入门篇 - 10】:模板
文章目录
- 模板的概念
- 函数模板
- 函数模板的定义
- 类模板
- 类模板不分文件编写
- 类模板写一个栈
- 类模板中的一些特殊情况
模板的概念
- 泛型编程:指的是编写与类型无关的逻辑代码,在泛型编程中,编写的代码可以作用多种类型的对象
- 模板:就是实现代码重用的一种工具,它可以实现类型参数化,即把类型定义为参数,从而实现了代码的重用性。模板就是泛型编程的基础
- 模板分类:函数模板、类模板
函数模板
函数模板:函数模板不是一个实体函数,编译器不会对一个函数模板生成可执行代码。定义函数模板之后只是说对一个函数功能框架的一个描述。只有在调用的时候才会为其生成可执行代码
函数模板和模板函数的区别:
- 函数模板:是模板,函数的模板
- 模板函数:函数,通过函数的模板产生的一个函数
函数模板的定义
- template 关键字,表示模板
- <>表示类型参数列表,类似函数的参数列表,只不过类型参数列表中的参数为类型参数
- typename 类型名(还有一个修饰类型的关键叫 class)
#include<iostream>template<typename T>
T myAdd(T a, T b)
{return a + b;
}template<class T1>
T1 mySub(T1 a, T1 b)
{return a - b;
}void main()
{printf("%d\n", myAdd(1, 2));printf("%f\n", myAdd(1.1f, 2.3f));
}template<class T>
T mydev(T a, T b)
{return a / b;
}
观察以上代码,T mydev(T a, T b)是在main函数后,但是运行成功,体现了只有在调用的时候才会为其生成可执行代码这个特性。当把T mydev(T a, T b)函数中的分号去掉时也不会报错,只有在调用时才会报错。
隐式推导和显示推导:
- 观察以下报错,报错原因:1是整型,1.2是浮点型,这样是无法推导出模板的T类型,只能用显示推导
模板与模板可以重载,模板与普通函数可以重载:
- 模板和模板进行重载只需要函数名相同,参数列表不同(函数的参数列表或者是模板的类型参数列表)
- 调用函数,优先匹配参数完全吻合的普通函数,匹配不上,再匹配函数模板,如果还匹配不上,再匹配能否经过强转执行普通函数
不能对结构体进行相加,若想相加必须重载运算符:
(除通用性操作(排序、数组的增删改查等)需要模板,大部分情况不需要用模板)
类模板
类模板:是一个类类型的样板,不能直接使用
模板类:通过类模板生成的一个具体类型的类类型
以下是类模板的一个代码示例:
#include<iostream>
template<typename T>
class MyArr
{T *pArr;int len;int maxSize;
public:MyArr(){pArr = NULL;len = maxSize = 0;}~MyArr(){if (pArr)delete[] pArr;pArr = NULL;}void setMaxSize(int m){maxSize = m;}bool insert(int index, T const& data){if (index < 0 || index >= maxSize)return false;if (pArr == NULL)pArr = new T[maxSize];if (index > len){pArr[len++] = data;}else{for (int i = len; i > index; --i){pArr[i] = pArr[i - 1];}pArr[index] = data;len++;}return true;}
};void main()
{//函数有参数列表,可以推导类型参数,类没有参数列表,只能显示表示模板类MyArr<int> m;m.setMaxSize(10);m.insert(5, 100);m.insert(0, 101);
}
类模板不分文件编写
-
先用文件编写的方式创建一个类
-
在头文件中编写类模板
-
在.cpp文件中发现类的构造和析构是不合法的
-
原因:因为在模板编译时不会生成可执行代码,所以虽然text是类名,但是现在还没有类,只有模板。而我们在分文件编写中提到,所有不产生内存的都写在.h文件中,.cpp文件是需要写带内存的东西。所以我们可以改为以下形式:
-
在主函数中调用:
类模板写一个栈
- 创建一个stack.h头文件,并写入栈结构
#pragma oncetemplate<class T, int maxSize>
class CMyStack //栈结构
{T stackArr[maxSize];int top;
public:CMyStack();~CMyStack();void push(T const& data); //入栈void pop(); //出栈T getTop() const; //得到栈顶元素
}; template<class T, int maxSize>
T CMyStack<T, maxSize>::getTop() const
{return stackArr[top - 1];
}template<class T, int maxSize>
void CMyStack<T, maxSize>::pop()
{top--;
}template<class T, int maxSize>
void CMyStack<T, maxSize>::push(T const& data)
{stackArr[top++] = data;
}template <class T, int maxSize>
CMyStack<T, maxSize>::CMyStack()
{top = 0;
}template <class T, int maxSize>
CMyStack<T, maxSize>::~CMyStack()
{}
- 在主函数中运行
类模板中的一些特殊情况
template <class T, int val>
class CA
{T arr[val];
};template<class T1>
void fun1(CA<T1, 10> & a){} //函数是模板,且把模板参数给到函数自己的参数中的类模板template<class T1, class T2>
void fun2(CA<T1, 20> & a) //函数是模板,且模板参数分两类,一类给到自己函数体用,一类给到参数中的类模板用
{T2 val;
}
- 基类是模板,派生类也是模板
class CX
{};template<class T>
class CY : public CX
{};