链表类的熟悉

定义一个Box类,表示一个产品。然后用装车类TruckLoad来装,每个产品Box打包到到容器Package中,容器有两大要素:Box,指向下一个Package.


为什么要用容器?因为不要让Box复杂,只让它代表产品。所以有了Package,容器概念是很通用的,可以以相同的方式为任何类型的对象设置窗口对象。

我们明确三点:

一、Box类是,内容,纯粹的内容,不带其它的链接,是一个被操作的东西;

二、Package类,是节点,有两项必备:一是数据(即Box),二是链接(指向下一个Package对象);

三、TruckLoad类,是链表类,用于操作结点,并显示内容的类。

整个过程就是TruckLoad类以中间容器为介质操作Box类。


一、容器(Package)类的定义

         按构思最初是这样的:

class Package{
	public:
	Package(Box* pBox):theBox(*pBox),pNext(0){}//复制一次 
	
	Box getBox()const{return theBox;}//显示内容 
	Package* getNext()const{return pNext;}//返回下个节点 
	
	void setNext(Package* pPackage){pNext=pPackage;}//连接下一个节点 
	private:
	Box theBox;  //复制一次 
	Package* pNext;
};

       可以看到,要复制两次,一是构造复制,另一个是下一节点的复制。造成多个副本,若有一项改变将不知要修改哪些?

      重新设置一下,以指针方式:

class Package{
	public:
	Package(Box* pNextBox):pBox(pNextBox),pNext(0){}//构造 
	
	Box* getBox()const{return pBox;}    //返回Box指针 
	Package* getNext()const{return pNext;}//返回下个节点 
	
	void setNext(Package* pPackage){pNext=pPackage;}//连接下一个节点 
	private:
	Box* pBox;   //关键处,这里指针提高了效率,导致整个设计高效。 
	Package* pNext;
}; 

         可以看到容器更的高效了,所以 开发容器类,需要仔细考虑该类的数据成员是否应是对象的副本,有时这很重要,应避免包含副本,除指针外还有引用也是可以来避免的



二、定义TruckLoad类

      它必须提供创建和扩展列表的所有功能,以及Box对象的方式。

      这个货车装的链表,必须有链头,链尾,为了显示运送过程发放货物(box)的情况,还应记录当前Box发到什么地方的节点。

class TruckLoad{
	public:
	TruckLoad(Box* pBox=0,int Count=1);//构造,无数据时空链 
	
	Box* getFirstBox();  //返回链头 
	Box* getNextBox();      //返回下一个Box 
	void addBox(Box* pBox); //增加一个Box,内部代码把它加入下一个节点的数据中去 
	private:
	Package* pHead;        //链头 
	Package* pCurrent;     //当前节点 
	Package* PTail;        //链尾 
}; 

        可以看出Package是作为一个中间介质容器来用的。要有TruckLoad来完成所有任务的想法。

       TruckLoad类的实现:

TruckLoad::TruckLoad(Box* pBox=0,int Count=0){
	pHead=pCurrent=pTail=0;//初始化链表为空, 
	if((Count>0)&&(pBox!=0)) //创建时不空时 
		for(int i=0;i<Count;i++) //按数组方式组建 
			addBox(pBox+1);     //指针增1是增加sizeof(Box)大小,一次增加一个节点 
}
void addBox(Box* pBox){
	Package* pPackage=new Package(pBox);//创建容器并初始化 
	if(pHeak)
		pTail->setNext(pPackage); //非空,链接到链尾上 
	else 
		pHead=pPackage; //空链,新创建节点到链头 
	pTail=pPackage; //移动链尾到新创建的节点上 
} 
Box*  TruckLoad::getFirstBox(){
	pCurrent=pHeak;       //开始分发,从链头开始,记录当前位置 
	return pCurrent->getBox();//返回当前节点的盒子 
}
Box* TruckLoad::getNextBox(){
	if(pCurrent)
		pCurrent=pCurrent->getNext();//非空,移至下一节点 
	else
		pCurrnet=pHead; //空,移到链头,节约代码不考虑删除等情况 
	return pCurrent?pCurrent->getBox():0; 
} 


三、Box类

       这个较简单,就是定义长宽高,计算体积,并进行两个例子体积的比较。类定义:

