(1)模板机制:将类型变成需要指定的参数,从而实现共用代码;
(2)类模板:定义一个类,变量类型以及方法的返回类型参数类型都需要用参数指定,主要用于容器;
和抽象类区分开来,类模板是一个具体的类类型;
(3)模板的定义template <typename T>,且方法的定义都需要放到头文件中,因为在创建模板实例之前就需要知道完整的定义。
(4)一个典型的模板定义如下:
//定义前面加一行表示这是一个模板,需要给类型参数T制定值
template <typename T>
class Grid
{
public:
Grid(size_t inWidth=kDefaultWidth,size_t inHeight=kDefaultHeight);
virtual ~Grid();
void setElementAt(size_t x,size_t y,const T& inElem);
T& getElementAt(size_t x,size_t y);
const T& getElementAt(size_t x,size_t y)const;
size_t getHeight() const{return mHeight;}
size_t getWidth() const{return mWidth;}
static const size_t kDefaultWidth=10;
static const size_t kDefaultHeight=10;
private:
void initializeCellsContainer();
std::vector<std::vector<T>> mCells;
size_t mWidth,mHeight;
};
//模板的每个方法定义前面都要加模板说明
template <typename T>
Grid<T>::Grid(size_t inWidth,size_t inHeight):mWidth(inWidth),mHeight(inHeight)//Grid后面要加<T>
{
initializeCellsContainer();
}
template <typename T>
Grid<T>::~Grid()
{
}
template <typename T>
void Grid<T>::initializeCellsContainer()
{
mCells.resize(mWidth);
for(std::vector<T>& colum:mCells)
{
colum.resize(mHeight);
}
}
template <typename T>
void Grid<T>::setElementAt(size_t x,size_t y,const T& inElem)
{
mCells[x][y]=inElem;
}
template <typename T>
T& Grid<T>::getElementAt(size_t x,size_t y)
{
return mCells[x][y];
}
template <typename T>
const T& Grid<T>::getElementAt(size_t x,size_t y) const
{
return mCells[x][y];
}
和正常的类定义类似,只是定义方法的时候需要注意格式的问题
(5)模板的实例化
Grid<int> myIntGrid;//指定数据类型,采用默认参数进行初始化
Grid<double> myDoubleGrid(11,11);//采用给定参数进行初始化
myIntGrid.setElementAt(0,0,10);
int x=myIntGrid.getElementAt(0,0);
Grid<int> grid2(myIntGrid);//复制构造函数
Grid<int> anotherIntGrid;
anotherIntGrid=grid2;//赋值
不仅可以保存数据类型,还可以保存其它类或者是指针等类型:
//使用SpreadsheetCell作为模板类型
Grid<SpreadsheetCell> mySpreadsheet;
SpreadsheetCell myCell("test");
mySpreadsheet.setElementAt(3,4,myCell);
//使用指针类型作为模板类型
Grid<const char*> myStringGrid;
myStringGrid.setElementAt(2,2,"hello");
//使用另一模板作为类型
Grid<vector<int>> gridOfVectors;
vector<int> myVector{1,2,3};
gridOfVectors.setElementAt(5,6,myVector);
编译器无法编译模板定义,因为不知道模板的数据类型,实例化其实就是将模板定义中的每一个T换成用户实例化的数据类型(int,char*...);
(6)可以给模板指定非类型参数如下:
template <typename T,size_t WIDTH,size_t HEIGHT>,编译之前就知道参数的值
这样在写模板方法的时候,需要加上这些参数如下:
template <typename T ,size_t WIDTH,size_t HEIGHT>
Grid<T,WIDTH,HEIGHT>::Grid(){...}
在实例化的时候需要:Grid<int,10,10> myGrid;
限制:不能使用非常量整数初始化实例;
Grid<int,10,10>和Grid<int,10,11>是两种不同的类型,不能互相赋值,或者当成参数;
(7)可以对某个方法定义模板;
(8)可以从类模板中派生一个新的类模板;
(9)函数模板:为不知道输入类型的函数定义模板
template <typename T>
size_t Find(T& value,T* arr,size_t size)
{
for(size_t i=0;i<size;i++)
{
if(arr[i]==value)
return i;
}
return (size_t)(-1);
}
调用方式:
res = Find<int>(x,intArr,sizeIntArr);
(10)模板别名的使用,typedef和using的区别:
使用typedef时候,必须给出T的类型:typedef MyTemplateClass<int> otherName;
typedef MyTemplateClass<T> otherName;是错误的
但是可以用:using otherName=MyTemplateClass<T>;
(11)替换函数语法
关键字decltype:将表达式作为实参,返回该表达式的类型;
在模板中,有时候并不知道函数返回值的类型,那么函数的定义可以如下:
template<typename Type1,typename Type2>
auto func(const Type1& t1,const Type2& t2)->decltype(t1+t2){return t1+t2;}
decltype(t1+t2)不能放在前面,因为那个时候t1和t2还没有定义