一,首先看看函数模板和类模板的区别:
1.声明方式不同,以sum(int a,int b)为例
//函数模板
template<tepename T>
const T&sum(const T&a,const T&b) const
{
...
return T();
}
然而类模板中的每个成员函数除了(友元)几乎都是函数模板,特别注意的是类模板的声明和定义往往都在 .h头文件中。
//类模板
#pragma once
template<class T>
class Pair
{
private:
...
public:
Pair(){}
const T&sum(const T&a,const T&b) const
~Pair(){;}
...
};
template<class T>//头文件中定义,因为类模板主要是给编译器下达指令如何编译在生成特定的运行代码
template<T>::const T&sum(const T&a,const T&b) const
{
...
return T();
}
2,具体化的异同
他们都有隐式实例化(编译器根据模板自己推演),和具体化(显示实例化和具体化),其形式大体相似。
//函数模板的具体化
...
template<class T>
void Swap(T&,T&);//函数模板
template<>void Swap(job&,job&);//显示具体化
int main()
{
template void Swap<>(char&,char &);//or template void Swap<char>(char&,char &)
//显示实例化,因为该实例化参数列表中可以推断出实例化的类型,所以可以不特定指出<>里面为char,如果没有参数列表必须指出实例化,<>里面的类型
...
int a,b;
Swap(a,b);//调用隐式实例化
...
job n,m;
Swap(n,m);//调用显示具体化;
...
char c,d;
Swap(c,d);//调用显示具体化;
//**
## 特别的你还可以自定义选择:
Swap(T1,T2)<>;
Swap(T1,T2)<int ,int>;//将模板中的T替换为int
}
实例化和具体化的区别第一点在于template后面的<>有和无,第二点在于具体化需要定义,而实例化只需要声明告诉编译器如何使用模板生成你指定的类型。
//类模板的具体化
//类的显示实例化
声明必须位于模板所在的名称空间内,然后根据通用模板生成特定的具体化
template class AraayTp<string,100>;
//类的显示具体化的格式
template<> class 类名<具体化的类型>{}
eg:
template<>class SortArray<const char* >
{
...
};
表示提供一个专门const char *类型使用的SortArray模板
值的注意的是,类模板还支持部分具体化,也就是部分闲置模板的通用性
eg:
template <class T1,class T2> class Pair<T1,T2>{...};//一般模板的定义
template <class T1> class Pair<T1,int>{...};//部分具体化
template后的<>里面表示没有被具体化的参数,上述列子表示参数T1没被具体化,而参数T2被具体化为int
如果<>为空将导致具体化
类模板部分具体化的注意的问题:
1,如果有多个模板可以选择则选择具体化程度最高的模板
Pair<double,double>p1;//选择一般模板
Pair<double,int>p2//选择部分具体化模板
2,编译器优先选择含有指针类型具体化,普通版本优先程度低于指针版本
stack<char>st1;//#1
stack<char *>st2//#2
//#2含有指针类型,编译器优先选择#2
二,深入探讨类模板的特性:
1,成员模板和将模板作为参数
...
//相当于类的嵌套,iner模板作为basic模板的成员
template<class T>
class basic
{
private:
template<typename U>
class iner
{
...
};
iner<T>q;
iner<int>q2;
public:
basic(){}
....
};
一个更恰当的例子,成员模板的实用:
//完整的代码可运行,实现了一个利用vector的特性实现一个队列
//其中vector模板作为Queue的成员
#pragma once
#include <vector>
#include <iostream>
#include <queue>
using namespace std;
template<class T>
class QueueTp
{
private:
using arryTP = vector<T>;
arryTP que;//vector模板作为成员
queue<int>q;
public:
QueueTp()
{
que = vector<T>();
}
int cuSize()const;
bool isempty() { return que.empty(); }
void push(const T&it);
void pop(T&it);//弹出一个元素到it中
T front()const;
~QueueTp() {}
};
template<class T>
int QueueTp<T>::cuSize() const
{
return que.size();
}
template<class T>
void QueueTp<T>::push(const T & it)
{
que.push_back(it);
}
template<class T>
void QueueTp<T>::pop(T & it)
{
//使用迭代器
typename arryTP::iterator firstElement = que.begin();
que.erase(firstElement);
}
template<class T>
T QueueTp<T>::front() const
{
return que.front();
}
2,类模板的还有一个特性是将模板作为参数(模板包含 本身就是模板 的参数)(全部文件在这,可粘贴测试):
//头文件
#pragma once
#include <iostream>
/*#include <stack>*/
#include "stackp.h"
#include <queue>
using namespace std;
template<template<typename T>class Thing>
class Crab
{
private:
Thing<int> s1;
Thing<double>s2;
public:
Crab() {};
//假定Thing有一个push()和pop()
bool push(int a,double x){ return s1.push(a) && s2.push(x); }
bool pop(int &a, double &x){ return s1.pop(a) && s2.pop(x); }
~Crab(){;}
};
//主函数进行测试
#include "pch.h"
#include <iostream>
/*#include <stack>*/
#include "Crab.h"
#include "beta.h"
int main()
{
Crab<stackp>neb;
int a;
double b;
while (cin>>a>>b&&a>0&&b>0)
{
if (!neb.push(a,b))
break;
}
cout << "两个元素同时出栈后的结果:" << endl;
while (neb.pop(a,b))
{
cout << a << " , " << b << endl;
}
return 0;
}
//stackp.h文件
#pragma once
#include <iostream>
using namespace std;
template<class Type>
class stackp
{
private:
enum { SIZE = 10 };
int stacksize;
int top;
int currentsize;
Type *item;
public:
explicit stackp(int s=SIZE);
stackp(const stackp&st);
int cuSize()const;
bool isfull() { return top == stacksize; }
bool isempty() { return top == 0; }
bool push(const Type&it);
bool pop(Type&it);//弹出一个元素到it中
Type Top()const;
~stackp() { delete[]item; }
};
template<class Type>
inline stackp<Type>::stackp(int s):stacksize(s), top(0)
{
item = new Type[stacksize];
currentsize = 0;
}
template<class Type>
stackp<Type>::stackp(const stackp & st)
{
stacksize = st.stacksize;
currentsize = st.currentsize;
top = st.top;
item = new Type[stacksize];
for (int i=0;i<stacksize;i++)
{
item[i] = st.item[i];
}
}
template<class Type>
inline int stackp<Type>::cuSize() const
{
return currentsize;
}
template<class Type>
inline bool stackp<Type>::push(const Type & it)
{
if (top<stacksize)
{
item[top++] = it;
currentsize++;
return true;
}
return false;
}
template<class Type>
inline bool stackp<Type>::pop(Type & it)
{
if (top>0)
{
it = item[--top];
currentsize--;
return true;
}
return false;
}
template<class Type>
inline Type stackp<Type>::Top() const
{
if (top-1>=0)
{
return item[top - 1];
}
cout << "错误" << endl;
exit(-1);
}
2,类模板和友元
1.非模板友元
...
class HasFriend
{
private:
static int ct;
...
public:
**//1 声明**
friend void counts();//不含有模板参数
friend void reports(HasFriend<T>&);//含模板参数()里面必须指出具体化,而不是HasFriend&
...
}
void counts()
{
cout<<"int count"<<HasFriend<int>::ct<<endl;
cout<<"int count"<<HasFriend<double>::ct<<endl;
}
**//2定义**
参数类型必须声明特定类型具体化
void reports(HasFriend<int>&a)
{
...
}
void reports(HasFriend<double>&a)
{
...
}
非模板友元report()本身并不是模板函数,只是使用了一个模板参数,所以定义中必须使用显示具体化其具体类型
2,约束模板友元函数(一个三部曲的每种T类型都有自己的友元函数)
第一步:在类定义的前面声明每个模板函数
template void counts();
template void reports(T
&);//外部声明
第二步:类中声明友元并根据类模板参数的类型声明具体化
friend void counts();
friend void reports<>(HasFriend&);
第三步:根据第一步的声明提供定义
template<typename TT》
inline void counts()
{
cout << "tempalte size: "
<< sizeof(HasFriend<TT》) << ": " << "template cunts: " <<
HasFriend<TT》::ct << endl;
}
友元不属于类所以模板声明是template中的参数可以自定义比如上面是TT,只要保证定义里面的参数是对应的即可(使用中文》是因为与博客的引用>发生冲突)
template<class T》
inline void reports(T &hf)
{
cout << hf.item <<
endl;
}
eg:
#pragma once
#include<iostream>
using namespace std;
template<typename T> void counts();
template<class T> void reports(T &);//外部声明
template<typename TT>
class HasFriend
{
private:
TT item;
static int ct;
public:
friend void counts<TT>();
friend void reports<>(HasFriend<TT>&);
HasFriend(const TT&i):item(i){ct++;}
~HasFriend(){ct--;}
};
template<typename T>
int HasFriend<T>::ct = 0;
template<typename T>
inline void counts()
{
cout << "tempalte size: " << sizeof(HasFriend<T>) << ": " << "template cunts: " << HasFriend<T>::ct << endl;
}
template<class T>
inline void reports(T &hf)
{
cout << hf.item << endl;
}
int main()
{
counts<int>();//是一个约束模板友元必须具体化
HasFriend<int>hfi1(10);
HasFriend<int>hfi2(20);
HasFriend<double>hfdb(10.5);
reports(hfi1);
reports(hfi2);
reports(hfdb);
cout << "count<int>的输出:" << endl;
counts<int>();
cout << "count<double>的输出:" << endl;
counts<double>();
return 0;
}
//输出:——————————————————————————————————————————
tempalte size: 4: template cunts: 0
10
20
10.5
count的输出:
tempalte size: 4: template cunts: 2
count的输出:
tempalte size: 8: template cunts: 1
E:\c++语法xuxi\c++重构\Debug\类模板-成员模板和友元.exe (进程 13896)已退出,返回代码为: 0。
若要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口…
**
注意:
**
正如标题所说每一T类型都有各自的友元函数counts()相互独立
3,模板类的非约束模板友元函数(友元模板类型参数和类模板参数不同的友元)
声明:
template<typename T>]
class xx
{
private:
T xxx;
...
template<class C,class D> friend void show(C&,D&);
}
定义:
template<class C,class D> friend void show(C&c,D&d)
{
cout<<c.xxx<<d.xxx;
}
假如在main中声明对象 xxhf(10); 则编译器工作时用xx替换C,D friend void
show(xx&,xx&);
模板(上)完!