1.C++模板简介
1.模板概观
模板是c++的一种特性,允许函数或类(对象)通过泛型的形式表现或运行
c++通常使用两种模板
a.类模板,使用泛型参数的类
b.函数模板,使用泛型参数的函数
2.函数模板
template<typename T>
inline T Max(T a,T b)
{
return (a > b)?a:b;
}
从语法上讲,class和typename没有区别,但不能使用struct; 尽量使用typename; 模板实例化,编译器自动的; 如果该型别不支持函数所使用的操作,会报错; (比如std::complex没有重载>,但函数里使用了complex类的>) 模板会被编译两次,一次是本身,一次是调用时候的实例化; 参数推导,每个T必须严格匹配,不允许自动类型转换; 比如Max(1,2.0); 解决1:用static_cast或强制转换:Max(static_cast(1),2.0) 解决2:显式指定T的类型:Max(1,2.0)//1可以转换成double 函数模板可以像普通函数一样重载,可以重载同名的非模板函数; Max('a',4.2)参数类型不同,当存在非模板函数时,会调用非模板函数进行类型转换; 重载版本的声明必须位于调用位置之前;
3.类模板
类也可以通过参数泛化,从而可以创建出一族不同类型的类实例(对象);
类模板实参可以使某一型别或者常量(仅限int或enum);
在内部T像其他类型一样定员成员变量和成员函数;
如果类模板中需要使用到这个类模板本身,应该使用其完整定义,带<...>的形式;
Stack >两个尖括号不要连到一起,会编译成<<,经过验证,新版编译器不会出现该问题;
类模板特化,要把所有成员函数都重写一遍;可以添加新的成员函数;
偏特化:
template <typename T1,typename T2>class MyClass{...};
template <typename T> class MyClass{...}; 偏特化成同类型;
template <typename T>class MyClass<T,int>{...}; 偏特化成非泛型;
template <typename T1,typename T2>class MyClass<T1*,T2*>{...}; 偏特化成指针;
如果调用同等的匹配不止一个偏特化,二义性,编译器不会通过;
如下所示:
MyClass<int,int>; //可同时匹配2,3同类型和参数2偏特化为int
MyClass<int*,int*>; //可同时匹配2,4同类型和指针
类模板参数可以有默认值,如下:
template<typename T,typename TContainer = std::vector<T>>
class stack
{
private:TContainer m_Container;
}
2.泛型编程
1.概观
泛型编程是一种编程方法,这种方法将型别(type)以一种to-be-specified-later的方式给出,等到需要调用时,再以参数方式,通过具体的、特定的型别实例化一个具体的方法或对象
泛型编程是一种编程想法或思维,并不依赖于具体语言
2.特性 Traits
A[0],A[1],A[2].....A[n]
如何构建型别为T的初始类型,姑且使用T(0);
如果求和函数,求出的结果超出了类型的容量,
比如char数组”abc“,求和的值大于char类型容量255
会溢出overflow,此时可以强制使用来储存,或者使用Traits; Sigma函数的返回值的型别叫做T的trait;
T -> association -> characteristic of T -> another type -> trait;
Traits可以实现为模板类,而关联(association)是针对每个具体型别T的特化;
在这个例子里traits命名为Sigma Traits,叫做traits模板(traits template);
Traits可以实现为模板类;
template class SigmaTraits{};
template<>class SigmaTraits{
public: typedef int Return Type;} //当传进去的是char,return的类型变成int;
把每个传入类型都指定return的类型;
typename SigmaTrais::ReturnType //这一串是返回类型的写法;
3.迭代器
本身是一个对象,指向另一个(可以被迭代的)对象;
算法通常以迭代器作为输入参数;
3.容器
1.Vector
存放任意型别的动态数组;数据结构和操作与数组类似,在内存中是一段地址连续的空间;
Vector支持动态空间大小调整,随着元素的加入,vector内部会自动扩充内存空间;
头文件vector,名称空间std;
2.创建
创建一个T型别的空vector: vectorv;
创建一个容量是n的T型别vector: vectorv(n);
创建一个容量是n的T型别vector,并且都初始化为i: vectorv(n,i);
创建一个已有v的拷贝: vectorcopyOfV(v);
通过一个数组创建一个vector: int array[]={1,2,3,4,5,6,7};
vectorv(array,array+10);
3.方法
调用push_back函数,表示将元素添加至其尾部:
判断是否为空:empty();
获取大小:size();
访问:
vector::at(); 越界会抛出exception,但效率不如operator;
vector::operator[]; 不做边界检查,但访问效率高;
删除:
clear:清除整个vector;
pop_back:弹出vector尾部元素;
erase:删除vector中某一位置的元素
用法一:指定删除某一元素,v.erase(it+1); //t位置+1的位置的元素;
用法二:删除指定的满足某条件的;
对象里面重载了operator()的话,调用方法像函数一样;
- Deque
能存放任意型别的双向队列,新增了两个方法:
push_front: 在头部插入一个元素;
pop_front: 在头部弹出一个元素;
5.List
存放任意型别的双向链表(double linked list); 有next和prev指针指向前一个和后一个元素; 头文件list和名称空间std;
优势: 弹性,可以随意插入和删除元素,只需要改变next和prev的指针链接; 对于移动元素到另一个list,没有发生复制;
劣势: 只能以连续的方式存取list中的元素,查找任意元素的平均时间和list长度成线性比例; 对于查找、随机存取等元素定位操作,效率低; 在每个元素节点上增加一些较为严重的开销,即向前向后两个指针;