博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
读书笔记--C++ Template(The complete guide)--Chapter3--类模板
阅读量:5752 次
发布时间:2019-06-18

本文共 4918 字,大约阅读时间需要 16 分钟。

上一篇讲到函数模板,自然需要接下来讲讲类模板,通俗点说,类模板就是带类型参数的类,它表示一族类,这些类的实现逻辑是一致的,STL中的容器类就是这一思想的典型应用,在这一章里,我们将用类模板来实现一个Stack的模板类(类模板?好像差不多)。

 

1.一个使用类模板的例子—Stack类

 
//bascis/stack1.hpp
 
#include 
#include 
 
template
class stack{
private:
std::vector
elems;
public:
void push(T const &);
void pop();
T top()const;
bool empty()const{
return elems.empty();
}
};
 
template
void Stack
::push(T const& elem)
{
elems.push_back(elem);
}
 
template
void Stack
::pop()
{
if(elems.empty())
{
throw std::out_of_range("Stack<>::pop():empty stack");
}
elems.pop();
}
 
template 
T Stack
::top()const
{
if(elems.empty())
{
throw std::out_of_range("Stack<>::top():empty stack");
}
return elem.back();
}

 

模板类中的数据用vector来储存,目的是为了排除冗杂反复的细枝末节,从而是我们能关注模板类的模板特性。

2.模板类的声明

声明模板类与声明模板函数差不多,也是需要有一行声明

 
template
声明的格式同样是
 
template

在之前给出的代码中,Stack是类的名称,Stack<T>是类的类型,这是需要注意的,在需要类型的地方,请用Stack<T>,反之,用Stack,如:

 
template
class Stack
{
...
Stack(Stack
const&);
Stack
&&operator=(Stack
const&);
...
}

3.模板类的成员函数

模板类的成员函数与普通的模板函数用法几乎一致,只是加上了属于某个类的束缚,不详述了。

4.怎么使用模板类

以刚才的Stack为例:

 
//basics/stack1test.cpp
 
#include 
#include 
#include 
#include "stack1.hpp"
 
int main()
{
try{
Stack
intStack;
Stack
stringStack;
 
intStack.push(7);
std::cout<
<
 
stringStack.push("hello");
std::cout<
<
 
stringStack.pop();
stringStack.pop();
}
catch
{
std::err<<"ExceptionL: "<
<
return EXIT_FAILURE;
}
}

可以看到,其实和普通的类用起来没多大区别,就是声明的时候带个参数。

模板类的使用时可以嵌套的,如下:

 
Stack
>intStackStack;

需要注意的是> >不能连着,否则会被识别成>>,导致错误(一直不理解编译器为什么连这种都识别不出)。

5.类模板的特化

具体来讲,类模板的特化允许对某些特定的类型采取更加有效的实现手段,或者是解决某些类型在用同样一套模板时会出现的行为异常(比如指针作为类模板的参数的话经常可能出现问题)。当然,一旦特化了某个类型参数,对应的成员函数全部需要特。第3小节说过,模板类的成员函数是跟普通的模板类差不多的,因此,它们当然也可以单独特化,但是这样的话,我们就不能特化整个类了。

要特化某个类模板,语法规则是前置一条生命:template<>,如:

 
tempalte<>
class Stack
{
...
}

一旦特化之后,所有的成员函数都必须重新定义成普通成员函数,如:

 
//bascis/stack2.hpp
 
#include 
#include 
 
template<> //特化标志
class Stack
{
//原来就是class Stack
private:
std::vector
elems;
public:
void push(std::string const &);
void pop();
std::string top()const;
bool empty()const{
return elems.empty();
}
};
 
//template
不需要了
void Stack
::push(std::string const& elem)
{
elems.push_back(elem);
}
 
 
void Stack
::pop()
{
if(elems.empty())
{
throw std::out_of_range("Stack<>::pop():empty stack");
}
elems.pop();
}
 
std::string Stack
::top()const
{
if(elems.empty())
{
throw std::out_of_range("Stack<>::top():empty stack");
}
return elem.back();
}

 

6.部分特化

侯捷先生称之为偏特化,可能是是他们的翻译习惯吧,记得偏最小二乘回归的英文就是PLSR,第一个单词就是partial,部分特化英文为partial specialization,这是一个非常值得讲一讲的问题,因为这是STL中trait编程技法的一个重要理论基础(有空的话会讲讲这方面的内容)。

