1.模板定义
模板是泛型编程
的基础,所谓泛型编程即编写与类型无关
的逻辑代码,是一种复用
方式。
分为:
(1)模板函数
(2)模板类
2.模板函数
格式:
template <class/typename 形参名1,class/typename 形参名2>
返回类型 函数名(参数列表){...}
基于普通函数讲解模板函数的实用性:
//普通函数
bool Equal(int x1, int x2)
{
return x1 == x2;
}
bool Equal(const string& s1, const string& s2)
{
return s1 == s2;
}
void TestEqual()
{
string s1="abc";
string s2="cde";
cout << Equal(s1, s2) << endl;
cout << Equal(2,2)<< endl;
}
若要比较char/double型还需实现多个函数,过于麻烦吧!接下来体会模板的复用之处。
//模板实现函数
template <class T>
bool Equal(const T& s1, const T& s2)
{
return s1 == s2;
}
void TestEqual()
{
string s1="abc";
string s2="cde";
cout << Equal(s1, s2) << endl;
cout << Equal(2,2)<< endl;
}
这样仅使用一份代码,即可实现编写者的需求,复用性高
。
编译器推演过程如下:
汇编层查看如下:
3.模板参数匹配及显示实例化
借助代码:
template <class T>
bool Equal(const T& s1, const T& s2)
{
return s1 == s2;
}
void TestEqual()
{
//cout << Equal(1, 1.3) << endl;//出错,编译不通过
cout << Equal<int>(1,2) << endl;//显示实例化
cout << Equal<double>(2.5,2)<< endl;
}
4.模板类
这里借助用模板实现Vector解释
模板类的格式:
template< class 形参名1 , class 形参名2 , … c lass 形参名n>
class 类名
{ … };
template <class T>
class Vector
{
public:
//构造函数
Vector()
:_first(NULL)
, _finish(NULL)
, _endofstorage(NULL)
{}
//拷贝构造函数
Vector(const T& v)
{
if (v.Size() > 0)//不为空
{
_first = new T[v.Size()];
memcpy(_first, v._first, sizeof(T)*v.Size());
_finish = _first + v.Size();
_endofstorage = _first + v.Size();
}
else//为空
{
_first = _finish = _endofstorage = NULL;
}
}
//析构函数
~Vector()
{
delete[] _first;
_first = _finish = _endofstorage;
}
size_t Capacity() const
{
return _endofstorage - _first;
}
//赋值运算符重载v1=v2
Vector<T>& operator=(T& v)
{
swap(_first, v._first);
swap(_finish, v._finish);
swap(_endofstorage, v._endofstorage);
return *this;
}
size_t Size()const
{
return _finish - _first;
}
//扩容
void Expand(size_t n)
{
if (n > Capacity())
{
size_t size = Size();
T* tmp = new T[n];
if (_first)
{
memcpy(tmp, _first, sizeof(T)*Size());
delete[] _first;
}
_first = tmp;
_finish = _first + size;
_endofstorage = _first + n;
}
}
void PushBack(T x)
{
//判满
if (_finish == _endofstorage)
{
if (Capacity() == 0)
{
Expand(3);
}
else
{
Expand(Capacity() * 2);
}
}
*_finish = x;
_finish++;
}
void PopBack()
{
assert(_finish > _first);
--_finish;
}
void Insert(size_t pos, T x)
{
assert(pos <Size());
if (_finish == _endofstorage)
{
if (Capacity() == 0)
{
Expand(3);
}
else
{
Expand(Capacity() * 2);
}
}
int end = Size() - 1;//下标
while (end >= (int)pos)
{
//后移数据
_first[end + 1] = _first[end];
--end;
}
_first[pos] = x;
++_finish;
}
void Erase(size_t pos)
{
assert(pos < Size());
size_t cur = pos;
while (cur < Size() - 1)
{
_first[cur] = _first[cur + 1];
++cur;
}
--_finish;
}
size_t Find(T x)
{
T*cur = _first;
while (cur != _finish)
{
if (*cur == x)
{
return 1;
}
else
{
++cur;
}
}
return -1;
}
void Print()
{
T* cur = _first;
while (cur != _finish)
{
cout << *cur << " ";
++cur;
}
cout << endl;
}
private:
T* _first;
T* _finish;
T* _endofstorage;
};
void TestVector()
{
Vector<int>v1;
v1.PushBack(1);
v1.PushBack(2);
v1.PushBack(3);
v1.Print();
Vector<double>v2;
v2.PushBack(1);
v2.PushBack(2);
v2.PushBack(3);
v2.Print();
}
编译器推演过程如下:
汇编层查看:
5.模板参数-实现容器适配器
使用容器适配器实现复用
。
实现栈:
template<typename T>
class SeqList
{
private:
T* _data;
int _size;
int _capacity;
};
template<class T,class Container=SeqList<T>>
class Stack
{
public:
void Push(const T& x);
void Pop();
const T& Top();
bool Empty();
private:
Container _con;
};
void TestContainer()
{
Stack<int> s1;
Stack<int, SeqList<int>>s2;
Stack<int, SeqList<char>> s3;
}
6.模板的模板参数- -容器适配器
template<typename T>
class SeqList
{
private:
int _size;
int _capacity;
T* _data;
};
// template <class T, template<class> class Container>
template<class T, template<class> class Container=SeqList> // 缺省参数
class Stack
{
public:
void Push(const T& x);
void Pop();
const T&Top();
bool Empty();
private:
Container<T> _con;
};
void Test()
{
Stack<int> s1;
Stack<int,SeqList<int>>s2;
}
比较5,6的推演过程:
7.非类型的类模板参数
//template<typename T, size_t MAX_SIZE>
template<typename T, size_t MAX_SIZE = 10> //带缺省模板参数
class SeqList
{
public:
SeqList();
private:
T _array[MAX_SIZE];
int _size;
};
template<typename T, size_t MAX_SIZE>
SeqList<T,MAX_SIZE>::SeqList()
:_size(0)
{}
void Test()
{
SeqList<int> s1;
SeqList<int,20> s2;
}
注意事项
:
1 .非类型模板形参:模板的非类型形参也就是内置类型形参,如:template < class T, int a> class B{};其中int a就是非类型的模板形参。
2.非类型形参在模板定义的内部是常量值,也就是说非类型形参在模板的内部是常量。
3.非类型模板的形参只能是整型,指针和引用,像double,String, String *这样的类型是不允许的。但是double &,double ,对象的引用或指针是正确的。
4. 调用非类型模板形参的实参必须是一个常量表达式,即他必须能在编译时计算出结果。
5.注意:任何局部对象,局部变量,局部对象的地址,局部变量的地址都不是一个常量表达式,都不能用作非类型模板形参的实参。全局指针类型,全局变量,全局对象也不是一个常量表达式,不能用作非类型模板形参的实参。
6.全局变量的地址或引用,全局对象的地址或引用const类型变量是常量表达式,可以用作非类型模板形参的实参。
7.sizeof表达式的结果是一个常量表达式,也能用作非类型模板形参的实参。
8.当模板的形参是整型时调用该模板时的实参必须是整型的,且在编译期间是常量,比如:template < class T, int a> class A{};如果有int b,这时A< int, b> m;将出错,因为b不是常量,如果const int b,这时A< int, b> m;就是正确的,因为这时b是常量。
9..非类型模板形参的形参和实参间所允许的转换:a.允许从数组到指针,从函数到指针的转换。如:template < int *a> class A{}; int b[1]; A< b> m;即数组到指针的转换
b.const修饰符的转换。如:template< const int a> class A{}; int b; A<&b> m; 即从int 到const int *的转换。
c.提升转换。如:template< int a> class A{}; const short b=2; A< b> m; 即从short到int 的提升转换
d.整值转换。如:template< unsigned int a> class A{}; A< 3> m; 即从int 到unsigned int的转换
8.非类型的模板函数参数
template <typename T, int VAL>
T addValue(T const& x)
{
return x + VAL;
}
当我们需要把「函数」或「某种通用操作」作为参数传递时,这一类函数就很有用。例如使用STL(Standard Template Library,标准模板库)时,你可以运用上述 function template 的实例(instantiation),将某值加到元素集内的每一个元素身上:
std::transform (source.begin(), source.end(), // 来源端起止位置
dest.begin(), // 目的端起始位置
addValue<int,5>); // 实际操作
最后一个自变量将 function template addValue()实例化了,使其操作成为「将5加进一个int 数 值中」。算法transform()会对source 中的所有元素调用这个具现体(函数),然后把结果传入 dest 中。注意上述例子带来的一个问题:addValue< int,5> 是个 function template 实体(instance),而我们知道,所谓「function templates 实体」被认为是命名了一组重载函数集,即使该函数集内可能只有一个函数。根据目前标准,编译器无法借助「重载函数集」来进行 template parameter 的推导。因此你不得不把 function template argument 强制转型为精确类型
std::transform (source.begin(), source.end(), // 来源端起止位置
dest.begin(), // 目的端起始位置
(int(*)(int const*)) addValue<int,5>); // 操作
9.模板的分离编译
解决办法:
1.在模板头文件 xxx.h 里面显示实例化-> 模板类的定义后面添加 template class
SeqList< int >; 一般不推荐这种方法,一方面老编译器可能不支持,另一方面实例化依赖调用者。(不推荐)
2.将声明和定义放到一个文件 ” x x x . hpp” 里面,推荐使用这种方法
模板总结
优点:
1.模板复用了代码,节省资源,更快的迭代开发,C++ 的标准模板库(STL )因此而产生。
2.增强了代码的灵活性。
缺点:
1.模板让代码变得凌乱复杂,不易维护,编译代码时间变长。
2.出现模板编译错误时,错误信息非常凌乱,不易定位错误