- Bjarne Stroustrup(C++创造者)最初将template称为被参数化的类型(parameterized type):称其参数化是因为类型相关信息可自template定义中剥离,称其类型则因为每个class template或function template基本上都随着他所作用或它所包含的类型而有性质上的变化,本身就像某种类型。
- template(模板)能根据用户指定的特定值或特定类型,自动产生一个函数或类。
- 二叉树(binary tree)class template包含两个class,BinaryTree用来储存指向根节点的指针,BTnode用来储存节点实值以及连接至左、右两个节点的链接。节点实值的类型(value type)正是我们希望加以参数化的部分。
- BinaryTree类提供的操作行为有:插入(insert)、移除(remove),搜寻(find),清除(clear)、以特定遍历方式打印(print),遍历方式有:中序(inorder)、前序(preorder)、后序(postorder)。
- template机制将类定义中 与类型相关(type-dependent) 和 独立于类型之外 的两部分分离开来。
template <typename Type>
class BinaryTree; //前置声明
template <typename valType>
class BTnode {
//针对每一种BTnode实际类型,
//使对应的BinaryTree实例成为其friend
friend class BinaryTree<valType>;
public:
//...
private:
valType _val;
int _cnt; //同一值的插入次数
BTnode *_lchild;
BTnode *_rchild;
};
- BinaryTree class仅声明一个 BTnode 指针,指向二叉树的根节点:
template <typename elemType>
class BinaryTree {
public:
//...
private:
//BTnode必须以template parameter list加以限定
BTnode<elemType> *_root;
};
- 在class template及其member的定义中,不必以template parameter list进一步限定class template,除此之外的场合均要限定(如上所示)。
- 为class template定义一个inline函数,做法和为non-template class定义一个inline函数一样。但是,在类体外,class template member function的定义语法有所不同:
template <typename elemType>
inline BinaryTree<elemType>:: //在class定义范围之外
BinaryTree() : _root(0) //在class定义范围之内
{}
/*
开始于关键字template和一个参数列表,然后便是函数定义本身,
并带有关键字inline和class scope运算符。
inline必须紧跟在template和参数列表之后。
*/
template <typename elemType>
inline BinaryTree<elemType>& //inline + 返回类型
BinaryTree<elemType>:: //class scope限定
operator=(const BinaryTree &rhs)
{
if(this != &rhs)
{
clear();
copy(_root, rhs>_root);
}
return *this;
}
在class scope运算符出现之后,其后所有东西都被视为位于class定义范围内,所以第二次出现的
BinaryTree就不再需要限定了。
- 不论内置类型或class类型,都可能被指定为class template的实际类型。建议将所有的template类型参数视为class 类型来处理,这意味着,我们会把它声明为一个const reference,而非以传值(by value)方式传递:(即使内置类型的值传递效率往往高于引用传递)
//constructor定义中在member initialization list初始化
template <typename valType>
inline BTnode<valType>::
BTnode(const valType &val)
//将valType视为某种class类型
: _val(val)
{
//_val = val; //不这样做,因为val可能是class类型
_cnt = 1;
_lchild = _rchild = 0;
}
若
_val是class类型MatrixBTnode<Matrix> btnm( matrix_a );:
constructor函数体内对_val的赋值操作可分为两个步骤:
(1)函数体执行前,Matrix的default constructor会先作用于_val身上;
(2)函数体内以copy assignment operator将val复制给_val。
而member initialization list只需一步:
(1)以copy assignment operator将val复制给_val。
new表达式可分解为两个操作:- 向程序的空闲空间请求内存。如果分配到足够的空间,就返回一个指针指向新对象(如果空间不足,会抛出 bad_alloc 异常( 第七章))
- 如果第一步成功,并且外界指定了一个初值,这个新对象便会以最适当的方式被初始化。
- 以pointer来传递参数,只能更改pointer所指的对象,不能更改pointer本身。reference to pointer(
*&)使我们不但可以改变pointer本身,也可以改变此pointer指向的对象。 - 将output运算符定义为一个function template:
template <typename elemType>
inline ostream&
operator<<(ostream &os, const BinaryTree<elemType> &bt)
{
os << "Tree: " << endl;
bt.print(os);
return os;
}
BinaryTree<string> bts;
cout << bts <<endl; //第二参数为 BinaryTree<string>
BinaryTree<int> bti;
cout << bti <<endl; //第二参数为 BinaryTree<int>
- template参数并不仅是类型(type),也可以使用常量表达式:
//将对象所含的元素个数参数化
template <int len>
class num_sequence {
public:
num_sequence(int beg_pos = 1);
//...
};
template <int len>
class Fibonacci : public num_sequence<len> {
public:
Fibonacci(int beg_pos = 1)
: num_sequence<len>(beg_pos){}
//...
};
Fibonacci<16> fib1; //beg_pos = 1,len = 16
Fibonacci<16> fib2(17); //beg_pos = 17,len = 16
- 全局作用域内的函数及对象,其地址也是一种常量表达式,因此也可以用来作为template的参数 :
//pf是一个指向 依据特定数列类型,产生pos个元素,放到vector seq内 的函数
template<void (*pf)(int pos, vector<int> &seq)>
class numeric_sequence {
public:
numeric_sequence(int len, int beg_pos = 1)
{
//检查pf是否为null
if(!pf)
//产生错误信息并退出
_len = len > 0 ? len : 1;
_beg_pos = beg_pos > 0 ? beg_pos : 1;
pf(_beg_pos + _len - 1, _elems);
}
//...
private:
int _len;
int _beg_pos;
vector<int> _elems;
};
void fibonacci(int pos, vector<int> &seq);
void pell(int pos, vector<int> &seq);
//...
numeric_sequence<fibonacci> ns_fib(12);
numeric_sequence<pell> ns_pell(18, 8);
- class template无法基于参数列表的不同而重载。
- 判断:class template的类型参数仅能用以传递元素类型——像二叉树或标准库的vector、list容器那样。 ( × )
//将数列类定义为class template,将实际的数列类剥离成为参数
template <typename num_seq>
class NumericSequence {
public:
NumericSequence(int len = 1, int bpos = 1)
: _ns(len, bpos){}
//以下会通过函数的命名规范,调用未知的数列类中的同名函数
//此处所谓函数命名规范是:每个num_seq参数类都必须提供名为
//calc_elems()和is_elem()的函数。
void calc_elems(int sz) const {_ns.calc_elems(sz);}
bool is_elem(int elem) const {return _ns.is_elem(elem);}
//...
private:
num_seq _ns;
};
- member function也可以定义为template形式;class template内也可以定义member template function。
- ofstream是ostream的子类。
本文深入探讨了C++中的模板(template)编程,介绍了如何使用template创建函数和类,以实现泛型编程。通过二叉树类模板示例,阐述了类型参数化、成员函数定义以及模板参数在不同场景下的应用。同时,讨论了template与类型相关和独立于类型之外的部分,以及template类型参数的处理方式。
6万+

被折叠的 条评论
为什么被折叠?



