c++模板
来源:互联网 发布:淘宝账号是什么 编辑:程序博客网 时间:2024/05/28 09:31
假设我们有如下 比较 Fun
bool IsEqual (const int& left , const int& right)
{
return left == right;
}
此时我们比较 int 值是否相等->函数调用-> isEqual( 1, 2 );
但如果我们想比较 两个 string 对象是否相等, 则需要重新写一个Fun:
bool IsEqual (const string& left , const string& right)
{
return left == right;
}
string s1 = "yuanyuanyuan";
string s2 = "zhaobaba";
->isEqual( s1, s2 );
这样我们每比较一种类型,就需要重新写一个比较函数,做了大量的重复工作。
c++为了提高代码复用性,提出了模板函数概念:
下面是一个 比较 模板函数框架:
template<typename/*or class*/ T>bool IsEqual( const T& left , const T& right )//传引用也是为了避免生成临时对象 or 临时变量 万一参数为string对象,则会拷贝构造生成临时对象,这里传引用是为了节省空间{return left == right;//如果不是内置类型而是自己定义的类型, 则必须实现即重载 operator==( )函数}T代表我们传参数时传入的类型, 我们只需要在模板函数中将参数写为T, 具体函数调用时,编译器会通过实参推演形参类型
void test1( ){string s1( "s1" ), s2( "s2" );cout << IsEqual( s1, s2 ) << endl;//调用不同Fun T分别为 string类型 与 int类型(编译器去做推演) 模板函数的实例化(编译器根据类型自动生成相应函数)cout << IsEqual( 1,1 ) << endl;//通过实参推演形参类型
//他们通过编译器实例化后是 重载函数 (模板实例化后生成代码(他们参数类型不同)所以重载)}
模板函数 不同类型 T 是调用不同函数.
如果有模板函数和具体类型函数,优先使用有参数的函数(非模板),没有才使用模板函数
例:
bool IsEqual (const int& left , const int& right)//如IsEqual( 1, 2 )优先调用它,而不是下面的Fun( ){return left == right;} template <typename T>bool IsEqual (const T& left , const T& right ){return left == right;}
模板参数匹配及显示实例化:
如第一种 只有一个模板参数T时 传 int 与 double 时, 不匹配 -> isEqual( 1, 1.2 );
template <typename T>bool IsEqual (const T& left , const T& right ){return left == right;} void test1 (){cout<<IsEqual (1,1)<<endl;cout<<IsEqual(1,1.2)<<endl; // 模板参数不匹配
//下面是一种解决办法:
//templat<class T1, class T2> ( const T1& left , const T2& right )
//万一 left 传 int, right 传 double 第一种会出错, 此时必须这样定义( 第一种解决办法,另一种方法是显示实例化 )cout<<IsEqual<int>(给定类型 编译器不会推演,直接生成该类型代码)(1,1.2)<< endl; // 显示实例化(第二种办法)(T规定为int)cout<<IsEqual<double>(1,1.2)<< endl; // 显示实例化}没有对象实例化时,模板函数编译器不生成响应代码 , 证明:
template <typename T>bool isequal (const t& left , const t& right ){return left == right//这没有“;”时 编译通过, 并没有错,因为没生成响应代码}
不调用时:函数内部不进行语法检查
但 函数外部 会进行格式检查 如 bool IsEqual ( const T& x, const T& y 少个括号 编译时还是会报错的
模板函数,把属于我们的工作,从实参推演形参,交给编译器去做
说完了模板函数,我们来说说模板类。 同样的问题不只存在于模板函数中,模板类中也存在.
如 类中数据成员的 类型.
但模板类不能自己推演, 不能根据你传的参数推演T的类型,必须显示实例化!
需要注意的是模板类 类型为ClassName<T>, 类名为ClassName
但普通类, 类名和类类型相同
构造函数还有拷贝构造函数名字和类名相同
But拷贝构造Fun()参数(这个类类型必须加上类型T):
ClassName( const ClassName<T>& s ); 经测试有无<T> 无影响
.h中声明
声明定义分离 标准!类中只有声明
.cpp中定义
类外面定义声明在类中的函数时:
类外定义时:
template<class T> //再写一次 模板
void SeqList<T>::Print( ) //然后注意,这里的返回类型 为 SeqList<T> 类类型需要加<T>
{
...;
}
是T就加引用, 万一T是string , 深拷贝代价太大, 不改变就加 const ->const T&
拷贝构造参数不给引用会循环递归! 想想因为->拷贝构造未完成,所以会一直构造参数,递归死循环。 必须传引用
调试 时 栈溢出错误, 一般都是(递归)死循环
存在才赋值(赋值运算符重载), 不存在就拷贝构造. s1 = s2 s1,s2均存在属于第一种情况。 string s2 = s1. s2不存在属于第二种情况。
有了类模板参数后,我们就可以实现容器适配器:
队列:队尾进,对头出,插入在队尾
栈:栈顶进, 栈底->栈顶
适配器模式实现一个队列:
使用 List 实现容器适配器:
template<class T, class Container>class Queue{public:void Push( const T& x );{_con.PushBack( x );}void Pop( ){_con.PopFront( );}T& Front( )//根据要实现的函数功能(此处为队列),再决定在里面调用容器(此处为List)的什么函数{return _con.Front( );}T& Back( )//因为出了作用域还存在! 所以T&{return _con.Back( );}size_t Size( ){return _con.Size( );}bool IsEmpty( ){return _con.IsEmpty( );}protected:Container _con;};void TestQueue( ){Queue<int, List<int>> q;//我们实现一个 List 容器(类模板List), 并且List中 有声明我们使用的Fun()
q.Push( 1 );while (!q.IsEmpty( )){cout << q.Front( ) << " ";q.Pop( );}}
template<class T, class Container = List<T>>//Stack<int> s 这样也可以 模板的缺省参数class Stack{public:void Push( const T& x ){_con.PushBack( x );}T& Top( )//因为栈后进先出,所以Top是最后进来的, 所以是返回Back( ).{return _con.Back( );}protected:Container _con;};void TestStack( )//若是链表,删除时 释放空间,插入又开空间。 不停释放开辟不好,但顺序表不需要, 原空间反复利用, 它删除只是 --size{Stack<int, SeqList<int>> s;s.Push( 1 );while ( !s.IsEmpty( ) ){cout << s.Top( ) << " ";s.Pop( );}}
模板的模板参数:接上面,若这样传参数,Stack<int, SeqList<char>> s; 也就是这个 char 传错 会存在隐患。
为了避免错误,我们进行改进:template<class T, template<class(不用传参数,和T类型一样)>class Container = SeqList(缺省的模板的模板参数)> (传一个类名,而不是传一个类型)。 避免传char等 出错。
例: Stack<int, List> s;
const size_t N = 100;
template<class T>
class SeqList
{
protected:
T _a[N];
size_t size;
};
非类型模板参数:
现在生成对象, _a大小只能是200。 不管定义几个。 为了解决这个问题 -> 非类型模板参数。 N是一个常量,可以给缺省参数 它也可以做模板函数的非类型模板参数。
template<class T(类型模板参数),size_t N = 200>
SeqList<int, 200> s1;
SeqList<double, 2000> s2;
浮点数和类对象不允许做非类型模板参数
模板的特化:
1.全特化::
template <typename T>class SeqList{ public :SeqList();~ SeqList();private :int _size ;int _capacity ;T* _data ;};template<typename T>SeqList <T>:: SeqList(): _size(0), _capacity(10), _data(new T[ _capacity]){cout<<"SeqList<T>" <<endl;}template<typename T>SeqList <T>::~ SeqList(){delete[] _data ;}
template <>class SeqList <int>{ public :SeqList(int capacity);~ SeqList();private :int _size ;int _capacity ;int* _data ;};
SeqList <int>:: SeqList(int capacity): _size(0), _capacity(capacity ), _data(new int[ _capacity]){cout<<"SeqList<int>" <<endl;} // 特化后定义成员函数不再需要模板形参SeqList <int>::~ SeqList(){delete[] _data ;} void test1 (){SeqList<double > sl2;SeqList<int > sl1(2);//优先调用特化部分(调用才生成代码,没有特化的,才是T)}
template <typename T1, typename T2>class Data{ public :Data();private :T1 _d1 ;T2 _d2 ;};template <typename T1, typename T2>Data<T1 , T2>:: Data(){cout<<"Data<T1, T2>" <<endl;}局部特化第二个参数:
template <typename T1>class Data <T1, int>{public :Data();private :T1 _d1 ;int _d2 ;};template <typename T1>Data<T1 , int>:: Data(){cout<<"Data<T1, int>" <<endl;}
ps:下面的例子可以看出,偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本。
局部特化两个参数为指针类型
template <typename T1, typename T2>class Data <T1*, T2*>{ public :Data();private :T1 _d1 ;T2 _d2 ;T1* _d3 ;T2* _d4 ;};template <typename T1, typename T2>Data<T1 *, T2*>:: Data(){cout<<"Data<T1*, T2*>" <<endl;}
局部特化两个参数为引用:
template <typename T1, typename T2>class Data <T1&, T2&>{ public :Data(const T1& d1, const T2& d2);private :const T1& _d1;const T2& _d2;T1* _d3 ;T2* _d4 ;};template <typename T1, typename T2>Data<T1&, T2&>:: Data(const T1& d1, const T2& d2): _d1(d1 ), _d2(d2 ){cout<<"Data<T1&, T2&>" <<endl;} void test2 (){Data<double , int> d1;Data<int , double> d2;Data<int*, int*> d3;Data<int&, int&> d4(1, 2);}模板的全特化和偏特化都是在已定义的模板基础之上,不能单独存在。
template <typename T1>//如果都是具体类型 则 template<>里面啥都不用写,因为不是模板参数,都是具体类型Date<T1, int>:: Date( )//第二个参数特化为 int 类型{;}
template <typename T1, typename T2>class Data<T1*, T2*>调用:Data<int*, double*>template <typename T1, typename T2>class Data<T1, T2*>调用:Data<int, double*>
1.全特化:template<class T> class Name2.偏特化:template<> class Name<int>
template<class T> class Name<T*>
1.进一步条件限制模板参数
2.多个模板参数时,特化部分参数
编译错误:语法
链接错误:找不到具体定义它地方
模板的分离编译:声明,定义分离
ClassName.h声明
ClassName.cpp实现(这样则链接错误)
Test.cpp测试
template<class T>
void Data<T>::F( )
{}
非模板类是可以分离编译的
模板不支持分离编译:
分离编译后,找不到定义!找不到cpp中的定义,因为大家分开编译(main cpp) 所以, cpp中模板未实例化,未生产相应代码。所以链接错误!
模板实例化才生成具体代码,才语法检查(但并不是不实例化时什么语法都不检查!)
main.cpp
Data.h
Data.cpp
预处理(预编译)(展开头文件,替换宏,去注释,条件编译,处理行号文件名函数名(__FILE__(也是一个宏)等))->main.i 和 Data.i
编译(检查语法错误,)->main.s 汇编代码 一句代码对应多句汇编代码 ++i (mov eax, i) -> (add eax) -> (mov i, eax) cpu不能识别汇编代码 main.s Data.s
汇编 汇编代码->二进制机器码(这是一个过程)main.o Data.o 符号表(函数名+地址 F:0x1122)
链接 ->.exe(Windows) a.out(Linux) 可执行文件
函数变成指令,第一句指令即Fun 地址
分离编译->链接错误(没有找到定义->函数的地址)
非模板类 .cpp .h
模板类 .hpp(声明定义放一起)
.hpp:
//分离编译template<class T>class Data{public:void F( );protected:T _d;};template<class T>void Data<T>/*类型*/::F( ){;}
- C++-模板
- C++-模板
- C++:模板
- C++:模板
- C++:模板
- 模板 (C++)
- C ++ 模板
- C ++ 模板
- 【C++】模板
- c++----------模板
- C++--------------------------------------------模板
- c++--模板
- C++:模板
- 【基础C&C++】模板
- 【c/c++】类模板
- 【C/C++】模板类
- 【c++】模板和模板类
- C/C++:函数模板与类模板
- 编译预处理
- 小鑫去爬山
- webstorm配置和使用
- 解决javaWEB中前台传数据到后台中文乱码问题的3种方法
- c/c++ No.18 字符串连接
- c++模板
- hashcode的作用
- js边框设置
- Linux Tsung压力测试安装说明
- js无缝滚动文字
- A new multimedia work, The Colorado will unite art, history, sociology and ecology on Stanford’s Bin
- 第九周:( LeetCode337) House Robber III(c++)
- HttpServletResponse介绍
- eclipse编码方式的几种修改方式