浅谈模板函数和模板类以及不使用乘除,for,while,等循环和递归来计算1+2+3+4+....+n的值

xiaoxiao2021-02-28  12

模板函数

概念:一种与类型无关的代码. 作用:复用. 下面通过一个实例来实现:

template<class T> //类比函数参数,但是模板参数传递的是类型,也可以使用typename T Add(T a,T b) { return a + b; } int main() { int a = 10; int b = 20; cout<<Add(a,b)<<endl; //Add<int>(a,b); //显示实例化,当add函数的参数类型不同时,就会在推演类型时出错,所以此时就用到了显示实例化,明确T的类型 float c = 1.1223; float d = 2.123; cout<<Add(c,d)<<endl; return 0; }

那么是怎么实现的呢?只要想探索底层的实现原理,有一个简便的方法就是:查看汇编. 此时,我们查看汇编: 所以:编译器在编译时,会进行模板类型的推演,然后模板函数在执行时会生成不同的函数名,进而实现函数的复用. 注意:模板函数只有在调用的时候才会进行检查.如果只写模板函数是不会检查函数体是否有错误

模板函数重载

template<class T> //类比函数参数,但是模板参数传递的是类型 T Add(T a,T b) { return a + b; } int Add(int a,int b) { return a + b; } int main() { int a = 10; int b = 20; Add(a,b); //调用非模板函数 float c = 1.1223; float d = 2.123; Add(c,d); //调用的是模板函数 return 0; }

函数名相同,且在同一作用域,所以上面两个函数构成了重载.

模板类

如果要看Vector的模板实现的代码,请戳这里 模板类与模板函数相似,在这儿,我们可以通过顺序表Vector来实现模板类.

template <class T> class Vector { public: Vector(); Vector(const Vector<T>& v); Vector<T>& operator=(const Vector<T>& v); void PushBack(const T& value); void PopBack(); void Insert(size_t pos,const T& value); void Erase(size_t pos); size_t Size(); size_t Capacity(); bool Empty(); ~Vector(); protected: T* _first; T* _finish; T* _endofstorage; };

敲黑板:在模板中.类名就是上例中的Vector;类型而是Vector;而在普通的类中,类名和类型相同. 当在类外面进行实现时,与我们以往写的声明是不同的.

template<class T> Vector<T>::Vector() //注意作用域 :_first(NULL) ,_finish(NULL) ,_endofstorage(NULL) {} template<class T> //必须写 Vector<T>::~Vector() { delete[] _first; _first = _finish = _endofstorage = NULL; }

当在创建对象使用时,它的声明是:

Vector<int> v;

经常使用的数据结构还有带头双向链表. 带头双向链表代码的实现

template<class T> struct LinkNode { T data; LinkNode<T>* prev; LinkNode<T>* next; LinkNode(const T& value) :data(value) ,prev(NULL) ,next(NULL) {} }; template<class T> class LinkList { public: typedef LinkNode<T> Node; LinkList(); LinkList(const LinkList<T>& l); LinkList<T>& operator=(const LinkList<T>& l); void PushFront(const T& value); void PopFront(); void PushBack(const T& value); void PopBack(); void Insert(Node* pos,const T& value); void Erase(Node* pos); void Clear(); ~LinkNdoe(); bool Empty(); protected: Node* head; };

模板参数——>容器适配器

在生活中,我们笔记本电脑的充电器,也叫适配器(实现电压的转化). 同样的,在我们的程序中,也可以通过某种代码去实现其它的功能.比如:用Vector去模拟实现栈.

template<class T,class Container> //template<class T,class Container = Vector<T> > //缺省参数 class Stack { public: void Push(const T& value) { con.PushBack(value); } void Pop() { con.PopBack() } T& Top() { con.Front(); } bool Empty() { con.Empty(); } protected: Container con; }; int main() { Stack<int,class<int> > s; return 0; }

模板的模板参数

与上面的适配器的实现类比:

template<class T,template<class T> class Container > //template<class T,template<class T> class Container = Vector<T>> class Stack { public: void Push(const T& value) { con.PushBack(value); } void Pop() { con.PopBack() } T& Top() { con.Front(); } bool Empty() { con.Empty(); } protected: Container<T> con; }; int main() { Stack<int,Vector> s; return 0; }

template<class T,template<class T> class Container> 其中:template<class T>表示Container是一个模板类类型的模板参数.

非类型的类模板参数

模板类的模板参数,也可以飞类型的模板参数. 比如:

template<class T,size_t N>

利用这个可以实现静态的顺序表. 注意:double是不可以做非类型的模板参数.

非类型的函数模板参数

template<class T,int value> T Add(const T& a) { return a + value; } int main() { cout<<Add<int,3>(10)<<endl; return 0; }

类模板的特化

使用日期类来举例学习:

