函数模板
模板的意义:对类型也可以进行参数化
(1)函数模板 <= 是不进行编译的,因为类型还不知道
template<typename T> //定义一个模板参数列表
bool compare(T a,T b) //compare是一个函数模板
{
return a>b;
}
(2)模板的实例化 <=函数调用点进行实例化
compare<int>(10,20);
compare<double>(10.5,20.5);
(3)模板函数 <= 才是要被编译器所编译的
bool compare<int>(int a,int b)
{
return a>b;
}
bool compare<double>(double a,double b)
{
return a>b;
}
(4)模板的实参推演 =>可以根据用户传入的实参的类型,来推到出模板类型参数的具体类型
compare(20,30);
(5)模板的特例化(专用化)
不是编译器提供的实例化,而是用户提供的实例化
比如下面遍历器的实例化是有问题的
//函数模板
template<typename T> //定义一个模板参数列表
bool compare(T a,T b) //compare是一个函数模板
{
return a>b;
}
/*
模板函数
bool compare<const char* >(const char* a,const char* b)
{
return a>b;//将地址进行比较,显然有问题
}
*/
//函数模板实参的推演 const char*
compare("aaa","bbb");
需要进行模板特例化
template<typename T> //定义一个模板参数列表
bool compare(T a,T b) //compare是一个函数模板
{
return a>b;
}
//函数模板 提供const char*类型的特例化版本
template<>
bool compare<const char *>(const char *a,const char *b)
{
return strcmp(a,b)>0;
}
//函数模板实参的推演 const char*
compare("aaa","bbb");
(6)非模板函数的重载关系
//函数模板 提供const char*类型的特例化版本
template<>
bool compare<const char *>(const char *a,const char *b)
{
return strcmp(a,b)>0;
}
bool compare(const char *a,const char *b)
{
return strcmp(a,b)>0;
}
compare("aaa","bbb");//编译器优先调用非模板函数
compare<const char*>("aaa","bbb");//编译器调用模板函数
注意:模板代码不能在一个文件中定义,在另外一个文件中使用(模板代码调用之前,一定要看到模板定义的地方,这样,模板才能够进行正常的实例化,产生能够被编译器编译的代码,所以一般模板代码都是放在头文件当中的)
(7)模板非类型参数 =》必须是整数类型(整数或者地址/引用都可以)都是常量
//SIZE就是模板非类型参数
template<typename T,int SIZE>
void sort(T* arr)
{
//冒泡排序
for(int i=0;i<SIZE-1;++i)
{
for(int j=0;j<SIZE-i-1;++j)
{
if(arr[j]>arr[j+1])
{
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1]=temp;
}
}
}
}
int main()
{
int arr[] = {12,3,4,5,6,4,3,1};
const int size = sizeof(arr)/sizeof(arr[0]);
sort<int,size>(arr);
for(int val:arr)
{
cout<<val<<" ";
}
}
函数模板示例代码:
#include<iostream>
#include<string.h>
#include<vector>
#include<algorithm>
using namespace std;
//函数模板
template<typename T> //定义一个模板参数列表
bool compare(T a,T b) //compare是一个函数模板
{
return a>b;
}
/*
模板函数
在函数调用点,编译器用用户指定的类型,从原模板实例化一份函数代码出来
bool compare<int>(int a,int b)
{
return a>b;
}
bool compare<double>(double a,double b)
{
return a>b;
}
bool compare<const char* >(const char* a,const char* b)
{
return a>b;
}
*/
//函数模板 提供const char*类型的特例化版本
template<>
bool compare<const char *>(const char *a,const char *b)
{
return strcmp(a,b)>0;
}
//非模板函数
bool compare(const char *a,const char *b)
{
return strcmp(a,b)>0;
}
int main()
{
//函数的调用点 进行模板的实例化
compare<int>(10,20);
compare<double>(10.5,20.5);
//函数模板的推演
compare(20,30);
compare<int>(30,40.5);//第二个参数会转为int
cout<<compare("aaa","bbb");//调用非模板函数
cout<<compare<const char*>("aaa","bbb");//调用模板函数
}
类模板(实现stack)
类模板 =>实例化 =>模板类
类模板的选择性实例化 :只会把调用到的成员函数进行实例化,没有调用的不会实例化
//使用类模板实现栈
#include<iostream>
#include<string.h>
using namespace std;
//类模板
template<typename T=int>//可以有默认参数
class SeqStack//模板名称
{
public:
//构造和析构函数名不用加<T>,其他出现模板的地方都加上类型参数列表
SeqStack(int size = 10)
:_pstack(new T[size]),
_top(0),
_size(size)
{
}
~SeqStack()
{
delete[] _pstack;
_pstack = nullptr;
}
SeqStack(const SeqStack<T> &stack)
:_top(stack._top)
,_size(stack._size)
{
_pstack = new T[_size];
for(int i=0;i<_top;i++)
{
_pstack[i] = stack._pstack[i];
}
}
SeqStack<T>& operator=(const SeqStack &stack)
{
if(this==&stack)
{
return *this;
}
delete[] _pstack;
_top = stack._top;
_size = stack._size;
for(int i=0;i<_top;i++)
{
_pstack[i] = stack._pstack[i];
}
return *this;
}
void push(const T &val)//入队
{
if(full())expand();
_pstack[_top++] = val;
}
void pop()//出队
{
if(empty())return;
--_top;
}
T top() const//返回栈顶元素
{
if(empty())
{
throw "stack is empty!";//抛出异常代表函数逻辑结束
}
return _pstack[_top-1];
}
bool full()const//栈满
{
return _top == _size;
}
bool empty()const//栈空
{
return _top==0;
}
private:
T *_pstack;//栈帧
int _top;//栈顶指针
int _size; //栈的内存空间
//顺序栈底层数组按2倍的方式扩容
int expand()
{
T *ptmp = new T[_size*2];
for(int i=0;i<_top;i++)
{
ptmp[i] = _pstack[i];
}
delete[] _pstack;
_pstack = ptmp;
_size*=2;
}
};
int main()
{
//类模板的选择性实例化
//模板类 class SeqStack<int>{};
SeqStack<int> stack;
stack.push(1);
stack.push(2);
stack.push(3);
while(!stack.empty())
{
cout<<stack.top();
stack.pop();
}
}
空间配置器(实现vector)
容器的空间配置器allocator负责:内存开辟/内存释放 对象构造/对象析构
大白话就是:将对象的构造和内存开辟,析构和内存释放分开
为了防止某些情况直接使用 new int[]
和 delete[] ptr
会出现误操作,因为new不仅会开辟内存还有调用构造函数,delete不仅释放内存还有调用析构函数(比如vector底层构造函数和开辟内存并不是同步进行的)
allocator底层实现:
//空间配置器 (和C++标准库的allocator实现一言)
template<typename T>
struct Allocator
{
T* allocator(size_t size)//负责内存开辟
{
return (T*)malloc(sizeof(T)*size);
}
void deallocate(void *p)//负责内存释放
{
free(p);
}
void construct(T *p,const T &val)//负责构造
{
new (p) T(val);//定位new 在指定的内存上 构造一个值为val的对象,会调用 T类型的拷贝构造
}
void destroy(T *p)//负责对象析构
{
p->~T();//~T()代表了T类型的析构函数
}
};
手写vector
#include<iostream>
#include<vector>
using namespace std;
//空间配置器 (和C++标准库的allocator实现一言)
template<typename T>
struct Allocator
{
T* allocator(size_t size)//负责内存开辟
{
return (T*)malloc(sizeof(T)*size);
}
void deallocate(void *p)//负责内存释放
{
free(p);
}
void construct(T *p,const T &val)//负责构造
{
new (p) T(val);//定位new 在指定的内存上 构造一个值为val的对象,会调用 T类型的拷贝构造
}
void destroy(T *p)//负责对象析构
{
p->~T();//~T()代表了T类型的析构函数
}
};
/*
容器底层内存开辟,内存释放,对象构造和析构,都通过allocator空间配置器来实现
*/
//类模板
template<typename T,typename Alloc = Allocator<T>>
class vector
{
public:
vector(int size=10)
{
//需要把内存开辟和对象构造分开处理
//_first = new T[size];
_first = _allocator.allocator(size);//仅开辟内存
_last = _first;
_end = _first+size;
}
~vector()
{
//析构容器有效的元素,然后释放_fisrt指针指向的堆内存
//delete[] _first;
for(T *p = _first;p!=_last;++p)
{
_allocator.destroy(p);//_first指针指向的数组的有效元素进行析构操作
}
_allocator.deallocate(_first);//释放堆上的数组内存
_first = _last = _end =nullptr;
}
vector(const vector& rhs)
{
int size = rhs._end - rhs.first;
//_first = new T[size];
_first = _allocator.allocate(size); //开辟空间
int len = rhs._last-rhs._last;
for(int i=0;i<len;i++)
{
//_first[i] = rhs._first[i];
_allocator.construct(_first+i,rhs._first[i]);
}
_last = _first + len;
_end = _first + size;
}
vector& operator=(const vector<T>& rhs)
{
if(this==&rhs)return *this;
//delete[] _first;
for(T *p = _first;p!=_last;++p)
{
_allocator.destrory(p);//_first指针指向的数组的有效元素进行析构操作
}
_allocator.deallocate(_first);//释放堆上的数组内存
int size = rhs._end - rhs.first;
//_first = new T[size];
_first = _allocator.allocate(size); //开辟空间
int len = rhs._last-rhs._last;
for(int i=0;i<len;i++)
{
//_first[i] = rhs._first[i];
_allocator.construct(_first+i,rhs._first[i]);
}
_last = _first + len;
_end = _first + size;
return *this;
}
void push_back(const T &val)
{
if(full())expand();
//*_last++ = val;
_allocator.construct(_last,val);//_last指针指向的内存构造一个值为val的对象
_last++;
}
void pop_back()
{
if(empty())return;
//--_last;
--_last;//不仅要把_last指针--,还需要析构删除的元素
_allocator.destroy(_last);
}
T back() const
{
return *(_last-1);
}
bool full() const
{
return _last == _end;
}
bool empty() const
{
return _first==_last;
}
int size() const
{
return _last - _first;
}
private:
T* _first;//指向数组起始的位置
T* _last;//指向数组中有效元素的后继位置
T* _end;//指向数组空间的后继位置
Alloc _allocator;//定义容器的空间配置器对象
void expand()//二倍扩容
{
int size = _end - _first;
//T* ptmp = new T[2*size];
T *ptmp = _allocator.allocator(2*size);//开辟空间
for(int i=0;i<size;i++)
{
//ptmp[i] = _first[i];
_allocator.construct(ptmp+i,_first[i]);//在 ptmp+i 位置构造值为 _first[i]的对象
}
//delete[] _first;
for(T *p=_first;p!=_last;++p)//把有效元素析构
{
_allocator.destroy(p);
}
_allocator.deallocate(_first);//释放空间
_first = ptmp;
_last = _first+size;
_end = _first + 2*size;
}
};
class Test
{
public:
Test()
{
cout<<"Test()"<<endl;
}
Test(const Test& tst)
{
cout<<"Copy Test()"<<endl;
}
~Test()
{
cout<<"~Test()"<<endl;
}
};
int main()
{
Test t1,t2,t3;
cout<<"----------------------------"<<endl;
vector<Test> vec;//仅开辟一段内存
vec.push_back(t1);//拷贝构造一个对象
vec.push_back(t1);//拷贝构造一个对象
vec.push_back(t1);//拷贝构造一个对象
cout<<"----------------------------"<<endl;
vec.pop_back();//仅析析构末尾有效对象,不释放内存
cout<<"----------------------------"<<endl;
return 0;
}