那么什么是部分特化呢?C++ template给出的说法是这样的:


You can specify special implementations for particular circumstances,but some template parameters must still be defined by the users.


也就是说,对于某些特定的使用环境,你可以为模板类定义特殊的实现,对于部分特化而言,还是需要用户指定一些参数,假设原来有个模板类如下:

 
template
class MyClass
{
...
};
部分特化有以下几种:
  • 当两个模板参数的类型一致时:
    template
    class MyClass
    {
    ...
    };
  • 某个模板参数需要固定
    template
    class MyClass
    {
    ...
    };
     
  • 模板参数是指针
    template
    class MyClass
    {
    ...
    };
具体使用时,选用哪个是有规则的,如果只有一个严格匹配的,自然是选严格匹配的,如:
 
 
MyClass
mif; //uses MyClass
MyClass
mff; //uses MyClass
MyClass
mfi;//use MyClass
MyClass
mp;//use MyClass

但是,有时候不只一个匹配的,而且匹配程度一样,就会发生编译错误:

 
MyClass
m; //Error: matches MyClass
and MyClass
MyClass
m;//Error: matches MyClass
and MyClass

匹配实际上是有强有弱的,如果编译器能够识别出匹配的强弱,选强的那个,对于模板匹配,越特化的那个越强,比如说,对于上面例子的第二个情况,我们可以再特化一个模板类:

 
 
template
class MyClass
{
...
};

这个模板类非常强化,以至于编译器会直接选用这个,关于强弱在12章里面会有详细解释,大概意思就是如果A能表示B,B不能表示A,那么说明B的特化更强。

7.类模板的默认参数

与函数模板不一样的是,类模板是可以拥有默认参数的,甚至默认参数可以用到模板标识前面的类型参数,请看这个例子:

 
//bascis/stack3.hpp
 
#include 
#include 
 
template
>
class stack{
private:
CONT elems;
public:
void push(T const &);
void pop();
T top()const;
bool empty()const{
return elems.empty();
}
};
 
template
>
void StacK
::push(T const& elem)
{
elems.push_back(elem);
}
 
template
>
void StacK
::pop()
{
if(elems.empty())
{
throw std::out_of_range("Stack<>::pop():empty stack");
}
elems.pop();
}
 
template 
T StacK
::top()const
{
if(elems.empty())
{
throw std::out_of_range("Stack<>::top():empty stack");
}
return elem.back();
}

 

当然,你可以定义一个这样的具现化实例:

 
Stack
>

8.总结

  • 模板类是一族类的表示,有一个或多个用户指定类型作为参数
  • 要使用模板类,传给它类型参数,模板据此生成特定的代码
  • 模板类中的方法只有在使用到的时候才会具现化.也就是说即便某个类型不适合具现化,不能实现某些操作,只要这些操作没在已经用到的函数中,就不会发生问题.
  • 模板类能够特化和部分特化.
  • 模板类能定义默认的模板类型参数.

转载于:https://www.cnblogs.com/obama/archive/2013/05/02/3055229.html

你可能感兴趣的文章
linux虚拟机下安装vmtools
查看>>
bower解决js的依赖管理 包含git的内容
查看>>
关于Fn的快捷键
查看>>
XenApp中关于打印机的部分问题
查看>>
nginx 1.0.x 建立 https 加密访问
查看>>
Ubuntu中root用户和user用户的相互切换
查看>>
SylixOS 内存性能测试程序设计与实现
查看>>
Spring4 学习系列之——bean的相关属性
查看>>
RA-hero-recovery-v1.6.2 功能详解(网络整理)
查看>>
VRRP企业组网配置
查看>>
C++对象模型学习——执行期语意学
查看>>
kickstat无人值守安装
查看>>
SQLCODE=-668, SQLSTATE=57016
查看>>
Centos7+Keepalived实现Mariadb(MYSQL)的高可用(HA)
查看>>
无法在VMware Player中安装ESXI5
查看>>
httpd-2.2配置补充及httpd-2.4浅析
查看>>
grep -A -B
查看>>
Spring3开发实战 之 第一章:Spring入门
查看>>
Activity启动流程
查看>>
Windows Professional 7 (专业版)中怎么实现自动登入系统?
查看>>