《深入实践C++模板编程》之二——模板类

摘要:
empty())31pop();32}3334boolempty(){returnhead==0;}35Tconst&top()constthrow36{37if38{39throwstd::runtime_error;40}41返回头-˃值;42}4344voidpush45{46head=newnode_type;47}4849voidpop();50}; 5152template<typenameT>//类成员函数的实现也应放在与类声明相同的头文件中_ Stack<T>::pop()54{55if56{57node_type*tmp=head;58head=head-˃next;59deletetmp;60}61}622模板类的继承1 template<typename T>2classcount_ Stack:publicmy_ Stack<T>3{4typedefmy_Stack<T>base_type;5unsignedsize;67 public:8count_Stack():base_type(),size{}9voidpush10{11base_type::push;12size++;13}1415voidpop()16{17if18{19base_type::pop(;20size--;21}22}2324unsignedgetSize()常量{returnsize;}25}; 26如果公共类将模板实例作为其基类,则需要为基类模板指定一个特定的模板参数值。例如,classanother_stack:publicmy_stack{}3。异构链接列表的类型是一个固定指向另一类型节点的类,这限制了链接列表中的节点必须是相同类型的,因此整个链接列表只能存储相同类型的数据。模板的一个特殊功能是构造异构链接列表。
1、类的模板的使用
类,由于没有参数,所以没有模板实参推导机制。
 1 #include <stdexcept>
 2  
 3 template<typename T> class my_stack;
 4  
 5 template<typename T>
 6 class list_node
 7 {
 8 T value;
 9 list_node *next;
10  
11 list_node(T const &v, list_node *n) :
12 value(v), next(n){}
13  
14 friend class my_stack<T>;
15 };
16  
17 template<typename T>
18 class my_stack
19 {
20 typedef list_node<T> node_type;
21 node_type *head;
22  
23 my_stack operator=(my_stack const&);
24 my_stack(my_stack const &s){}
25  
26 public:
27 my_stack() :head(0){}
28 ~my_stack()
29 {
30 while (!empty())
31 pop();
32 }
33  
34 bool empty(){ return head == 0; }
35 T const& top() const throw(std::runtime_error)
36 {
37 if (empty())
38 {
39 throw std::runtime_error("Stack is empty");
40 }
41 return head->value;
42 }
43  
44 void push(T const &v)
45 {
46 head = new node_type(v, head);
47 }
48  
49 void pop();
50 };
51  
52 template<typename T>//类成员函数的实现同样要放在和类声明相同的头文件里
53 void my_stack<T>::pop()
54 {
55 if (head)
56 {
57 node_type* tmp = head;
58 head = head->next;
59 delete tmp;
60 }
61 }
62  
2、模板类的继承
 
 1 template<typename T>
 2 class count_stack : public my_stack<T>
 3 {
 4 typedef my_stack<T> base_type;
 5 unsigned size;
 6  
 7 public:
 8 count_stack() :base_type(), size(0){}
 9 void push(T const &v)
10 {
11 base_type::push(v);
12 size++;
13 }
14  
15 void pop()
16 {
17 if (size > 0)
18 {
19 base_type::pop();
20 size--;
21 }
22 }
23  
24 unsigned getSize() const { return size; }
25 };
26  

如果是普通类以模板实例为基类,需要为基类模板给定明确的模板参数值。例如,

class another_stack:public my_stack<char>{}
 
3、异质链表
链表的类型为固定指向另一个类型节点的类,这就约束了链表中节点必须是同一类型,从而整个链表只能保存同一类型的数据。模板的一个特殊功能就是可以构造异质链表。构造的思路是以嵌套的形式,一会会进行演示。
 
这里只涉及到了异质链表的节点:
 1 template<typename T, typename N>
 2 struct hetero_node
 3 {
 4 T value;
 5 N* next;
 6 hetero_node(T const &v, N *n) :value(v), next(n){}
 7 };
 8  
 9 template<typename T, typename N>
10 hetero_node<T, N>* push(T const &v, N *n)
11 {
12 return new hetero_node<T, N>(v, n);
13 }
14  
15 template<typename T, typename N>
16 N* pop(hetero_node<T, N> *head)
17 {
18 N *next = head->next;
19 delete head;
20 return next;
21 }
22  
使用异质链表结构构造三元组:
typedef hetero_node<int, void> node0;
typedef hetero_node<char, node0> node1;
typedef hetero_node<std::string, node1> node2;
 
node2 *p2 = push(std::string("Awesome"),
push('w',
push(1, (void*)NULL)));
 
pop(pop(pop(p2)));
构建链表时,一个节点的指针,就代表了整个链表,所有的操作都采用一种嵌套的形式,可以体现一种新的设计思路。
 
