前言:Template(模板)能根据用户指定的特定值或特定类型,自动产生一个函数或类。
为什么要有模板呢,举一个例子:
(假定已经想好要定义的BTnode,用以存储结点数值,和左右孩子指针)
•这是二叉树的结点定义。
•为了存储不同类型的值,我们必须还要实现不同的BT_node类,比如 int_BTnode,double_BTnode类
•template机制把“与类型相关的”和“独立于类型之外”的两部分分离开来。
•一些成员函数的操作行为,不会随着包含这些成员函数的类不同而不同,普通模板也是。
•在一个类模板中,与类型相关的部分会被抽离出来,成为一个或多个参数。
示例如下:
class string_BTnode{
public:
//...
private:
string _val;
int _cnt;
string_BTnode *lchild;
string_BTnode *rchild;
}
template<typename valType>class BTnode;//类模板前置说明
//...
template<typename valType>
class BTnode{
public:
//...
private:
valType _val;
int _cnt;
BTnode *_lchild;
BTnode *_rchild;
};
定义该类对象的方式如下:
BTnode<int>bti;
BTnode<string>bts;
在类模板和类成员的定义中,可以用模板参数列表进一步限定类模板。
示例如下:
template<typename elemType>
class BinaryTree{
public:
//...
private:
BTnode<elemType> *_root;
//BTnode(模板)类必须以模板参数列表加以限定的例子
}
当指定某个实际类型作为BinaryTree模板类的参数,如:BinaryTree<string> st ,指针_root指向一个结点值类型为string的BTnode类类对象;
如果是BinaryTree<int> it; ,指针_root指向值类型为int的BTnode类对象。
BTnode模板类和BinaryTree模板类配合使用,所以这两个类要建立friend关系。
template<typename Type>
class BinaryTree;//BinaryTree类的前置声明
template<typename valType>
class BTnode{
friend class BinaryTree<valType>
//...
}
模板类的定义
template<typename elemType>
inline BinaryTree<elemType>::BinaryTree():_root(0){}
//这个类模板的内联成员函数是带成员初始化列表的构造函数
BinaryTree<elemType>::BinaryTree()
上述例子中,第二次出现的BinaryTree被视为类定义范围内,因为在类作用域运算符出现后,所以不需要加template<typename elemType>来限定,而第一次出现的BinaryTree前需要加template<typename elemType>。
Template类型参数的处理
规则:将所有的模板类型参数视为class类型处理
于是,模板类的实际类型参数,声明为const类模板类的实际类型名 &参数名。示例如下:
template<typename valType>
inline BTnode<valType>::BTnode(const valType &val):_val(val)
{
//将模板类型valType视为class类型
_cnt=1;
_lchild = _rchild = 0;
}
实现一个 Class Template
关于new表达式
new表达式可分解为两个操作:
示例如下:
_root = new BTnode<elemType>(elem);
其中elem被传入BTnode模板类的类构造函数。分配内存失败,初始化操作(类构造函数操作)不会发生。
模板类成员函数的定义/声明
示例如下:(具体看格式)
一般模板类成员函数定义:
template<typename valType>
void BTnode<valType>::insert_value(const valType &val)
{
if(val == _val)
{
//...
}//二叉树有这个结点,就把结点插入次数记录下来即可
if(val<_val)
//插入的结点比当前结点小,就插到当前结点的左孩子(如果当前结点的左孩子没有)
{
//...
}
else
//插入的结点比当前结点大,插入当前结点的右孩子
{
//...
}
}
内联模板类成员函数定义:
template<typename elemType>
inline void BinaryTree<elemType>::remove(const elemType &elem)
{
if(_root)
{
if(_root->_val==elem)
{
remove_root();
}
else
{
_root->remove_value(elem,_root);
}
}
}
一般模板类成员函数的声明:
template<typename valType>
void BTnode<valType>::
remove_value(const valType &val,BTnode *&prev);
模板类成员函数的定义/声明格式:
template<typename 自定义类型名(占位符)>
(按情况加inline)返回类型 模板类类名<自定义类型名>::
模板类成员函数名(参数表(比如const 自定义类型名 &参数名等));或者{//...}
函数参表出现 *&的说明
以FunctionTemplate完成的Output运算符
一个以模板函数完成的输出(<<)运算符
非模板函数形式的重载运算符函数的声明:
ostream& operattor<<(ostream&,const BinaryTree<int>&);
其定义与调用如下:
template<typename elemType>
inline ostream&
operator<<(ostream &os,const BinaryTree<elemType>&bt)
{
os<<"Tree:"<<endl;
bt.print(os);
return os;
}
int main()
{
BinaryTree<string>bts;
cout<<bts<<endl;
//编译器将elemType指定为string,产生一个对应的(针对BinaryTree<string>类型的),,运算符。
BinaryTree<int>bti;
cout<<bti<<endl;
}
在C++中可以用常量表达式作为模板参数,而且以常量表达式作为模板参数(<>内的参数)还可以给这个模板参数提供默认值,示例如下:
template<int len,int beg_pos>
class num_sequence{
//...
}
template<int len,int beg_pos>ostream&
//<<运算符的模板函数定义
operator<<(ostream&os,const num_sequence<len,beg_pos>*ns)
{
return ns.print(os);
}
以Template 参数作为一种设计策略
将某种特定的命名规范强加于被当作参数的类身上:每个类都必须提供某模板类中调用到的函数。这样的独特设计很高级,说明模板类的类型参数不光可用以传递元素类型,还可以传递class类型。示例如下:
非模板内定义成员模板函数
模板内定义成员模板函数