//box.h definition
#ifndef BOX_H
#define BOX_H
class Box{
	public:
	Box(double aLength=1.0,double aWidth=1.0,double aHeight=1.0);
	double volume()const;
	double getLength()const;
	double getWidth()const;
	double getHeight()const;
	int compareVolume(const Box& otherBox)const;
	private:
	double length;
	double width;
	double height; 
};
#endif

      然后是对应 的实现:

//box.cpp   implementation
#include"box.h"
Box::Box(double aLength,double aWidth,double aHeight){
	length=aLength>0.0?aLength:1.0;
	width=aWidth>0.0?aWidth:1.0;
	height=aHeight>0.0?aHeight:1.0;
}
double Box::volume()const{return length*width*height;}
double Box::getLength()const{return length;}
double Box::getWidth()const{return width;}
double Box::getHeight()const{return height;}
int Box::compareVolume(const Box& otherBox){
	double vol1=volume();
	double vol2=other.volume();
	return vol1>vol2?1:(vol1<vol2?-1:0);
}


四、链表初步完成

      共5个文件,Box两个文件:声明与实现

                            TruckLoad与Package结合在一起用,共两个文件:声明与实现

                             主文件一个

     初步的文件有三个缺点:1、Package可以被任意文件访问,我们要把它封闭到TruckLoad类中:嵌套类

                                                 2、浅拷贝问题,TruckLoad对象复制只是指针的复制,如果一个修改了,另一个并不知道修改没

                                                 3、内存泄露:Package没有释放分配的内存,须要手动析构函数。

     先看看已经初步得到的程序:

#ifndef BOX_H
#define BOX_H
class Box{
	public:
	Box(double aLength=1.0,double aWidth=1.0,double aHeight=1.0);
	double volume()const;
	double getLength()const;
	double getWidth()const;
	double getHeight()const;
	int compareVolume(const Box& otherBox)const;
	private:
	double length;
	double width;
	double height; 
};
#endif

#ifndef BOX_H
#define BOX_H
class Box{
	public:
	Box(double aLength=1.0,double aWidth=1.0,double aHeight=1.0);
	double volume()const;
	double getLength()const;
	double getWidth()const;
	double getHeight()const;
	int compareVolume(const Box& otherBox)const;
	private:
	double length;
	double width;
	double height; 
};
#endif

//list.h  Pakage and TruckLoad
#ifndef LIST_H
#define LIST_H

#include"box.h"  //不要忘记 
class Package{  //Package类必须在TruckLoad前面 
	public:
	Package(Box* pNewBox);//构造 
	
	Box* getBox()const;    //返回Box指针 
	Package* getNext()const;//返回下个节点 
	
	void setNext(Package* pPackage);//连接下一个节点 
	private:
	Box* pBox;   //关键处,这里指针提高了效率,导致整个设计高效。 
	Package* pNext;
}; 

class TruckLoad{
	public:
	TruckLoad(Box* pBox=0,int Count=1);//构造,无数据时空链 
	
	Box* getFirstBox();  //返回链头 
	Box* getNextBox();      //返回下一个Box 
	void addBox(Box* pBox); //增加一个Box,内部代码把它加入下一个节点的数据中去 
	private:
	Package* pHead;        //链头 
	Package* pCurrent;     //当前节点 
	Package* pTail;        //链尾 
}; 
#endif

//list.cpp   Package and TruckLoad
#include"box.h"
#include"list.h" 
Package::Package(Box *pNewBox):pBox(pNewBox),pNext(0){}//新结点的下结点始终指针向0
Box* Package::getBox()const{return pBox;} 
Package* Package::getNext()const{return pNext;}
void Package::setNext(Package *pPackage){pNext=pPackage;}

TruckLoad::TruckLoad(Box* pBox,int Count){
	pHead=pCurrent=pTail=0;//初始化链表为空, 
	if((Count>0)&&(pBox!=0)) //创建时不空时 
		for(int i=0;i<Count;i++) //按数组方式组建 
			addBox(pBox+1);     //指针增1是增加sizeof(Box)大小,一次增加一个节点 
}

Box*  TruckLoad::getFirstBox(){
	pCurrent=pHead;       //开始分发,从链头开始,记录当前位置 
	return pCurrent->getBox();//返回当前节点的盒子 
}
Box* TruckLoad::getNextBox(){
	if(pCurrent)
		pCurrent=pCurrent->getNext();//非空,移至下一节点 
	else
		pCurrent=pHead; //空,移到链头,节约代码不考虑删除等情况 
	return pCurrent?pCurrent->getBox():0; 
}
void TruckLoad::addBox(Box* pBox){
	Package *pPackage=new Package(pBox);//创建容器并初始化 
	if(pHead)
		pTail->setNext(pPackage); //非空,链接到链尾上 
	else 
		pHead=pPackage; //空链,新创建节点到链头 
	pTail=pPackage; //移动链尾到新创建的节点上 
}

