今天看书的地方是关于类模板的特化,在实际的使用中,可以使用类模板实参来特化类模板,这种实现与函数模板的重载类似,
通过特化类模板,就可以优化基于某种特定类型的实现,
或者是客服某种特定类型在实例化类模板的时候所出现的不足。需要注意的是如果要特化一个类模板的话,还需要特化在该类模板中多定义的所有的成员函数。
如果仅仅是特化类模板中的某个成员函数的话, 这种做法并没有特化整个类中的模板,所以这种特化部分成员函数的做法病不能叫做特化整个模板类。
下面我们来看看应该如何特化一个模板类吧。
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 elements
public :
void push ( std::string const & ) ; //method to push element
void pop( ) ;//method to pop elements
std::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>
{} ;
//局部特化: 第二个模板参数的类型是 int
template <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::deque
Stack <double , std::deque<double> > dblStack ;
intStack.push( 7 ) ;
std::cout<<intStack.top() <<std::endl ;
intStack.pop() ;
//usages of the double stack
dblStack.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 中文版》
今天先到这里,今天开始做高中数学题了,希望自己的思维能灵光一些(笑) ~
快要分布式期末考试了,希望一切顺利。