C++之模板
1.0 函数模板
2.0 类模板
1.0 函数模板:
1.0 函数模板是什么?
2.0 函数模板怎么写?
3.0 函数模板的实例化。
4.0 参数推演
5.0 函数模板的编译。
6.0 函数模板的重载
7.0 特化(特殊化处理)
8.0 类型形参的转换
以前写一个通用函数一般是函数重载。缺点:代码复用率不高,新类型参数出现,得重写函数。它不能解决返回值不同的问题。而且重载的函数内部大致一样,一错全错。多态,也只是重载的的另外一种体现。为了解决上面的不足所以有了函数模板。
1.0 函数模板是什么?
答:严格来说函数模板并不是真正意义上的函数它的类型不明确只有在调用的时候才知道。它视为了函数的通用而存在的。
2.0 函数模板怎么写?
格式就是:template< class T>
T Function(T t)
{}
不要忘记函数模板的两个参数列表:1.0 模板参数列表(有模板类型参数和非模板类型参数) 2.0 模板函数参数列表(调用参数)
class可以用typename替换但是不能用struct替换
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
template<typename T>
T Add(T left, T right)
{
return left + right;
}
int main()
{
cout << Add(10, 20) << endl;
cout << Add(1.0, 2.0) << endl;
cout <<Add('1','2')<< endl;
system("pause");
return 0;
}
结果:
非模板类型参数:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
template<class T1,class T2>
void PrintfArray(T1 t1, T2 t2)
{
}
template<class T,int N>
void PrintfArray(T (&array)[N])
{
cout << N << endl;
for (int i = 0; i < N; ++i)
{
cout <<array[i]<< endl;
}
cout<<endl;
}
int main()
{
const int iByteCnt = 9;
int array[9] = {1, 2, 3, 5, 6, 7, 8, 9 };
int b[iByteCnt + 1];
int a[10];
PrintfArray(array);//等价于PrintfArray<int,9> (08814BFh)
PrintfArray(a); // 等价于PrintfArray<int,10> (08814BAh)
PrintfArray(b); //等价于PrintfArray<int,10> (08814BAh) 编译器不会合成新的函数
system("pause");
return 0;
}
3.0 函数模板的实例化
实例化就是在测试函数里调用模板函数(类型明确(自定义或者内置类型)),将函数模板里的模板参数列表替换。
来却定编译器究竟需要些写什么样的模板函数
4.0 参数推演
call后面的就是编译器进行 的参数推演(即通过函数的形参合返回值来确定模板函数参数列表里的类型参数)
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
template<typename T>
T Add(T left, T right)
{
return left + right;
}
int main()
{
cout << Add(10, 20) << endl; // call Add<int> (08910AAh)
cout << Add(1.0, 2.0) << endl; // call Add<double> (0D9110Eh)
cout <<Add('1','2')<< endl; // call Add<char> (0DA14B0h)
system("pause");
return 0;
}
5.0参数模板的编译
有两次编译 1.0 在实例化之前进行语法检测。(是不是具有一个函数的内容,返回值,参数列表,作用域即{}) 但是不能够检测遗失的分号。
2.0 在实例化之后,检测调用是否正确。
6.0函数模板的重载
代码以及注释足以说明问题
一般不会转换实参以匹配已有的实例化,相反会产生新的实例(两次形参类型不一样的情况下)
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
template<typename T>
T Max(const T& left, const T& right)
{
cout<<typeid(a).name() << endl;
return left>right ? left : right;
}
template<typename T>
T Max(const T& a, const T& b, const T& c)
{
cout <<"重载:"<<typeid(a).name() << endl;
return Max(Max(a, b), c);
};
int Max(int left, int right)
{
return left>right ? left : right;
}
int main()
{
Max(10, 20, 30);
Max<>(10, 20);//告诉编译器调用模板函数
Max(10, 20);//优先调用非模板函数(有模板函数和普通函数的情况下)
//Max(10, 20.12);//没有与参数列表匹配的重载函数,模板函数不允许自动类型转换
Max<int>(10.0, 20.0);//等价于Max((int)10.0,(int)20.0);模板函数不允许自动类型转换
Max(10.0, 20.0);//等价于Max<double>(10.0, 20.0)
system("pause");
return 0;
}
7.0
特化(特殊化处理)
一般的模板函数不能处理字符串大小的比较,它比较的是p1和p2地址的大小。
特化形式如下:
template<>
返回值 函数名<Type>(参数列表)
{
// 函数体
}
返回值 函数名<Type>(参数列表)
{
// 函数体
}
特化的声明必须与特定的模板相匹配
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
template<class T>
T compare(T left,T right)
{
return left>right ? left : right;
}
template<>
char* compare<char*>(char* left, char* right)
{
if (1 == strcmp(left, right))
return left;
return right;
}
int main()
{
char* b = "sab";
char* a = "sah";
//cout<<compare(b,a)<<endl;/*调函数模板结果为 sab 比较的是a和b第一个字符的地址的大小*/
cout << compare(b, a) << endl;
system("pause");
return 0;
}
特化后的模板函数会被优先调用,与源模板函数相比较
8.0 类型形参的转化
代码一:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
template<class T>
void FunTest(T t)
{
cout <<typeid(T).name()<< endl;
}
void Add()
{}
int main()
{
int a[] = { 0, 1, 2 };
FunTest(a);
FunTest(Add);
system("pause");
return 0;
}
结果:
代码二:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
template<class T>
void FunTest(/*T */const T& t)
{
cout << typeid(T).name() << endl;
}
int main()
{
int a[] = { 0, 1, 2 };
FunTest(a);//
system("pause");
return 0;
}
结果:
1.0 代码一以及结果说明了测试函数里的参数为数组名时模板函数接收的是指向数组首元素的指针,测试函数里的参数为函数名时模板函数接收的是指向函数的指针。
2.0 代码二只把模板函数里的参数T 改为 const T& 结果就变了。
模板参数为T时,传的是a数组名它本来的类型为int[ ] 被编译器修改为 int *
模板参数为const T&时 ,传的是a数组名它本来的类型为int[ ] 被编译器修改为 int [ 3]
我认为:引用指向的是一个对象能具体就尽量具体( int a[ 3]),所以是整个数组类型自然就为 int [ 3]
2.0类模板
形式:template<class T1, class T2, ... ,class Tn>
class className
{ ... };
看一个实例: Vector容器
class className
{ ... };
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<assert.h>
using namespace std;
template<class T>
class Vector
{
public:
Vector()
: _start(NULL)
, _finish(NULL)
, _endOfStorage(NULL)
{}
Vector(T* array, int size)
: _start(new T[size])
{
for (int i = 0; i < size; i++)
_start[i] = array[i];
_finish = _start + size;
_endOfStorage = _finish;
}
Vector(const Vector<T>& v)
{
size_t size = v.Size();
size_t capacity = v.Capacity();
_start = new T[capacity];
for (size_t i = 0; i < size; i++)
_start[i] = v._start[i];
_finish = _start + size;
_endOfStorage = _start + capacity;
}
Vector& operator=(const Vector& v)
{
if (this != &v)
{
size_t capacity = v.Capacity();
size_t size = v.Size();
if (_finish - _start < size)
{
_start = new T[capacity];
}
for (size_t i = 0; i < size; i++)
{
_start[i]= v._start[i];
}
_finish = _start + size;
_endOfStorage = _statr+capacity;
}
return *this;
}
~Vector()
{
delete[] _start;
_start = _finish = _endOfStorage = NULL;
}
void PushBack(const T& data)
{
_CheckCapacity();
*_finish = data;
++_finish;
}
void PopBack()
{
assert(0 != Size());
_finish--;
}
void Insert(size_t pos, const T& data) //pos后插入
{
_CheckCapacity();
int count = Size() - pos; //count包括了pos以及它后面的元素
int i = 0;
while (count)
{
_start[Size() - i] = _start[Size() - i - 1];
i++;
count--;
}
_start[pos] = data;
++_finish;
}
void Erase(size_t pos)
{
T* position = NULL;
position = _start + pos;
size_t p = position-_start;
assert(0!=Size());
assert(p<Size());
size_t a = _finish - (_start + pos) - 1;//pos后面的有效元素个数
for (size_t i = 0; i < a;++i)
{
_start[pos + i] = _start[pos + i+1];
}
--_finish;
}
void Clear()
{
_finish = _start;
}
size_t Size()const
{
return _finish - _start;
}
bool Empty()const
{
return _start == _finish;
}
size_t Capacity()const
{
return _endOfStorage - _start;
}
void ReSize(size_t newSize, const T& data = T())
{
if (newSize > Size())
{
_CheckCapacity();
size_t i = 0;
for (i = Size(); i < newSize; i++)
{
_start[i] = data;
}
_finish = _start + newSize;
}
else
{
_finish = _start + newSize;
}
}
T& operator[](size_t index)
{
assert(index < Size());
return _start[index];
}
const T& operator[](size_t index)const
{
assert(index < Size());
return _start[index];
}
T& Front()
{
return *_start;
}
const T& Front()const
{
return *_start;
}
T& Back()
{
return *(finish - 1);
}
const T& Back() const
{
return *(finish - 1);
}
/*T* find(const T& data)
{
for (size_t i = 0; i < Size(); ++i)
{
if (_start[i] == data)
return _start + i;
}
}*/
friend ostream& operator<<(ostream& _cout, const Vector& l)
{
size_t count=0;
for (size_t i = 0; i < l.Size(); i++)
{
cout << l._start[i] << "-";
count++;
if(count % 5 == 0) //每行输出五个元素
{
cout << endl;
}
}
return _cout;
}
private:
void _CheckCapacity()
{
if (_finish == _endOfStorage)
{
size_t size = Size();
size_t newsize = 2 * Size() + 3;
T* temp = new T[newsize];
if (_start)
{
for (size_t i = 0; i < size; ++i)//效率低 1.0拷贝值
temp[i] = _start[i];
delete[] _start; //2.0 释放_start
_start = NULL;
}
_start = temp; //更换指向
_finish = _start + size;
_endOfStorage = _start + newsize;
}
}
private:
T* _start;
T* _finish;
T* _endOfStorage;
};
void VectorTest()
{
int a[] = { 1, 2, 1, 4, 5 };
Vector<int> s1(a,sizeof(a)/sizeof(a[0]));
Vector<int> s2=s1;
s2[2] = 3;
s2.ReSize(7, 177);
s2.Size();
s2.Capacity();
s2.PushBack(12);
s2.PopBack();
s2.Insert(4, 125); //插入为前插,pos位置在时s2的pos-1处。
s2.Erase(4);
cout <<s2<< endl;
}
int main()
{
VectorTest();
system("pause");
return 0;
}
结果: