如何实现一个通用的函数呢?(如针对不同的参数类型均可)
在平时的编程中,如何实现一个通用的函数呢?
首先想到的就是使用函数重载,但是这样一来会有几个不好的地方:
- 重载的函数仅仅只是类型不同,代码的复用率比较低,只要有新类型出现时就需要增加对应的函数
- 代码可维护率比较低,一个函数出错就有可能所有的重载都出错
那么有没有一种方式,使得编译器可以根据不同的类型来利用该模子来生成代码呢?
答案是有的——泛型编程,也就是一种编写与类型无关,与使用场景无关的通用代码,使得代码可以复用的手段,而模板则是泛型编程的基础
模板:
模板是C++支持参数化多态的工具,使用模板可以使用户为类或者函数声明一种一般模式,使得类中的某些数据成员或者成员函数的参数、返回值取得任意类型。
- 模板是一种对类型进行参数化的工具;
- 通常有两种形式:函数模板和类模板;
- 函数模板针对仅参数类型不同的函数;
- 类模板针对仅数据成员和成员函数类型不同的类。
使用模板的目的就是能够让程序员编写与类型无关的代码。比如编写了一个交换两个整型int 类型的swap函数,这个函数就只能实现int 型,对double,字符这些类型无法实现,要实现这些类型的交换就要重新编写另一个swap函数。使用模板的目的就是要让这程序的实现与类型无关,比如一个swap模板函数,即可以实现int 型,又可以实现double型的交换。模板可以应用于函数和类。下面分别介绍。
注意:模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。
函数模板
是一个函数家族,与类型无关,使用时才被参数化,根据实参类型产生函数的适用版本
函数模板格式
template<class T1,class T2> //模板参数列表 可以使用template ,不可以使用struct
T1 _Add(T1 left,T2 right) //函数模板
{
return left + right;
}
实例化:
隐式实例化:
编译器根据实参推演模板参数的实际类型
显示实例化:
函数名和后加<>指定参数实际类型
匹配原则:
1.优先调用自己写的模板函数
2.模板函数不允许自动类型转换,普通函数可以自动类型转换
#pragma once
#include<assert.h>
#include<iostream>
using namespace std;
//不是一个真正的函数
template<class T> //模板参数列表 可以使用template<typename T> ,不可以使用struct
T _Add(T left,T right) //函数模板
{
return left + right;
}
template<class T>
void PrintfArray(T array,int size) //数组会自己转换类型
{
cout<<typeid(array).name() <<endl;
int i = 0;
for(i=0;i<size;i++)
{
cout<< array[i] << " ";
cout<<endl;
}
}
int main()
{
int array[] = {1,2,3,4,};
char str[] = "hello";
PrintfArray(array,sizeof(array)/sizeof(int));
PrintfArray(str,strlen(str));
//隐式实例化---不会进行隐式的类型转化,需要用户自己来强转/
cout<< _Add(1,2) <<endl; //根据实参类型来进行类型推演生成处理具体类型的函数
cout<< _Add(1,(int)2.0) <<endl; //但是面对参数为不同类型时无法判断如何输出,需要对参数进行处理,如强转类型
//显式实例化
_Add<int>(1,2.2);
_Add<>(1,2); //隐式实例化
return 0;
}
用模板写list类
template<class T>
class Seqlist
{
public:
Seqlist(int capacity = 10)
:_Data(new T[capacity])
,_capacity(capacity)
,_size(0)
{}
//拷贝构造:Seqlist(const Seqlist<T>& s);
//赋值运算符重载:Seqlist<T>& operator = (const Seqlist<T>& s)
void PushBack(const T& data)
{
_CheckCapacity();
_Data[_size++] = data;
}
void PopBack()
{
if(_size != 0)
--_size;
}
//访问任意位置
T& operator[](size_t index) //可能会修改s[i]中的元素
{
assert(index < _size);
return _Data[index];
}
//因为const类型的对象不能调用普通函数,这里是const对象访问
const T& operator[](size_t index) const
{
assert(index < _size);
return _Data[index];
}
//访问第一个元素
T& Front()
{
return _Data[0];
}
const T& Front()const
{
return _Data[0];
}
//访问最后一个元素
T& Back()
{
return _Data[_size-1];
}
const T& Back()const
{
return _Data[_size-1];
}
size_t Size() const
{
return _size;
}
size_t Capacity() const
{
return _capacity;
}
void Clear()
{
_size = 0;
}
~Seqlist();
private:
void _CheckCapacity()
{
if(_size == _capacity)
{
size_t newCapacity = (_capacity<<1);
//申请空间
T* Temp = new T[newCapacity];
//拷贝元素
memcpy(Temp,_Data,_size*sizeof(T));
//释放旧空间
delete[] _Data;
//替换新空间
_Data = Temp;
_capacity = newCapacity;
}
}
private:
T* _Data;
size_t _capacity;
size_t _size;
};
template<class T>
Seqlist<T>::~Seqlist()
{
if(_Data)
{
delete[] _Data;
_Data = nullptr;
_capacity = 0;
_size = 0;
}
}
void TestSeqlist()
{
Seqlist<int> s1;
Seqlist<double> s2;
Seqlist<char> s3;
s1.PushBack(1);
s1.PushBack(2);
s1.PushBack(3);
s1.PushBack(4);
s1[0] = 6;
cout<< s1.Size()<<endl;
cout<< s1.Front()<<endl;
cout<< s1.Back()<<endl;
s1.Clear();
cout<<s1.Size()<<endl;
}
#include<string>
void Test()
{
Seqlist<string> s;
s.PushBack("0000");
s.PushBack("1111");
s.PushBack("2222");
s.PushBack("3333");
s.PushBack("4444");
s.PushBack("5555");
s.PushBack("6666");
s.PushBack("7777");
s.PushBack("8888");
s.PushBack("9999");
}
int main()
{
//TestSeqlist();
Test();
return 0;
}