magazines[j]=magazines[j+1];
CounterM--;
}
测试程序设计如下:
//Exam5-7.cpp
#include
#include
#include"Retrieval.h"
#include"Reader.h"
voidmain(void)
{
Bookb1("朱战立","C++面向对象程序设计","P306/5",10001);
Bookb2("朱战立","数据结构——使用C语言(第3版)","P306/6",10002);
Magazinem1("计算机学报",13,20001);
Magazinem2("计算机应用",12,20002);
Readerr1("张三",30001);
Readerr2("李四",30002);
r1.Show();
r2.Show();
r1.AddBook(b1);
r1.AddBook(b2);
r1.ShowBooks();
r2.AddMagazine(m1);
r2.AddMagazine(m2);
r2.DelMagazine(m1);
r2.ShowMagazines();
}
程序运行输出如下:
读者:
张三30001
读者:
李四30002
图书:
C++面向对象程序设计朱战立P306/510001
图书:
数据结构——使用C语言(第3版)朱战立P306/610002
杂志:
计算机应用1220002
问题:
进一步分析图书馆的图书和杂志管理和借阅方式,设计一个基本符合图书馆实际工作方式的图书和杂志借阅系统。
.1.2链式堆栈——私有继承举例
私有继承的特点是:
基类的保护成员和公有成员将成为派生类的私有成员。
私有继承的应用意义是:
派生类在利用基类已经设计完成的代码资源的同时,限制派生类的对象调用基类中的公有成员。
【例51.1】先设计一个带头结点的单链表类,再设计一个带头结点的链式堆栈类,要求带头结点的链式堆栈类利用带头结点的单链表类的代码资源。
设计:
带头结点的链式堆栈和带头结点的单链表在结构上完全相同,其构造都是一个由头指针指示的有size个结点的链表,惟一的差别是单链表允许在任意结点位置插入和删除,而链式堆栈只允许在头结点后插入和删除。
带头结点的单链表结构如图5-7(a)所示,带头结点的链式堆栈结构如图5-7(b)所示。
图51.2单链表和链式堆栈
(a)单链表;(b)链式堆栈
单链表类的成员变量包括头指针和结点个数,成员函数包括:
取结点个数、判链表空否、插入结点、删除结点、取结点的数据值。
链式堆栈类的成员变量包括头指针和结点个数,成员函数包括:
取结点个数、判堆栈空否、入栈、出栈、取栈顶结点的数据值。
为了简化链式堆栈类的设计,可以把链式堆栈类设计成单链表类的派生类,这样就可以利用单链表类的代码资源。
以入栈为例,单链表类的插入结点是在参数指定的某个结点后插入一个结点,而链式堆栈类的入栈是在头结点后插入一个结点,因此入栈成员函数就可以用参数值固定为0调用插入结点成员函数来实现。
链式堆栈类的其它成员函数设计方法类同。
由于链式堆栈类只允许在栈顶位置插入结点,不允许在其他位置插入结点,因此,链式堆栈类要私有继承单链表类,这样就能保证外部程序的对象不能在其他位置随意插入结点,从而保证堆栈的正确性。
带头结点的单链表类设计如下:
//LinList.h
#include
#include
classListNode//结点类
{
friendclassLinList;
private:
ListNode*next;//指向下一结点的指针
floatdata;//数据元素
public:
ListNode(ListNode*ptrNext=NULL)//构造函数,构造头结点
{next=ptrNext;}
ListNode(constfloat&item,ListNode*ptrNext=NULL)
//构造函数,构造其他结点
{
data=item;
next=ptrNext;
}
};
classLinList//单链表类
{
private:
ListNode*head;//头指针
intsize;//结点个数
voidClearList(void);//清空链表
ListNode*Index(intpos)const;//返回指向第pos个结点的指针
public:
LinList(void);//构造函数
~LinList(void);//析构函数
intListSize(void)const;//取结点个数
intListEmpty(void)const;//判链表空否
voidInsert(constfloat&item,intpos);//插入一个结点
floatDelete(intpos);//删除第pos个结点
floatGetData(intpos)const;//取第pos个结点的data值
};
LinList:
:
LinList()//构造函数
{
head=newListNode;//头指针指向头结点
size=0;//定义size的初值为0
}
LinList:
:
~LinList(void)//析构函数
{
ClearList();//清空链表
deletehead;
}
voidLinList:
:
ClearList(void)//清空表为初始化状态
{
ListNode*p,*p1;
p=head->next;//p指向第一个结点
while(p!
=NULL)//释放结点空间直至初始化状态
{
p1=p;
p=p->next;
deletep1;
}
size=0;//结点个数置为初始化值0
}
ListNode*LinList:
:
Index(intpos)const//返回指向第pos个结点的指针
{
if(pos<-1||pos>size)
{
cout<<"参数pos越界出错!
"<exit(0);
}
if(pos==-1)returnhead;//pos为-1时返回头指针head
ListNode*p=head->next;//p指向第一个结点
inti=0;//从0开始计数
while(p!
=NULL&&i{
p=p->next;
i++;
}
returnp;//返回第pos个结点指针
}
intLinList:
:
ListSize(void)const//取结点个数
{
returnsize;
}
intLinList:
:
ListEmpty(void)const//判链表空否
{
if(size<=0)return1;
elsereturn0;
}
voidLinList:
:
Insert(constfloat&item,intpos)
//在第pos个结点后插入一个元素值为item的新结点
{
ListNode*p=Index(pos-1);//p为指向第pos-1个结点指针
//构造新结点newNode,newNode的data域值为item,next域值为p->next
ListNode*newNode=newListNode(item,p->next);
p->next=newNode;//新结点插入第pos个结点前
size++;//结点个数加1
}
floatLinList:
:
Delete(intpos)//删除第pos个结点
{
if(size==0)
{
cout<<"链表已空无元素可删!
"<exit(0);
}
ListNode*q,*p=Index(pos-1);//p为指向第pos-1个结点指针
q=p->next;//q指向第pos个结点
p->next=p->next->next;//第pos个结点脱链
floatdata=q->data;
deleteq;//释放第pos个结点空间
size--;//结点个数减1
returndata;//返回第pos个结点的data域值
}
floatLinList:
:
GetData(intpos)const//取第pos个结点的data值
{
ListNode*p=Index(pos);//p指向第pos个结点
returnp->data;
}
带头结点的链式堆栈类设计如下:
//LinStack.h
#include"LinList.h"
classLinStack:
privateLinList//私有继承
{
public:
LinStack(void):
LinList(){}
~LinStack(void){}
intStackSize(void)const//返回堆栈元素个数
{returnListSize();}
intStackEmpty(void)const//判堆栈空否
{returnListEmpty();}
voidPush(constfloat&item)//入栈
{Insert(item,0);}
floatPop(void)//出栈
{returnDelete(0);}
floatGetTop(void)const//取栈顶元素
{returnGetData(0);}
};
测试程序设计如下:
//Exam5-8.cpp
#include"LinStack.h"
voidmain(void)
{
LinStackmyStack;
inti;
cout<<"初始元素个数:
"<for(i=1;i<=5;i++)
myStack.Push(i);
cout<<"入栈后元素个数:
"<cout<<"依次出栈的元素:
";
for(i=1;i<=5;i++)
cout<cout<cout<<"结束元素个数:
"<}
程序运行输出结果为:
初始元素个数:
0
入栈后元素个数:
5
依次出栈的元素:
54321
结束元素个数:
0
私有继承方式可以利用已设计完成的基类代码资源,简化派生类的代码设计,但是,这时所有成员函数的执行都是通过调用基类的成员函数实现的,其时间效率会受到一定的影响。