目录
一、模板函数
1.概念
模板函数不是一个实在的函数,编译器不能为其生成可执行代码。定义模板函数后只是一个对函数功能框架的描述,当它具体执行时,将根据传递的实际参数决定其功能。
2.一般模板函数
以一个简单的Swap函数为例,其功能为交换两个变量的值。
void Swap(int & x, int & y)
{
int tmp = x;
x = y;
y = tmp;
}
上面的代码能实现交换功能,但是只能交换整数型即int类型,那么如果要交换浮点型或者字符型又需要另外写以下代码。
//浮点型
void Swap(double & x, double & y)
{
int tmp = x;
x = y;
y = tmp;
}
//字符型
void Swap(char & x, char & y)
{
int tmp = x;
x = y;
y = tmp;
}
为了减少我们的代码量,此时就可以使用模板函数。所谓“模板”,就是可以用一个模子做出多种类似的东西,那么模板函数就是可以用一个函数实现对多种类型值的操作。
模板函数写法如下:
template <class 类型参数1, class类型参数2, ...>
返回值类型 模板名(形参表)
{
函数体
}
还是以Swap函数为例,实现代码为:
#include <iostream>
using namespace std;
template<class T>
void Swap(T & x, T & y)
{
T tmp = x;
x = y;
y = tmp;
}
int main()
{
int n = 2, m = 8;
Swap(n, m); //编译器自动生成 void Swap (int &, int &)函数
cout<<"n="<<n<<" "<<"m="<<m<<endl;
double f = 2.8, g = 8.2;
Swap(f, g); //编译器自动生成 void Swap (double &, double &)函数
cout<<"f="<<f<<" "<<"g="<<g<<endl;
char *a = "abc",*b = "def"; //编译器自动生成 void Swap (char &, char &)函数
Swap(a,b);
cout<<"a="<<a<<" "<<"b="<<b<<endl;
return 0;
}
仅靠一个模板函数便实现了整数型、浮点型和字符型三种类型值的交换,运行截图如下:
3.特化模板函数
尽管模板函数能够对多种类型值进行操作,但是不同类型值在有些操作时需要先进行一定处理即特化,比如字符型是无法直接进行大小比较的,但我们可以比较其字符串长短,下面我们以Compare函数为例。
#include <iostream>
#include <cstring>
using namespace std;
//函数模板
template<class T>
bool Compare(T t1,T t2){
return t1==t2;
}
template<> //函数模板特化
bool Compare(char *t1,char *t2){
return strcmp(t1,t2)==0;
}
int main(int argc, char* argv[])
{
int a = 2;
int b = 2;
int c = 8;
double d = 2.8;
double e = 2.8;
double f = 8.2;
char str1[] = "abc";
char str2[] = "abc";
char str3[] = "def";
cout<<Compare(a,b)<<endl;
cout<<Compare(a,c)<<endl;
cout<<Compare(d,e)<<endl;
cout<<Compare(e,f)<<endl;
cout<<Compare(str1,str2)<<endl;
cout<<Compare(str1,str3)<<endl;
return 0;
}
如果类型值相等输出为1,反之则输出为0。运行截图如下:
可见经过特化之后的Compare函数除了能对数值进行大小比较还可以对字符型进行比较操作。需要注意的是模板函数只有全特化,没有偏特化。
二、模板类
1.概念
一个类模板(类生成类)允许用户为类定义个一种模式,使得类中的某些数据成员、默认成员函数的参数,某些成员函数的返回值,能够取任意类型(包括系统预定义的和用户自定义的)。
如果一个类中的数据成员的数据类型不能确定,或者是某个成员函数的参数或返回值的类型不能确定,就可以将此类声明为模板,它的存在不是代表一个具体的、实际的类,而是代表一类 类。
2.模板类(Queue,Stack)
模板类的作用和模板函数的相同,都是为了减少代码量方便操作,这里就不再赘述。
模板类的写法:
template <类型形参表>
class <类名>
{ //类说明体 };
template <类型形参表>
<返回类型> <类名> <类型名表>::<成员函数1>(形参表)
{ //成员函数定义体 }
template <类型形参表>
<返回类型> <类名> <类型名表>::<成员函数2>(形参表)
{ //成员函数定义体 }
…
template <类型形参表>
<返回类型> <类名> <类型名表>::<成员函数n>(形参表)
{ //成员函数定义体 }
在C++标准库(STL)中有栈和队列的类模板,因此可以直接使用。
2.1Queue
队列的特点:先进先出,只能对队头和队尾的元素进行操作。
queue的函数组成主要有:
queue.size():返回队列中元素个数;
queue.push():会将一个元素置入queue中;
queue.front():会返回queue内的第一个元素(也就是第一个被置入的元素);
queue.back():会返回queue中的最后一个元素(也就是最后被插入的元素);
queue.pop():会移除queue内的第一个元素(也就是第一个被置入的元素);
使用队列,要先包含头文件 : #include<queue>
#include <iostream>
#include <queue>
using namespace std;
int main(){
queue<int> queue;
int num;
cout<<"入队:";
for(int i = 1;i<10;i++){
queue.push(i);
cout<<queue.back()<<" ";
}
queue.empty()?cout<<endl<<"当前队列空":cout<<endl<<"当前队列非空";
cout<<endl<<"队列内元素数:"<<queue.size()<<endl;
cout<<"出队:";
while(!queue.empty())
{
cout<<queue.front()<<" ";
queue.pop();
}
queue.empty()?cout<<endl<<"当前队列空":cout<<endl<<"当前队列非空";
return 0;
}
运行截图:
2.2Stack
栈的特点:先进后出,只能对栈顶的元素进行操作。
stack的函数组成主要有:
stack.push():往栈头添加元素;
stack.pop():从栈头移除第一个元素;
stack.top():返回栈顶元素;
stack.empty():判断堆栈是否为空,栈空返回true,栈非空返回false;
stack.size():返回堆栈的大小;
使用栈之前,要先包含头文件 : #include<stack>
#include <iostream>
#include <stack>
using namespace std;
int main(){
stack<int> stack;
int num;
cout<<"入栈:";
for(int i = 1;i<10;i++){
cout<<i<<" ";
stack.push(i);
}
stack.empty()?cout<<endl<<"当前栈空":cout<<endl<<"当前栈非空";
cout<<endl<<"栈内元素数:"<<stack.size()<<endl;
cout<<"出栈:";
while(!stack.empty())
{
cout<<stack.top()<<" ";
stack.pop();
}
stack.empty()?cout<<endl<<"当前栈空":cout<<endl<<"当前栈非空";
return 0;
}
运行截图:
3.成员模板函数
成员模板函数在使用时有以下需要注意的地方:
普通类的成员函数模板不管是普通类还是模板类,成员函数都可以是函数模板,称为“成员函数模板”,但不可以是虚函数,否则编译器报错。
类模板的模板参数必须用<>指定,成员函数模板(函数模板)的模板参数可以自动推导。
类模板的成员函数(普通成员函数/函数模板)只有为程序所用时(代码对函数或函数模板进行调用时)才实例化。 如果某函数从未使用,则不会实例化该函数。
//普通类
class A{
public:
template<class T> //成员函数模板
void myfunc(T tempVal)
cout << tempval <<endl;
}
A a;
a.myfunc(3);//模板函数自动类型推断
//编译器遇到这条语句,将函数模板实例化。模板,不使用,不实例化。
//类模板的成员函数模板
template<class C>
class A{
public:
template<class T2>
A(T2 v1, T2 v2){
//构造函数模板
}
template<class T> //成员函数模板
void myfunc(T tempVal)
cout << tempval <<endl;
void myfpt(){
//普通成员函数
}
C m_ic;
}
A<float> a(1,2); //类模板的模板参数必须指定,函数模板自动推导。
A<float> a(1.1, 2.2);
a.myfpt(); //调用时,才实例化该函数
成员函数外部实现
template<class C> //先跟类模板的模板函数列表
template<class T2> //构造函数自己的模板函数列表
A<C>::A(T2 v1, T2 v2){
cout << v1 << v2 << endl; //构造函数模板
}
3.模板类特化
模板函数特化和模板类特化的区别:模板函数只能全特化,而模板类既有全特化又有偏特化。
3.1全特化
全特化即对所有模板类型都进行特化。
#include <iostream>
using namespace std;
template<typename T1, typename T2>
class A{
public:
void function(T1 value1, T2 value2){
cout<<"value1 = "<<value1<<endl;
cout<<"value2 = "<<value2<<endl;
}
};
template<>
class A<int, double>{ // 类型明确化,为全特化类
public:
void function(int value1, double value2){
cout<<"intValue = "<<value1<<endl;
cout<<"doubleValue = "<<value2<<endl;
}
};
int main(){
A<int, double> a;
a.function(28, 28.8);
return 0;
}
运行截图:
3.2偏特化
偏特化即对模板类型做一些限制特化。
#include <iostream>
using namespace std;
template<typename T1, typename T2>
class A{
public:
void function(T1 value1, T2 value2){
cout<<"value1 = "<<value1<<endl;
cout<<"value2 = "<<value2<<endl;
}
};
template<typename T>
class A<T, double>{ // 部分类型明确化,为偏特化类
public:
void function(T value1, double value2){
cout<<"charValue = "<<value1<<endl;
cout<<"doubleValue = "<<value2<<endl;
}
};
int main(){
A<char, double> a;
a.function('a', 28.8);
return 0;
}
运行截图:
三、模板类AutoPtr
1.构造函数
template<class T>
AutoPtr<T>::AutoPtr(T* pData)
{
m_pData = pData;
m_nUser = new int(1);
}
2.析构函数
~AutoPtr()
{
decrUser();
}
void decrUser();
template<class T>
void AutoPtr<T>::decrUser()
{
--(*m_nUser);
if ((*m_nUser) == 0)
{
delete m_pData;
m_pData = 0;
delete m_nUser;
m_nUser = 0;
}
}
3.拷贝构造函数
template<class T>
AutoPtr<T>::AutoPtr(const AutoPtr<T>& h)
{
m_pData = h.m_pData;
m_nUser = h.m_nUser;
(*m_nUser)++;
}
4.等号、->、*等运算符重载
AutoPtr<T>& operator=(const AutoPtr<T>& h);
T* operator->()
{
return m_pData;
}
T& operator*()
{
return *m_pData;
}
const T& operator *()const
{
return *m_pData;
}
const T* operator ->()const
{
return m_pData;
}
template<class T>
AutoPtr<T>& AutoPtr<T>::operator=(const AutoPtr<T>& h)
{
decrUser();
m_pData = h.m_pData;
m_nUser = h.m_nUser;
(*m_nUser)++;
}
5.主函数调用AutoPtr
#include<iostream>
#include <vector>
#include "autoptr.h"
#include "CMatrix.h"
using namespace std;
int main()
{
AutoPtr<CMatrix> h1;
double data[6] = {1,2,3,4,5,6};
h1->Create(2,3,data);
cout << *h1 << endl;
AutoPtr<CMatrix> h2(h1);
(*h2).Set(0,1,10);
cout << *h1 << endl << *h2;
}
运行截图: