目录

8模板

8.1模板的概念

8.2函数模板和模板函数

8.3类模板与模板类

8.4程序实例


8模板

8.1模板的概念

函数重载可以实现具有相同功能的函数的函数名相同,使程序更加易于理解。系统可以根据不同的参数类型来区分函数。这样虽然很方便,但是书写函数的个数并没有减少,重载函数的代码量几乎完全相同。那么如何解决这个问题?

C++提供了模板。模板是C++的一个重要特征。模板是类型参数化的工具。所谓类型参数化,是指把类型定义为参数,当参数实例化时,可指定不同的数据类型,从而真正实现代码的可重用。模板分函数模板和类模板,它们分别允许用户构造模板函数和模板类。

例如,求两个数的最大值:

int max(int x, int y)
{return (x>y)?x:y;
}
char max(char x, char y)
{return (x>y)?x:y;
}
float max(float x, float y)
{return (x>y)?x:y;
}

这些函数所执行的功能相同,只是参数类型和函数返回值类型不同。这样不仅程序代码的重用性差,而且存在大量冗余信息,是程序维护起来相当困难。解决这个问题的最好方法是使用模板。

8.2函数模板和模板函数

当对每种数据类型执行相同的操作,用函数模板来完成将非常简单。程序员只需定义一次函数模板,根据调用函数时提供的参数类型,编译器会产生相应的目标代码函数,以正确处理每种类型的调用。

声明函数模板的格式:

template <class 类型参数>
返回类型 函数名(模板形参表)
{函数体
}

例如,输出不同类型数组的元素值可定义成函数模板:

template <class T>
void Printarray(T *array, int count)
{for(int i = 0; i < count; i++){cout << array[i] << " ";}cout <<endl;
}

也可以定义成:

template <typename T>
void Printarray(T *arrya, int count)
{for(int i = 0; i < count; i++){cout << array[i] << " ";}cout <<endl;
}

说明:

(1)T是类型参数,它既可以是系统预定义的数据类型,也可以是用户自定义的类型。

(2)类型参数前需要加关键字class(或typename),这个class并不是类的意思,而是表示任何类型的意思。

(3)在使用模板函数时,关键字class(或typename)后面的类型参数,必须实例化,即用实际的数据类型替代它。

(4)< >里面的类型参数可以有一个或多个类型参数,但多个类型参数之间要用逗号分隔。

函数模板应用举例:输出不同类型数组的元素值。

#include <iostream>
using namespace std;template <class T>
void Printarray(T *array, int count)
{for(int i = 0; i < count; i++){cout << array[i] << " ";}cout <<endl;
}int main()
{int a[5] = {1,2,3,4,5};double b[7] = {1.1,2.2,3.3,4.4,5.5,6.6,7.7};char c[6] = "HELLO";Printarray(a,5);Printarray(b,7);Printarray(c,6);return 0;
}

程序在调用Printarray()打印各个数组时,当参数为a,则类型参数T为int;当参数是b,则类型参数T为double;当参数是c,类型参数T为char。从本程序可以看出,使用函数模板不用单独定义3个函数,从而解决了当采用函数重载技术时所产生的代码冗余问题。

注意:(1)在执行Printarray函数调用时,根据参数的类型,系统自动在内存中生成一个模板函数(即实例化函数),并执行该函数。

(2)函数模板中可以使用多个类型参数。但每个模板形参前必须有关键字class或typename。

多个类型参数应用举例:求两个数的最大值。

#include <iostream>
using namespace std;template <class T1, class T2>
T1 Max(T1 x, T2 y)
{return (x>y)?x:y;
}int main()
{int i = 10;float f = 12.5;double d = 50.344;char c = 'A';cout << "the max of i,c is: " << Max(i,c) <<endl;cout << "the max of i,f is: " << Max(f,i) <<endl;cout << "the max of d,f is: " << Max(d,f) <<endl;return 0;
}

说明:由于函数模板中的参数类型不一样,因此每个模板形参前都有class。在执行语句Max(i,c)时,令编译器实例化Max模板函数,类型参数T1为int,T2为char,然后调用Max计算最大值。其他语句与此类似。

(3)在template语句和函数模板定义语句之间不允许有其他的语句,例如:

template <class T1, class T2>
int n;
T1 Max(T1 x, T2 y)
{return (x>y)?x:y;
}

(4)模板函数类似于函数重载,但与函数重载不同。在进行函数重载时,每个函数体的动作可以相同也可以不同;但模板函数中的动作必须相同。例如,下面的函数只能用函数重载,而不能用模板函数。

void print(char *name)
{cout << name <<endl;
}
void print(char *name, int no)
{cout << name << no <<endl;
}

(5)函数模板中的模板形参T可以实例化为各种类型,但实例化T的各模板实参之间必须保证类型一致,否则将发生错误,例如:

#include <iostream>
using namespace std;
template <class T>
T Max(T x, T y)
{return (x>y)?x:y;
}
int main()
{int i = 10, j = 20;float f = 12.5;cout << "the max of i, j is: " << Max(i , j) <<endl;cout << "the max of i, j is: " << Max(i , f) <<endl;return 0;
}

解决这个问题有两种方法:

(1)采用强制类型转换,将Max(i , f)修改为Max(i , int(f))。

(2)定义一个完整的非模板函数重载函数模板,例如:

处理所有类型值的冒泡排序:

#include <iostream>
using namespace std;template <class T>
void bubble_sort(T *array, int n);
template <class T>
void show_array(T *array, int n);int main()
{int a[7] = {7,6,5,4,3,2,1};double b[5] = {2.4,2.3,2.1,2.9,2.6};bubble_sort(a,7);show_array(a,7);bubble_sort(b,5);show_array(b,5);return 0;
}template <class T>
void bubble_sort(T *array, int n)
{int i,j,flag;T temp;for(i = 0; i < n-1; i++){flag = 0;for(j = 1; j < n-i; j++){if(array[j-1] > array[j]){temp = array[j-1];array[j-1] = array[j];array[j] = temp;flag = 1;}}if(flag == 0)break;}
}template <class T>
void show_array(T *array, int n)
{int i;for(i = 0; i < n; i++){cout << array[i] <<" ";}cout <<endl;
}

从以上的例子可以看出,函数模板提供了一类函数的抽象,它可以是任意类型T为参数及函数返回值。函数模板经实例化而生成的具体函数模板函数。函数模板代表了一类函数,模板函数表示某一具体的函数。

普通函数和函数模板共存时程序运行结果:

#include <iostream>
using namespace std;template<class T>
int Add(T a, T b)
{cout << "使用函数模板" <<endl;return a+b;
}int Add(int a, char c)
{cout << "使用普通函数" <<endl;return a+c;
}void test()
{int a = 10;int b = 20;char c1 = 'a';char c2 = 'b';Add(a, c1);Add(a, b);Add<>(a, b);Add(c1, a);  //普通函数可以进行自动类型转换}int main()
{test();return 0;
}

总结:

(1)函数模板可以像普通函数一样被重载。

(2)C++编译器优先考虑普通函数。

(3)可以通过空模板实参列表使编译器只能通过模板匹配。

接下来看一下函数模板是如何生成相应的模板函数,首先需要了解C/C++的编译过程:

#include <iostream>   //asd.cpp
using namespace std;
int main()
{cout << "Test!" <<endl;return 0;
}

1.预处理

使用-E选项:gcc -E asd.cpp -o asd.i

预处理阶段的过程有:头文件展开,宏替换,条件编译,去掉注释等。

2.编译

使用-S选项:gcc -S asd.cpp -o asd.s

编译阶段将高级语言翻译成机器语言,生成对应的汇编代码。

3.汇编

使用-c选项:gcc -c asd.cpp -o asd.o

汇编阶段将源文件翻译成二进制文件。

4.链接

链接过程将二进制文件与需要用到的库链接。连接后便可以生成可执行文件。

asd.i文件内容(预处理):

asd.s文件内容(生成汇编代码):

asd.o文件内容(生成二进制文件):

运行可执行文件:

通过上述的例子简单了解程序的编译过程,接下观察在编译过程中函数模板是怎样生成模板函数的。

#include <iostream>
using namespace std;
template <class T>
T Add(T a, T b)
{cout << "使用函数模板" <<endl;return a+b;
}
/** 函数模板产生模板函数,然后模板函数被调用*/void test()
{int a,b;float c,d;a = 10;b = 15;Add(a, b);Add(c, d);
}int main()
{test();return 0;
}

这里主要看这个例子编译过程中的汇编代码:

 .file   "asd1.cpp".local  _ZStL8__ioinit.comm _ZStL8__ioinit,1,1.section  .rodata
.LC0:.string    "\344\275\277\347\224\250\346\231\256\351\200\232\345\207\275\346\225\260".text.globl _Z3Addic.type   _Z3Addic, @function
_Z3Addic:
.LFB972:.cfi_startprocpushq %rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq    %rsp, %rbp.cfi_def_cfa_register 6subq   $16, %rspmovl   %edi, -4(%rbp)movl  %esi, %eaxmovb  %al, -8(%rbp)movl   $.LC0, %esimovl $_ZSt4cout, %edicall    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKcmovl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, %esimovq   %rax, %rdicall  _ZNSolsEPFRSoS_Emovsbl  -8(%rbp), %edxmovl  -4(%rbp), %eaxaddl  %edx, %eaxeave.cfi_def_cfa 7, 8ret.cfi_endproc
.LFE972:.size   _Z3Addic, .-_Z3Addic.globl  _Z4testv.type   _Z4testv, @function
_Z4testv:
.LFB973:.cfi_startprocpushq %rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq    %rsp, %rbp.cfi_def_cfa_register 6subq   $32, %rspmovl   $10, -4(%rbp)movl   $15, -8(%rbp)movl   -8(%rbp), %edxmovl  -4(%rbp), %eaxmovl  %edx, %esimovl  %eax, %edicall  _Z3AddIiET_S0_S0_movl   -16(%rbp), %edxmovl -12(%rbp), %eaxmovl %edx, -20(%rbp)movss    -20(%rbp), %xmm1movl    %eax, -20(%rbp)movss    -20(%rbp), %xmm0call    _Z3AddIfET_S0_S0_leave.cfi_def_cfa 7, 8ret.cfi_endproc
.LFE973:.size   _Z4testv, .-_Z4testv.globl  main.type   main, @function
main:
.LFB974:.cfi_startprocpushq %rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq    %rsp, %rbp.cfi_def_cfa_register 6call   _Z4testvmovl    $0, %eaxpopq    %rbp.cfi_def_cfa 7, 8ret.cfi_endproc
.LFE974:.size   main, .-main.section    .rodata
.LC1:.string    "\344\275\277\347\224\250\345\207\275\346\225\260\346\250\241\346\235\277".section    .text._Z3AddIiET_S0_S0_,"axG",@progbits,_Z3AddIiET_S0_S0_,comdat.weak    _Z3AddIiET_S0_S0_.type  _Z3AddIiET_S0_S0_, @function
_Z3AddIiET_S0_S0_:
.LFB978:.cfi_startprocpushq %rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq    %rsp, %rbp.cfi_def_cfa_register 6subq   $16, %rspmovl   %edi, -4(%rbp)movl  %esi, -8(%rbp)movl  $.LC1, %esimovl $_ZSt4cout, %edicall    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKcmovl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, %esimovq   %rax, %rdicall  _ZNSolsEPFRSoS_Emovl    -8(%rbp), %eaxmovl  -4(%rbp), %edxaddl  %edx, %eaxleave.cfi_def_cfa 7, 8ret.cfi_endproc
.LFE978:.size   _Z3AddIiET_S0_S0_, .-_Z3AddIiET_S0_S0_.section  .text._Z3AddIfET_S0_S0_,"axG",@progbits,_Z3AddIfET_S0_S0_,comdat.weak    _Z3AddIfET_S0_S0_.type  _Z3AddIfET_S0_S0_, @function
_Z3AddIfET_S0_S0_:
.LFB979:.cfi_startprocpushq %rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq    %rsp, %rbp.cfi_def_cfa_register 6subq   $16, %rspmovss  %xmm0, -4(%rbp)movss    %xmm1, -8(%rbp)movl $.LC1, %esimovl $_ZSt4cout, %edicall    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKcmovl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, %esimovq   %rax, %rdicall  _ZNSolsEPFRSoS_Emovss   -4(%rbp), %xmm0addss    -8(%rbp), %xmm0movss    %xmm0, -12(%rbp)movl    -12(%rbp), %eaxmovl %eax, -12(%rbp)movss    -12(%rbp), %xmm0leave.cfi_def_cfa 7, 8ret.cfi_endproc
.LFE979:.size   _Z3AddIfET_S0_S0_, .-_Z3AddIfET_S0_S0_.text.type    _Z41__static_initialization_and_destruction_0ii, @function
_Z41__static_initialization_and_destruction_0ii:
.LFB985:.cfi_startprocpushq %rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq    %rsp, %rbp.cfi_def_cfa_register 6subq   $16, %rspmovl   %edi, -4(%rbp)movl  %esi, -8(%rbp)cmpl  $1, -4(%rbp)jne .L10cmpl    $65535, -8(%rbp)jne .L10movl    $_ZStL8__ioinit, %edicall   _ZNSt8ios_base4InitC1Evmovl $__dso_handle, %edxmovl $_ZStL8__ioinit, %esimovl   $_ZNSt8ios_base4InitD1Ev, %edicall  __cxa_atexit
.L10:leave.cfi_def_cfa 7, 8ret.cfi_endproc
.LFE985:.size   _Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii.type _GLOBAL__sub_I__Z3Addic, @function
_GLOBAL__sub_I__Z3Addic:
.LFB986:.cfi_startprocpushq %rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq    %rsp, %rbp.cfi_def_cfa_register 6movl   $65535, %esimovl    $1, %edicall    _Z41__static_initialization_and_destruction_0iipopq %rbp.cfi_def_cfa 7, 8ret.cfi_endproc
.LFE986:.size   _GLOBAL__sub_I__Z3Addic, .-_GLOBAL__sub_I__Z3Addic.section  .init_array,"aw".align 8.quad _GLOBAL__sub_I__Z3Addic.hidden  __dso_handle.ident  "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-36)".section    .note.GNU-stack,"",@progbits

汇编代码看起来有难度,这里只需要看call后面的内容就可以了。从上往下,_Z3Addic是函数模板,编译器先对它进行编译。_Z4testv是调用函数模板的函数(test()),这里有两个call分别是call _Z3AddIiET_S0_S0_和call _Z3AddIfET_S0_S0_,代表调用int和float的模板函数。相应的模板函数汇编代码在后面,它与我们使用的情况相同。如果再多一个别的类型,就会生成相应的模板函数。

总结:函数模板通过具体类型产生不同的函数,编译器会对函数模板进行两次编译,在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码进行编译。

8.3类模板与模板类

类是对一组对象的公共性质的抽象,而类模板是更高层次的抽象。类模板允许用户为类定义一种模式,使类中的某些数据成员、成员函数的参数或返回值可以根据需要取任意类型。

类模板的定义格式:

template <class T>
class 类名
{//...
};

说明:

(1)在每个类模板定义之前,都需要在前面加上模板声明,如template <class T>。在使用类模板时首先应将它实例化为一个具体的类(即模板类),类模板实例化为模板类的格式为:类名<具体类型名>。

(2)模板类可以有多个模板参数。

(3)在类定义体外定义成员函数时,如果该成员函数中有类型参数,则需要在函数体外进行模板声明,即在类名和函数名之间缀上<T>。具体格式:

返回类型 类名<T>::成员函数名(参数表)

(4)类模板不代表一个具体的、实际的类,而代表着一类类。因此在使用时,必须将类模板实例化为一个具体的类,格式如下:

类名<实际的类型> 对象名;

实例化的类称为模板类。模板类是类模板对某一特定类型的实例。类模板代表了一类类,模板类表示某一具体的类。

用类模板实现栈的基本运算:

#include <iostream>
using namespace std;const int Size = 100;
template <class T>
class Stack
{private:T s[Size];int top;public:Stack();void Push(T x);T Pop();
};template <class T>
Stack<T>::Stack()
{top = -1;
}template <class T>
void Stack<T>::Push(T x)
{if(top == Size-1){cout << "Stack is full." <<endl;return;}s[++top] = x;
}template <class T>
T Stack<T>::Pop()
{if(top == -1){cout << "Stack underflow." <<endl;return 0;}return s[top--];
}int main()
{Stack<int> a;Stack<double> b;Stack<char> c;int i;char ch;for(i = 0; i < 10; i++){a.Push(i);}for(ch = 'a'; ch <= 'j'; ch++){c.Push(ch);}for(i = 0; i < 10; i++){b.Push(1.1+i);}for(i = 0; i < 10; i++){cout << a.Pop() << " ";}cout <<endl;for(i = 0; i < 10; i++){  cout << b.Pop() << " ";}cout <<endl;for(i = 0; i < 10; i++){  cout << c.Pop() << " ";}cout <<endl;return 0;
}

类模板中有多个类型参数实例:

#include <iostream>
using namespace std;template <class T1, class T2>
class A
{private:T1 x;T2 y;public:A(T1 a, T2 b);void print();
};template <class T1, class T2>
A<T1, T2>::A(T1 a, T2 b)
{x = a;y = b;
}template <class T1, class T2>
void A<T1, T2>::print()
{cout << x << " " << y <<endl;
}int main()
{A<int, double> ob1(10, 12.3);A<char*, char*> ob2((char*)"Diligence", (char*)"makes your dreams come true");ob1.print();ob2.print();return 0;
}

用类模板和重载[]运算符实现不同类型的数组的输出:

#include <iostream>
using namespace std;const int Size = 10;
template <class T>
class Array
{private:T a[Size];public:Array();T &operator[](int i);
};template <class T>
Array<T>::Array()
{int i;for(i = 0; i < Size; i++){a[i] = 0;}
}template <class T>
T & Array<T>::operator[](int i)
{if(i < 0 || i > Size-1){cout << "Index value of" << i << "is not of bounds." <<endl;}return a[i];
}int main()
{Array<int> int_array;Array<double> double_array;int i;cout << "Integer array:";for(i = 0; i < Size; i++)int_array[i] = i;for(i = 0; i < Size; i++)cout << int_array[i] <<" ";cout <<endl;cout << "Double array:";cout.precision(2);for(i = 0; i < Size; i++)double_array[i] = double(i)/3;for(i = 0; i < Size; i++)cout << double_array[i] <<" ";cout <<endl;return 0;
}

由于在类中重载[]运算符,因此执行cout << int_array[i] << “ ”;时,编译系统将int_array[i]解释为int_array.operator[](i),从而调用运算符重载函数operator[](int i)。定义重载[]函数时,由于返回时一个T的引用,因此可以使用重载的[]用在赋值语句的左边,所以语句int_array[i] = i;是合法的。对于对象double_array的操作与int_array相同。

8.4程序实例

用类模板实现学生成绩管理系统:

分析:本例将要操作的所有对象构成一个链表,链表中的每个结点元素就是一个对象。定义一个类模板Linklist,数据成员*head表示指向链表的头指针,链表中每个结点元素包含数据域data和指针域next,数据域data是T类型,指针next指向链表中下一个结点元素。成员函数Inser_Linklist表示插入一个结点元素;成员函数Get_Linklist表示返回第i个结点元素的地址;成员函数Del_Linklist表示删除第i个结点元素;成员函数Print_Linklist表示输出链表中结点元素的值。

Linklist
Node<T> *head;
Linklist();
int Insert_Linklist();
Node<T> *Get_Linklist(int i);
int Del_Linklist(int i);
void Print_Linklist();
~Linklist();
Stu
long no;
char name[10];
float score;
Stu();
void Print();
#include <iostream>
using namespace std;template <class T>
struct Node
{T data;Node *next;
};template <class T>
class Linklist
{private:Node<T> *head;Node<T> *r;public:Linklist();int Insert_linklist();Node<T>* Get_linklist(int i);int Del_linklist(int i);void Print_linklist();~Linklist();
};class Stu
{private:long no;char name[10];float score;public:Stu();void Print();
};template <class T>
Linklist<T>::Linklist()
{head = NULL;r = head;
}template <class T>
int Linklist<T>::Insert_linklist()
{Node<T> *s = new Node<T>;if(s){if(head){r->next = s;r = s;}else{head = s;r = s;}}if(r)r->next = NULL;return 1;
}template <class T>
void Linklist<T>::Print_linklist()
{Node<T> *p;p = head;while(p){p->data.Print();p = p->next;}cout <<endl;
}template <class T>
Node<T> * Linklist<T>::Get_linklist(int i)
{Node<T> *p = head;int j = 1;while(p != NULL && j < i){p = p->next;j++;}if(j == i)return p;elsereturn NULL;
}template <class T>
int Linklist<T>::Del_linklist(int i)
{Node<T> *p, *s;if(i == 1){p = head;head = head->next;delete p;return 1;}else{p = Get_linklist(i-1);if(p == NULL){cout << "Error!" <<endl;return -1;}else if(p->next == NULL){cout << "NULL!" <<endl;return 1;}else{s = p->next;p->next = s->next;if(!p->next)r = p;delete s;return 1;}}
}template <class T>
Linklist<T>::~Linklist()
{delete head;
}void menu()
{cout <<endl;cout << "1......Insert:" <<endl;cout << "2......Delete:" <<endl;cout << "3......Display:" <<endl;cout << "4......Exit:" <<endl;cout << "Choice:" <<endl;
}Stu::Stu()
{cout << "Input number,name,score:" <<endl;cin >>no>>name>>score;
}void Stu::Print()
{cout << no << " " << name << " " << score <<endl;
}int main()
{Linklist<Stu> L;int n,m = 1;while(m){menu();cin >> n;switch(n){case 1:{int success;success = L.Insert_linklist();if(success == 1)cout << "Insert successfully" <<endl;elsecout << "Insert failure" <<endl;break;}case 2:{int i,success;cout << "Input sitution" <<endl;cin >> i;success = L.Del_linklist(i);if(success == 1)cout << "Delete successfully" <<endl;else cout << "Delete failure" <<endl;break;}case 3:{cout << "Infomation:" <<endl;L.Print_linklist();break;}case 0: m = 0;} }return 0;
}

类Stu是类模板Linklist所要实例化的一个具体类。数据成员包括学生的学号、姓名和成绩;成员函数Print表示输出学生信息。

用类模板实现银行办公系统:

本例将要操作的所有对象构成一个队列,队列中的每个结点元素就是一个对象。定义一个类模板Lqueue,数据成员*front表示指向队头的指针,*rear表示指向队尾的指针,链表中每个结点元素包含数据域data和指针域next,数据域data是T类型,指针next指向链表中下一个结点元素。成员函数In_Lqueue表示入队操作;成员函数Empty_Lqueue表示判断队列是否为空;成员函数Out_Lqueue表示出队操作;成员函数Print_Lqueue表示输出队列中结点元素的值。

Lqueue
QNode<T> *front,*rear;
Lqueue();
void In_Lqueue();
int Empty_Lqueue();
void Print_Lqueue();
~Lqueue();
Customer
int account, amount;
Customer();
void Print();
#include <iostream>
using namespace std;template <class T>
struct Qnode
{T data;Qnode *next;
};template <class T>
class Lqueue
{private:Qnode<T> *front,*rear;public:Lqueue();void In_lqueue();int Empty_lqueue();int Out_lqueue();void Print_lqueue();~Lqueue();
};class Customer
{private:int account;int amount;public:Customer();void Print();
};template <class T>
Lqueue<T>::Lqueue()
{rear = NULL;front = NULL ;
}template <class T>
void Lqueue<T>::Print_lqueue()
{Qnode<T> *p;p = front;while(p != NULL){p->data.Print();p = p->next;}cout << endl <<endl;
}template <class T>
void Lqueue<T>::In_lqueue()
{Qnode<T> *p;p = new Qnode<T>;p->next = NULL;if(front == 0){front = p;rear = p;}else {rear->next = p;rear = p;}
}template <class T>
int Lqueue<T>::Empty_lqueue()
{if(front == NULL && rear == NULL)return 1;elsereturn 0;
}template <class T>
int Lqueue<T>::Out_lqueue()
{Qnode<T> *p;if(Empty_lqueue() == 1)return 0;else {p = front;front = p->next;delete p;if(front == NULL)rear = front;return 1;}
}template <class T>
Lqueue<T>::~Lqueue()
{delete rear;
}void menu()
{cout <<endl;cout << "1......Push" <<endl;cout << "2......Show" <<endl;cout << "3......Pop" <<endl;cout << "0......Exit" <<endl;cout << "Choice:" <<endl;
}Customer::Customer()
{cout << "Input account and amount:" <<endl;cin >>account>>amount;
}void Customer::Print()
{cout << account << "  " << amount <<endl;
}int main()
{Lqueue<Customer> L;int n,m = 1;while(m){menu();cin >> n;switch(n){case 1:{L.In_lqueue();cout << endl << "Element of Queue:" <<endl;L.Print_lqueue();break;}case 2:{int flag;flag = L.Empty_lqueue();if(flag != 1){cout << "Element of Queue:" <<endl;L.Print_lqueue();}else cout << "NULL" <<endl;break;}case 3:{int flag;flag = L.Out_lqueue();if(flag == 1){cout << endl << "Element of Queue:" <<endl;L.Print_lqueue();}else cout << "Empty!" <<endl;break;}case 0: m = 0;} }return 0;
}

类Customer是类模板Lqueue所实例化的一个具体类。数据成员包括顾客的账号和金额;成员函数Print表示输出顾客的信息。

C++学习笔记:(八)模板相关推荐

  1. Halcon 学习笔记八:颜色识别

    Halcon 学习笔记八:颜色识别 一.图像处理需要的知识 二.图像处理的预处理和分割过程 二.颜色识别的方法 三.例子一 四.例子二 五.例子三 一.图像处理需要的知识 1.图像处理基础(rgb(h ...

  2. ReactJS学习笔记八:动画

    ReactJS学习笔记八:动画 分类: react学习笔记 javascript2015-07-06 20:27 321人阅读 评论(0) 收藏 举报 react动画 目录(?)[+] 这里只讨论Re ...

  3. 【opencv学习笔记八】创建TrackBar轨迹条

    createTrackbar这个函数我们以后会经常用到,它创建一个可以调整数值的轨迹条,并将轨迹条附加到指定的窗口上,使用起来很方便.首先大家要记住,它往往会和一个回调函数配合起来使用.先看下他的函数 ...

  4. python3.4学习笔记(八) Python第三方库安装与使用,包管理工具解惑

    python3.4学习笔记(八) Python第三方库安装与使用,包管理工具解惑 许多人在安装Python第三方库的时候, 经常会为一个问题困扰:到底应该下载什么格式的文件? 当我们点开下载页时, 一 ...

  5. ROS学习笔记八:创建ROS msg和srv

    ROS学习笔记八:创建ROS msg和srv 本节主要讲述了如何创建和建立ROS msg和srv,同时使用命令行工具rosmsg.rossrv和roscp. msg和srv简介 msg:描述ROS m ...

  6. ZooKeeper学习笔记(八):ZooKeeper集群写数据原理

    写数据原理 写流程直接请求发送给Leader节点 这里假设集群中有三个zookeeper服务端 ACK (Acknowledge character)即是确认字符,在数据通信中,接收站发给发送站的一种 ...

  7. MongoDB 学习笔记八 复制、分片、备份与恢复、监控

    MongoDB 学习笔记八 复制.分片.备份与恢复.监控 MongoDB复制(副本集) 什么是复制? MongoDB 复制原理 MongoDB 副本集设置 副本集添加成员 MongoDB 分片 分片 ...

  8. OpenCV 学习笔记(模板匹配)

    OpenCV 学习笔记(模板匹配) 模板匹配是在一幅图像中寻找一个特定目标的方法之一.这种方法的原理非常简单,遍历图像中的每一个可能的位置,比较各处与模板是否"相似",当相似度足够 ...

  9. python3第三方库手册_python3.4学习笔记(八) Python第三方库安装与使用,包管理工具解惑...

    python3.4学习笔记(八) Python第三方库安装与使用,包管理工具解惑 许多人在安装Python第三方库的时候, 经常会为一个问题困扰:到底应该下载什么格式的文件? 当我们点开下载页时, 一 ...

  10. Polyworks脚本开发学习笔记(八)-组合运用命令批量改名

    Polyworks脚本开发学习笔记(八)-组合运用命令批量改名 需求解析 以下是使用包边比较点创建的一组包边点(即Gap点)和曲面点-包边点(即Flush点),这种命名方式不太常规,改为Gap和Flu ...

最新文章

  1. 清理C盘无用的垃圾的文件,给c盘瘦身
  2. 干货 | 顾险峰:对抗生成网络的几何理论解释(附视频PPT)
  3. amos看拟合度在哪里看_小程序开发公司哪里强?看这几点
  4. 【vuejs小项目】一、脚手架搭建工作
  5. MspEmu W.I.P.
  6. Linux系统安全工具之NMAP
  7. 蓝桥杯-前缀表达式(java)
  8. json txt格式转换器_SpringBoot项目中如何定制HTTP消息转换器
  9. 模拟人生4修身拉德兰连衣裙MOD下载
  10. 超越竞争(2) 价值创新
  11. 2020运动相机推荐_2020年超有价值入门级微单相机推荐,超高性价比几款入门级微单相机(选购指南)...
  12. 硬质合金销售真的有那么难么?
  13. C语言,产生一组数字,并将其写入txt文档中
  14. Android ueventd浅析
  15. SlickEdit 之Ubuntu 版本安装
  16. 华为云devops认证考试课堂笔记4
  17. CM311-1a YST(2G+16G)刷Armbian后的网络设置
  18. frontpage中没有动态HTML效果,[多选] 在frontpage中,应用文字的动态HTML效果时可选择的事件有()...
  19. URAL 1069 Prufer Code 优先队列
  20. 如何给网站添加CNZZ站长统计功能代码的常用办法

热门文章

  1. 2021年高压电工考试及高压电工模拟考试题
  2. Centos 9 终端下vim修改文件后如何保存退出及相关命令
  3. 人际关系处理文库 怎样与女人相处 怎样与领导相处 张胜利 岳贵安著 PDF 网盘免费...
  4. 昨天520表白失败,我想用Python分析一下...
  5. 二本程序员的向上之路——规划书
  6. pdf转换成ppt的方法,格式不变
  7. 记录每天学习的新知识:MQTT客户端
  8. 【django】忘记管理员账号/密码找回
  9. SuperMap GIS基础软件许可二十问
  10. 手机日程提醒在哪里设置?