next指针占据额外的开销,有没有办法节省这种开销?
template<typename T, typename N>
struct tuple
{
T value;
N next;
tuple(T const &v, N const &n) :value(v), next(n){}
};
 
template<typename T, typename N>
tuple<T, N> push(T const &v, N const &n)
{
return tuple<T, N>(v, n);
}
 
typedef tuple<int, char> tuple0;
typedef tuple<float, tuple0> tuple1;
typedef tuple<std::string, tuple1> tuple2;
 
tuple2 t = push(std::string("test"),
push(1.0f,
push(1, 'a')));
当然你也可以用类来实现tuple,但是这样的元组的元素个数是固定的:
template<typename T0, typename T1, typename T2>
struct tuple3
{
T0 v0;
T1 v1;
T2 v2;
tuple3(T0 const &_v0, T1 const & _v1, T2 const & _v2) :
v0(_v0), v1(_v1), v2(_v2){}
};

总结:异质链表摆脱具体数据类型的束缚。

 
4、成员函数模板
如果类是一个模板,以我们前面的my_stack为例:
template<typename T>
void my_stack<T>::pop()
{
if (head)
{
node_type* tmp = head;
head = head->next;
delete tmp;
}
}
如果类不是一个模板,但成员函数是一个模板:
struct normal_class
{
int value;
template<typename T>
void set(T const &v)
{
value = int(v);
}
};
如果类和成员函数都是模板,该怎么在类外定义:
template<typename N>
struct a_class_template
{
N value;
 
template<typename T>
void set(T const& v)
{
value = N(v);
}
 
template<typename T>
T get();
};
 
template<typename N> template<typename T>
T a_class_template<N>::get()
{
return T(value);
}
 
5、类模板的静态成员
template<typename T>
struct the_class
{
static int id;
the_class(){ id++; }
};
 
template<typename T> int the_class<T>::id = 0;
 
void call1()
{
the_class<int> c;
printf("static id:%d
", c.id);
}
 
void call2()
{
the_class<int> c;
printf("static id:%d
", c.id);
}
输出结果:
《深入实践C++模板编程》之二——模板类第1张
编译call1.cpp和call2.cpp会生成两个the_class的实例,会拥有两个the_class<int>::id的分配内存地址,但是在链接时,链接器将随机选择一个目标中的空间作为最终的存储空间,从而使不同目标文件中的多个等价模板实例共享同一套静态成员存储空间。
 
 
 

免责声明:文章转载自《《深入实践C++模板编程》之二——模板类》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Webbrowser控件判断网页加载完毕的简单方法Vue 使用 Element 组件实现前端自己的分页功能下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

在C#中快速查询文件

相信使用过Everything的人都对其超快的搜索速度印象非常深刻,它的主要原理是通过扫描NTFS磁盘的USN Journal读取的文件列表,而不是磁盘目录,由于USN Journal非常小,因此能实现快速搜索。在CodePlex上也有人对这个功能进行了.Net的封装:MFT Scanner in VB.NET。 由于.Net程序的Dll基本上是通用的,在...

图片在 canvas 中的 选中/平移/缩放/旋转,包含了所有canvas的2D变化,让你认识到数学的重要性

1、介绍   canvas 已经出来好久了,相信大家多少都有接触。   如果你是前端页面开发/移动开发,那么你肯定会有做过图片上传处理,图片优化,以及图片合成,这些都是可以用 canvas 实现的。   如果你是做前端游戏开发的,可能会非常熟悉,或者说对几何和各种图形变化非常了解。   这里我介绍的是简单的、基本的,但是非常完全的一个 2d 的 canva...

Live555 分析(一):类介绍

从程序的结构来看,live项目包括了四个基本库、程序入口类(在mediaServer中)和一些测试代码(在testProgs中)。 四个基本静态库是UsageEnvironment、BasicUsageEnvironment、groupsock和liveMedia。 UsageEnvironment: 包括抽象类UsageEnvironment和抽象类Ta...

c语言系统函数——进程的创建

一 、 进程的创建    1.fork();创建一个独立的进程      pid_t fork(void);      使用这个命令会创建一个独立于父进程而且拷贝父进程全部存储空间的子进程      返回值有三种情况               1.父进程会返回子进程的进程号,pid>0              2.子进程会返回0;pid = 0...

如何利用JS检查元素是否在视口内

前言 分享两个监测元素是否在视口内的方法 1. 位置计算 使用 Element.getBoundingClientRect() 方法返回元素相对于视口的位置 const isElementVisible = (el) => {const rect = el.getBoundingClientRect();}; 获取浏览器窗口的宽高 const isE...

golang数据结构之双链表

目录结构:  doubleLink.go package link import ( "fmt" ) //HerosNode 链表节点 type HerosNode struct { ID int Name string pre *HerosNode //指针 next *HerosNode //指针 }...