template<typename T1,typename T2> class Date { public: Date() { cout<<"Date()"<<endl; } private: T1 _d; T2 _d1; }; template<typename T1,typename T2> class Date<T1& ,T2&> { public: Date(T1 a,T2 b) { cout<<"Date<T1&,T2&>"<<endl; } private: T1& _d1; T2& _d2; }; template<typename T1,typename T2> class Date<T1*,T2*> { public: Date() { cout<<"Date<T1*,T2*>"<<endl; } private: T1* _d1; T2* _d2; }; //第二个参数偏特化 template<typename T1> class Date<T1,char> { public: Date() { cout<<"Date<T1,char>"<<endl; } private: T1 _d1; char _d2; }; //第一个参数偏特化 template<typename T2> class Date<char,T2> { public: Date() { cout<<"Date<char,T2>"<<endl; } private: char _d1; T2 _d2; }; template<> //全特化 class Date<int,int> { public: Date() { cout<<"Date<int,int>"<<endl; } private: int _d1; int _d2; }; int main() { Date<int,int> d; Date<char,int> d1; Date<double&,double&> d2(1.11,2.22); Date<int*,int*> d3; return 0; }

总结:当该类型的模板函数实现了时,在调用时就会调用(偏特化模板函数)实现了的对应的函数.当该函数没有实现时,就会调用模板函数.注意:当类的成员变量为引用类型时,在初始化类时对于成员变量也要初始化.(引用必须初始化)

注意:模板的全特化和半特化都是在已定义的模板的基础之上,不能单独存在.

模板的分离编译

当模板的定义在.h文件中,而声明在.c文件中,而测试函数又在另外一个文件中时,在编译时可以通过吗?

总结:模板不支持分离编译,最好的做法就是,直接在模板的定义后面进行模板的实现. 如果定义和声明不在同一个文件中,那么在执行函数时就会直接编译不通过,链接失败.因为模板函数只是在执行的时候才会去推演它的类型,才会去找实现的函数,当它们不在同一个文件时,就会因为找不到而报错.

模板总结: 优点: 1. 提高了代码的复用性,节省资源. 2. 增强代码的灵活性. 缺点: 1. 不易维护,让代码变得复杂. 2. 当模板使用错误产生错误信息时,不容易找到错误信息的根源.

模板的萃取

我们发现,在内置类型的拷贝或赋值与自定义类型是不同的,自定义类型的不能用memcpy函数直接去拷贝,而是要自己去手动实现这个过程.怎么实现在调用的时候可以及时的找到对应的类型呢? 答案是通过萃取的方法可以实现. 下面是一个小小的例子:

//模板的萃取 #include<string> struct __TrueType {}; struct __FalseType {}; template<class T> struct TypeTraits { typedef __FalseType IsPodType; }; template<> struct TypeTraits<int> { typedef __TrueType IsPodType; }; template<> struct TypeTraits<char> { typedef __TrueType IsPodType; }; template<class T> T* __TypeCopy(T * dst,const T * src,size_t n,__FalseType) { size_t i=0; for(i=0;i<n;++i) { dst[i]=src[i]; } cout<<"operator=()->"<<endl; return dst; } template<class T> T* __TypeCopy(T * dst,const T * src,size_t n,__TrueType) { cout<<"memcpy->"<<endl; return (T*)memcpy(dst,src,sizeof(T)*n); } template<class T> void TypeCopy(const T* src,T* dest,size_t n) { __TypeCopy(dest,src,n, typename TypeTraits<T>::IsPodType()); } //特化+内嵌----->特化 void Test() { int arr[5] = {1,2,3,4,5}; int arr1[5] = {0}; std::string str[4] = {"aaaa","bbbb","cccc","dddd"}; std::string str1[4] = {""}; TypeCopy(arr1,arr,5); //首先根据对象的类型去推演T的类型-->__TrueType, //含有该类型的特化版本,就会去掉用它的拷贝方法 TypeCopy(str1,str,4); //过程:由str1,str去推演T的类型--->__FalseType-->调用它的拷贝过程 }

下面是一道拓展思维的题: 不使用乘除,for,while,等循环和递归来计算1+2+3+4+….+n的值; 方法1:

class Add_1_to_n { public: Add_1_to_n() { ret = ret + i; ++i; } static int sum; static int i; }; int Add_1_to_n::sum = 0; int Add_1_to_n::i = 1; void Test() { Add_1_to_n* p = new Add_1_to_n[10]; cout<<Add_1_to_n::sum<<endl; delete[] p; }

方法2:在编译期间就已经解决问题. 缺点;当n太大时,就会因为递归的层数太深而程序崩溃,无法计算出来

tempalate<size_t N> class Add_1_to_n { public: enum { sum = Add_1_to_n(N - 1)::sum + N; }; }; template<> class Add_1_to_n<1> { public: enum { sum = 1; }; }; void test { cout<<Add_1_to_n<10>::sum<<endl; }

方法3:鬼畜方法(反正我是想不到~~~) 缺点:因为栈比较小,所以当N太大时,就会导致栈溢出

char arr[N][N + 1] = {0}; cout<<(sizeof(arr))>>1)<<endl;

若读者有更好的方法:请指教

转载请注明原文地址: https://www.6miu.com/read-2150141.html

最新回复(0)