#include <iostream>
#include<cstdlib>//random
#include<ctime>

#include"box.h"
#include"list.h"
using std::cout;
using std::endl;
inline int random(int count){
	return 1+static_cast<int>(count*static_cast<double>(std::rand())/(RAND_MAX+1.0)); 
}
int main(int argc, char *argv[])
{
	const int dimLimit=100;
	std::srand((unsigned)std::time(0));
	TruckLoad load1;
	for(int i=0;i<10;i++) 
		load1.addBox(new Box(random(dimLimit),random(dimLimit),random(dimLimit)));
	Box* pBox=load1.getFirstBox();
	Box* pNextBox;
	while(pNextBox=load1.getNextBox()){   
		if(pBox->compareVolume(*pNextBox)<0) pBox=pNextBox;//选取最大体积 
	}
	cout<<"largest is "<<pBox->volume()<<endl;
	const int boxCount=20;
	Box boxes[boxCount];
	for(int i=0;i<boxCount;i++)
		boxes[i]=Box(random(dimLimit),random(dimLimit),random(dimLimit));
	TruckLoad load2(boxes,boxCount);
	pBox=load2.getFirstBox();
	while(pNextBox=load1.getNextBox()){   
		if(pBox->compareVolume(*pNextBox)<0) pBox=pNextBox;//选取最大体积 
	}
	cout<<"second largest is "<<pBox->volume()<<endl;
	pNextBox=load1.getFirstBox();
	while(pNextBox){
		delete pNextBox;
		pNextBox=load1.getNextBox();
	}
	return 0;
}



五、解决外用,使用嵌套类

      把Package纳入到TruckLoad类的私有成员中,可以禁止Package类在外部使用。

class TruckLoad{
	public:
	TruckLoad(Box* pBox=0,int Count=1);//构造,无数据时空链 
	
	Box* getFirstBox();  //返回链头 
	Box* getNextBox();      //返回下一个Box 
	void addBox(Box* pBox); //增加一个Box,内部代码把它加入下一个节点的数据中去 
	private:
	class Package{
		public:
		Box* pBox;      //因为两数据是公有的,不再使用两个提取成员函数 
		Package* pNext;
		Package(Box* pNewBox);
		void setNext(Package* pPackage);
	};//注意这里少了分号,会出意思不到的问题
	Package* pHead;        //链头 
	Package* pCurrent;     //当前节点 
	Package* pTail;        //链尾 
}; 

      相应地更改实现:

//list.cpp   Package and TruckLoad
#include"box.h"
#include"list.h" 
TruckLoad::Package::Package(Box *pNewBox):pBox(pNewBox),pNext(0){}//新结点的下结点始终指针向0
//Box* Package::getBox()const{return pBox;} 
//Package* Package::getNext()const{return pNext;}
void TruckLoad::Package::setNext(Package *pPackage){pNext=pPackage;}

TruckLoad::TruckLoad(Box* pBox,int Count){
	pHead=pCurrent=pTail=0;//初始化链表为空, 
	if((Count>0)&&(pBox!=0)) //创建时不空时 
		for(int i=0;i<Count;i++) //按数组方式组建 
			addBox(pBox+1);     //指针增1是增加sizeof(Box)大小,一次增加一个节点 
}
Box*  TruckLoad::getFirstBox(){
	pCurrent=pHead;       //开始分发,从链头开始,记录当前位置 
	return pCurrent->pBox;//返回当前节点的盒子 
}
Box* TruckLoad::getNextBox(){
	if(pCurrent)
		pCurrent=pCurrent->pNext;//非空,移至下一节点 
	else
		pCurrent=pHead; //空,移到链头,节约代码不考虑删除等情况 
	return pCurrent?pCurrent->pBox:0; 
}
void TruckLoad::addBox(Box* pBox){
	Package *pPackage=new Package(pBox);//创建容器并初始化 
	if(pHead)
		pTail->pNext=pPackage; //非空,链接到链尾上 
	else 
		pHead=pPackage; //空链,新创建节点到链头 
	pTail=pPackage; //移动链尾到新创建的节点上 
}













  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值