C++学习笔记:(八)模板
目录
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++学习笔记:(八)模板相关推荐
- Halcon 学习笔记八:颜色识别
Halcon 学习笔记八:颜色识别 一.图像处理需要的知识 二.图像处理的预处理和分割过程 二.颜色识别的方法 三.例子一 四.例子二 五.例子三 一.图像处理需要的知识 1.图像处理基础(rgb(h ...
- ReactJS学习笔记八:动画
ReactJS学习笔记八:动画 分类: react学习笔记 javascript2015-07-06 20:27 321人阅读 评论(0) 收藏 举报 react动画 目录(?)[+] 这里只讨论Re ...
- 【opencv学习笔记八】创建TrackBar轨迹条
createTrackbar这个函数我们以后会经常用到,它创建一个可以调整数值的轨迹条,并将轨迹条附加到指定的窗口上,使用起来很方便.首先大家要记住,它往往会和一个回调函数配合起来使用.先看下他的函数 ...
- python3.4学习笔记(八) Python第三方库安装与使用,包管理工具解惑
python3.4学习笔记(八) Python第三方库安装与使用,包管理工具解惑 许多人在安装Python第三方库的时候, 经常会为一个问题困扰:到底应该下载什么格式的文件? 当我们点开下载页时, 一 ...
- ROS学习笔记八:创建ROS msg和srv
ROS学习笔记八:创建ROS msg和srv 本节主要讲述了如何创建和建立ROS msg和srv,同时使用命令行工具rosmsg.rossrv和roscp. msg和srv简介 msg:描述ROS m ...
- ZooKeeper学习笔记(八):ZooKeeper集群写数据原理
写数据原理 写流程直接请求发送给Leader节点 这里假设集群中有三个zookeeper服务端 ACK (Acknowledge character)即是确认字符,在数据通信中,接收站发给发送站的一种 ...
- MongoDB 学习笔记八 复制、分片、备份与恢复、监控
MongoDB 学习笔记八 复制.分片.备份与恢复.监控 MongoDB复制(副本集) 什么是复制? MongoDB 复制原理 MongoDB 副本集设置 副本集添加成员 MongoDB 分片 分片 ...
- OpenCV 学习笔记(模板匹配)
OpenCV 学习笔记(模板匹配) 模板匹配是在一幅图像中寻找一个特定目标的方法之一.这种方法的原理非常简单,遍历图像中的每一个可能的位置,比较各处与模板是否"相似",当相似度足够 ...
- python3第三方库手册_python3.4学习笔记(八) Python第三方库安装与使用,包管理工具解惑...
python3.4学习笔记(八) Python第三方库安装与使用,包管理工具解惑 许多人在安装Python第三方库的时候, 经常会为一个问题困扰:到底应该下载什么格式的文件? 当我们点开下载页时, 一 ...
- Polyworks脚本开发学习笔记(八)-组合运用命令批量改名
Polyworks脚本开发学习笔记(八)-组合运用命令批量改名 需求解析 以下是使用包边比较点创建的一组包边点(即Gap点)和曲面点-包边点(即Flush点),这种命名方式不太常规,改为Gap和Flu ...
最新文章
- 清理C盘无用的垃圾的文件,给c盘瘦身
- 干货 | 顾险峰:对抗生成网络的几何理论解释(附视频PPT)
- amos看拟合度在哪里看_小程序开发公司哪里强?看这几点
- 【vuejs小项目】一、脚手架搭建工作
- MspEmu W.I.P.
- Linux系统安全工具之NMAP
- 蓝桥杯-前缀表达式(java)
- json txt格式转换器_SpringBoot项目中如何定制HTTP消息转换器
- 模拟人生4修身拉德兰连衣裙MOD下载
- 超越竞争(2) 价值创新
- 2020运动相机推荐_2020年超有价值入门级微单相机推荐,超高性价比几款入门级微单相机(选购指南)...
- 硬质合金销售真的有那么难么?
- C语言,产生一组数字,并将其写入txt文档中
- Android ueventd浅析
- SlickEdit 之Ubuntu 版本安装
- 华为云devops认证考试课堂笔记4
- CM311-1a YST(2G+16G)刷Armbian后的网络设置
- frontpage中没有动态HTML效果,[多选] 在frontpage中,应用文字的动态HTML效果时可选择的事件有()...
- URAL 1069 Prufer Code 优先队列
- 如何给网站添加CNZZ站长统计功能代码的常用办法