C++模板学习3
来源:互联网 发布:js截取小数点后两位 编辑:程序博客网 时间:2024/05/21 06:33
今天看书的地方是关于类模板的特化,在实际的使用中,可以使用类模板实参来特化类模板,这种实现与函数模板的重载类似,
通过特化类模板,就可以优化基于某种特定类型的实现,
或者是客服某种特定类型在实例化类模板的时候所出现的不足。需要注意的是如果要特化一个类模板的话,还需要特化在该类模板中多定义的所有的成员函数。
如果仅仅是特化类模板中的某个成员函数的话, 这种做法并没有特化整个类中的模板,所以这种特化部分成员函数的做法病不能叫做特化整个模板类。
下面我们来看看应该如何特化一个模板类吧。
1,类模板的特化
首先,为了特化一个类模板,你必须在起始处声明一个template<> , 接下来声明用来特化类模板的类型。这个类型将会被用作是模板实参,
并且这个类型必须要在后面的代码中进行明确的指定。
template <>class Stack<std::string>{...}
在类模板的特化的时候,每个成员函数中的实现算子都必须重新定义为普通函数,原来在模板函数中的每个 T
也是需要被进行类模板特化的类型来进行取代的:
void Stack<std::string>::push( std::string const & elem){ elems.push_back( elem ) ;//push the reference of elem with the type of std::sting into the stack}
下面我们来看使用std::string 特化 Stack<> 的完成代码实现:
代码总共分为两个部分,一个部分是实现stack1的头文件, 另一个部分是根据stack1中定义的方法来进行其中定义的template来进行模板进行特化。
//stack1.hpp#include <vector>#include <stdexcept>template <typename T >class Stack{private:std::vector<T> elems ;public :void push ( T const & ) ;void pop ( ) ;T top () const ;bool empty () const {return elems.empty() ;}} ;template <typename T >void Stack<T>::push( T const &elem ){elems.push_back( elem ) ;}template <typename T>void Stack<T>::pop () {if ( elems.empty() ){throw std::out_of_range("Stack<>::pop() : empty stack ") ;}elems.pop_back() ;}template <typename T>T Stack<T>::top () const {if ( elems.empty() ){throw std::out_of_range("Stack<>::top : empty stack ") ;}return elems.back() ;//method to return the last element pushed into the stack }
//stack2.hpp
#include <deque>#include <string>#include <stdexcept>#include <iostream>#include "stack1.hpp"template <>class Stack<std::string>{private :std::deque<std::string> elems ;//the capacity contains the elementspublic :void push ( std::string const & ) ; //method to push elementvoid pop( ) ;//method to pop elementsstd::string top () const ;bool empty () const{return elems.empty() ;}} ;void Stack<std::string >::push( std::string const & elem ){elems.push_back( elem ) ;}void Stack<std::string>::pop() {if ( elems.empty() ){throw std::out_of_range("Stack<std::string>::pop() : empty stack ") ;}elems.pop_back() ;}std::string Stack<std::string>::top () const{if ( elems.empty () ){throw std::out_of_range("Stack<std::string>::top() : empty stack ") ;}return elems.back () ;}int main (){Stack<std::string> s;s.push("hello ") ;s.push("inuyasha") ;s.push("!") ;while (!s.empty()){std::string tempC = s.top() ;std::cout<<tempC<<std::endl ;s.pop() ;}system("pause") ;return 0 ;}
在上面的例子里面,我们使用一个deque,而不是vector,来对stack内部的元素进行管理。我们使用这种用法并不在于获得某种好处,而只是为了说明:
特化的实现可以和基本类模板(prinmary template)的实现是有所不同的。
<在这里需要说明一点的就是, 使用dequeu来代替vector 实现一个stack是有一定的好处的, 因为当删除元素的时候,
deque会对自己的内存空间进行释放,当需要重新分配内存的时候,deque 的元素并不需要被移动。
然而,这种好处对于string类型的变量是并不起任何作用的。正是因为这个原因,在基本的类模板中,
使用deque来作为内部使用的容器通常是一个很好的注意,但是如果想要作为特殊类型对象的内部容器的话,还是需要考虑一下的。>
2. 类模板的局部特化
在介绍了类模板的特化之后,下面来学习一下局部特化,类模板也是可以被局部特化的,即可以再特定的环境下指定类模板的特定的实现,
并且要求某一些模板参数仍然必须有用户自己来定义,
对于一个模板可以针对模板中的不同参数进行不同的特化,具体的操作请看下面的这个例子:
template <typename T1, typename T2>class MyClass{} ;//局部特化: 两个模板参数具有相同的类型template <typename T >class MyClass<T , T>{} ;//局部特化: 第二个模板参数的类型是 inttemplate <typename T>class MyClass< T, int >{} ;//局部特化: 两个模板参数都是指针类型的template <typename T, typename T1>class MyClass<T* , T1*>{} ;
下面的这个例子展示了在各种的声明中会使用到哪一个模板:
MyClass<int , float> mif ; //this will use the MyClass< T1 , T2 > MyClass<float, float> mff ; //this will use the MyClass< T, T>MyClass<float, int > mfi ; //this will call the MyClass<T , int >MyClass<int* , float*> mp ; //this will call the MyClass<T*, T1*>
如果有多个局部特化同等程度低匹配某个声明,那么就声称这个声明是具有二义性的:
MyClass <int , int > m ;
//call MyClass <T , int > and MyClass <T,T>Myclass <int* , int*> mpp;
//call both MyClass <T*, T1*> and MyClass <T, T>
为了在实际的编写代码的是偶解决这种二义性,可以另外提供一个指向相同类型的特化:
template<typename T>class MyClass<T* , T* >{...} ;
最后一个要介绍的就是,缺省模板实参
对于我们常使用的模板类,除了对其进行模板参数进行特化或是部分特化以外,还可以为其定义缺省值;
这些定义的模板参数被定义为缺省值的话,那么这些值就被称为模板实参;
而且它们还可以引用之前的模板函数;
例如,在类Stack<>中,可以把用于管理元素的容器定义为第二个模板参数,并且使用std::vector<> 来作为它的缺省值,
下面我们来一起看一下实现缺省值模板实参的代码吧:
//basics/stack3.hpp#include <vector>#include <stdexcept>template <typename T , typename CONT = std::vector<T> >class Stack{private :CONT elems ;public :void push( T const & ) ;void pop () ;T top () const ;bool empty () const {return elems.empty () ;}} ;template <typename T , typename CONT >void Stack<T,CONT>::push ( T const & elem ){elems.push_back(elem ) ;}template <typename T , typename CONT >void Stack<T, CONT>::pop (){if ( elems.empty() ){throw std::out_of_range("Stack<T,CONT>::pop() : empty stack" ) ;}elems.pop_back() ;}template < typename T , typename CONT >T Stack<T, CONT>::top () const {if ( elems.empty() ){throw std::out_of_range("Stack<T, CONT>::top() : empty stack ") ;}return elems.back () ;}
template <typename T , typename CONT >void Stack<T,CONT>::push ( T const & elem ){elems.push_back(elem ) ;}
还可以执行在程序里面用来更改底层的用来存放Stack 的数据结构,
即可以在Stack数据结构的定义中,可以通过此种方式来指定存放Stack中底层数据的容器类型。 具体的调用方法可以通过下面的代码来判断出来:
//博客中的程序均已经调试成功,并显示出正确的结果
//basics/stack3.hpp#include <vector>#include <stdexcept>#include <iostream>#include <deque>#include <cstdlib>template <typename T , typename CONT = std::vector<T> >class Stack{private :CONT elems ;public :void push( T const & ) ;void pop () ;T top () const ;bool empty () const {return elems.empty () ;}} ;template <typename T , typename CONT >void Stack<T,CONT>::push ( T const & elem ){elems.push_back(elem ) ;}template <typename T , typename CONT >void Stack<T, CONT>::pop (){if ( elems.empty() ){throw std::out_of_range("Stack<T,CONT>::pop() : empty stack" ) ;}elems.pop_back() ;}template < typename T , typename CONT >T Stack<T, CONT>::top () const {if ( elems.empty() ){throw std::out_of_range("Stack<T, CONT>::top() : empty stack ") ;}return elems.back () ;}int main (){try{Stack<int> intStack ;//double stack ,managed by the std::dequeStack <double , std::deque<double> > dblStack ;intStack.push( 7 ) ;std::cout<<intStack.top() <<std::endl ;intStack.pop() ;//usages of the double stackdblStack.push( 42.42 ) ;std::cout<<dblStack.top() <<std::endl ;dblStack.pop() ;Stack<char , std::vector<char> > charStack ;charStack.push('k') ;charStack.push('o') ;charStack.push('k' ) ;charStack.push('i') ;charStack.push('a') ;while ( !charStack.empty() ){std::cout<<charStack.top() <<std::endl ;charStack.pop() ;}}catch ( std::exception const & ex ){std::cerr<< "Exception :" <<ex.what() << std::endl ;return EXIT_FAILURE ;}system("pause") ;return 0 ;}
最后,对开始学习类模板中的知识点进行总结:
类模板是具有一下性质的类: 在类的实现中,可以有一个或是多个类型还没有被指定,这些类型的指定根据使用者在编写代码中进行指定,并且具体的绑定时期是在程序运行时动态绑定的。
为了使用类模板,可以传入某个具体类型来作为模板实参; 然后编译期将会基于该类型来实例化对应类模板所指定的类实例
可以使用某种特定类型特化类模板
可以使用某种特定的类型来局部化类模板
可以为类模板的参数指定缺省值,并且这些值还可以引用之前的模板参数。
参考书目《C++ Template 中文版》
今天先到这里,今天开始做高中数学题了,希望自己的思维能灵光一些(笑) ~
快要分布式期末考试了,希望一切顺利。
0 0
- C++template ;模板学习
- 学习C++模板---模板函数
- C/C++学习之模板
- C++primer学习:模板编程(3):效率与灵活
- C++ Template学习笔记之函数模板(3)——模板实参推演
- C++ Template学习笔记之函数模板(3)——模板实参推演
- 学习C++模板---模板类带简单参数
- 学习C++模板---模板类作为基类
- C++primer学习:模板编程(2):类模板的定义
- C++primer学习:模板编成(5):模板实参推断{1}
- C++primer学习:类模板(2)类模板:模板参数,成员模板和控制实例化
- C++Template学习笔记之函数模板
- C/C++学习----第五章 模板
- C++模板 学习要点
- (C/C++学习笔记)函数模板加强
- C++primer学习:模板编程(4)
- C++Primer学习:模板特例化
- C++模板学习3
- xampp mysql 优化最好心得
- 《C专家编程》学习笔记(分析C语言的声明)
- 实时大单[博客]名博:五月买两种股最赚钱锤
- 如果在android项目中试用Jnit做测试
- PWM(脉宽调制)的基本原理及其应用实例
- C++模板学习3
- 創建同義詞
- .bash_profile和.bashrc的区别(如何设置生效)
- android 视频通话相关
- 使用ajax请求servlet返回json格式字符串
- C# 串口通信相在资料链接(转贴) .
- Basin hopping是什么全局优化算法?
- 火水未濟
- 关于用excel导入数据到mysql