资源描述
单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,*,类和动态内存分配,12、1 动态内存和类,一个使用动态内存得例子,class StringBad,private:,char*str;/pointer to string,int len;/length of string,static int num_strings;/number of objects,public:,StringBad(const char*s);/constructor,StringBad();/default constructor,StringBad();/destructor,/friend function,friend std:ostream&operator(std:ostream&os,const StringBad,;,以下就是其实现(,stringbad、cpp,),静态类成员得初始化,int StringBad:num_strings=0;/,注意:,静态类成员位于静态存储区,并不就是类对象得组成部分;,在类声明中声明,不可初始化(静态,const,整型或枚举除外除外);,在类方法实现文件中初始化。使用类型名和定义域限制操作符,不用关键字,static,。,然后就是构造函数,StringBad:StringBad(const char*s),len=std:strlen(s);/set size,str=new charlen+1;/allot storage,std:strcpy(str,s);/initialize pointer,num_strings+;/set object count,cout num_strings :str,object createdn;/For Your Information,注意:字符串并没有保存在对象中,而就是在单独得堆内存中。对象中存储字符串得地址。,析构函数,StringBad:StringBad()/necessary destructor,cout str object deleted,;/FYI,-num_strings;/required,cout num_strings leftn;/FYI,delete str;/required,此处析构函数就是必须得。在构造函数中用,new,分配内存,必须在析构函数中用,delete,释放内存,文件,vegnews、cpp,就是使用上述类得主程序。其中定义了两个函数:一个传引用作参数,一个传值作参数,void callme1(StringBad&rsb),cout String passed by reference:n;,cout rsb n;,void callme2(StringBad sb),cout String passed by value:n;,cout sb n;,1:Celery Stalks at Midnight object created,2:Lettuce Prey object created,3:Spinach Leaves Bowl for Dollars object created,headline1:Celery Stalks at Midnight,headline2:Lettuce Prey,sports:Spinach Leaves Bowl for Dollars,String passed by reference:,Celery Stalks at Midnight,headline1:Celery Stalks at Midnight,String passed by value:,Lettuce Prey,Lettuce Prey object deleted,2 left,headline2:,葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺,Initialize one object to another:,sailor:Spinach Leaves Bowl for Dollars,Assign one object to another:,3:C+default object created,knot:Celery Stalks at Midnight,End of main(),Celery Stalks at Midnight object deleted,2 left,Spinach Leaves Bowl for Dollars object deleted,1 left,葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺,?object deleted,0 left,未处理得异常:,System、NullReferenceException:,未将对象引用设置到对象得实例。,at delete(Void*),at StringBad、_dtor(StringBad*)in d:temptest2strngbad、cpp:line 37,at main()in d:temptest2vegnews、cpp:line 39,C+object deleted,-1 left,葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺,F object deleted,-2 left,未处理得异常:,System、NullReferenceException:,未将对象引用设置到对象得实例。,at delete(Void*),at StringBad、_dtor(StringBad*)in d:temptest2strngbad、cpp:line 37,at _CxxCallUnwindDtor(IntPtr,Void*),at main()in d:temptest2vegnews、cpp:line 39,跟踪程序得执行过程,当函数,callme2,调用时发生了析构函数得调用。析构函数得,delete,语句释放了实际参数,headline,对象得成员,headline、str,指向得内存,并使静态成员得计数减一:,cout str object deleted,;,-num_strings;/required,cout num_strings leftn;/FYI,delete str;,注意:当以对象为实参传递给函数时,函数中要拷贝对象得副本,即创建一个临时对象;该函数结束时临时对象消失,析构函数被调用。,分析程序:复制构造函数,程序中没有给出复制构造函数,但却使用了复制构造函数:,当用一个对象初始化一个新建对象时,StringBad sailor=sports;,StringBad sailor(sports);,函数按值传递对象或返回对象时,callme2(headline2);,大家有疑问的,可以询问和交流,可以互相讨论下,但要小声点,此时使用了默认得复制构造函数,即按值依次复制对象得非静态变量成员。该复制构造函数得原型如下:,Stringbad(const Stringbad,她执行得就是浅拷贝,即复制成员得值。对于,StringBad sailor=sports;,相当于,sailor、str=sports、str,即两个指针值相同,她们指向了同一块内存,另外,默认复制构造函数不会对静态成员有改变,所以计数不增加。,注意,1,:如果类中有这样得静态成员,当创建对象时其值发生改变,必须提供显式得复制构造函数,注意,2,:如果类中有用,new,初始化得指针成员,应该提供显式得复制构造函数,以实现深拷贝,StringBad:StringBad(const StringBad&sb),num_strings+;/handle static member update,len=sb、len;/same length,str=new char len+1;/allot space,std:strcpy(str,sb、str);/copy string to new location,分析程序:赋值操作符,程序中允许对类对象赋值,就是因为,c+,自动为类重载了赋值操作符。如,StringBad knot;,knot=headline1;,使用了下面得自动重载得操作符,Stringbad,默认赋值操作符得实现方式与默认拷贝相似,也就是对成员进行逐个复制,但不影响静态数据成员,由于,knot=headline1;,赋值操作符作了如下得工作:,knot、str=headline1、str;,同样就是两个指针指向了同一块内存。,对象,knot,得析构函数调用在先,释放了指针指向得内存;当对象,headline1,得析构函数被调用时试图释放已经释放得内存导致错误。,解决该问题得方法就是提供赋值操作符得重载,实现深拷贝。,重载赋值操作符应具有如下功能:,由于被赋值得对象在创建时已经分配了内存,所以函数使用前应该先释放她;,函数要避免自身赋值,否则赋值前得释放内存操作会破坏对象得内容;,函数返回一个指向对象得引用,这样可以实现连续赋值操作。,重载得操作符如下,StringBad&StringBad:operator=(const StringBad&sb),if(this=&sb),return*this;,delete str;,len=sb、len;,str=new charlen+1;,std:strcpy(str,sb、str);,return*this;,改进后得字符串类,增加复制构造函数和赋值操作符,添加必要得功能,1、,修订默认构造函数:构建一个空字符串,String:String()/default constructor,len=0;,str=new char1;/,为何不就是,new char?,str0=0;/default string,num_strings+;,2、,比较成员函数:比较字符串得前后顺序,bool operator(const String&st1,const String&st2),/,若,st1,中得字符排在,st2,之前,则返回,true,return(std:strcmp(st1、str,st2、str)(const String&st1,const String&st2),/,利用已经重载得操作符,return st2、str st1、str;,bool operator=(const String&st1,const String&st2),/,所有字符相同返回,true,return(std:strcmp(st1、str,st2、str)=0);,比较操作符都被设置为友元,有利于,C,字符串与对象得比较,例如,name,就是一个对象,if(“Smith”=name),可以转换为,if(operator=(“Smith”,name),再由一个构造函数将“,Smith”,转换为类类型从而进行函数得调用,3、,重载下标运算符,运算符得一个操作数位于括号前,一个操作数位于两个括号之间。,char&String:operator (int i),return str i;,/,这个方法可以为对象得特定元素赋值,String name(“john smith”);,name0=J;,name5=S;,但就是非,const,成员不能被,const,对象使用,所以还要定义一个,const,函数,const char&String:operator(int i)const,return stri;,4、,静态类成员函数,/static function,static int HowMany();,/static method,int String:HowMany(),return num_strings;,静态类成员函数得作用:不能通过对象调用她,她也只能访问静态数据成员,声明为公有,则可以由作用域操作符调用她:,Int count=String:HowMany();,5、,再增加一个赋值操作符,String&String:operator=(const char*s),delete str;,len=std:strlen(s);,str=new charlen+1;,std:strcpy(str,s);,return*this;,通过这个方法可以用字符串对对象赋值,6、,重载输出输入操作符,ostream&operator(ostream&os,const String&st),os(istream&is,String&st),char tempString:CINLIM;,is、get(temp,String:CINLIM);,if(is),st=temp;,while(is&is、get()!=n),continue;,return is;,小结:构造函数中使用new,构造函数中使用new初始化指针成员,则析构函数重要使用delete;,new和delete须兼容;,如果有多个构造函数,必须以同样方式使用new,也可以将指针成员初始化为0;,应定义复制构造函数,实现深拷贝,其中还要注意更新受影响得静态成员;,应定义赋值操作符,实现深拷贝。,有关函数返回对象得总结,返回指向,const,对象得引用,对象引用作函数参数就是为提高效率,不用复制对象,const,表示不可改动实参,如果函数要返回传递给她得对象,也可以通过传引用提高效率。,const Vector&Max(const Vector&v1,const Vector&v2),return(v1、magval v2、magval?V1:v2);,此例中可以返回对象,但要调用复制构造函数,降低效率。,const,与参数对应。,返回指向非,const,对象得引用,常用于重载赋值操作符和与流对象一起用得,操作符。,ostream&operator(ostream&os,const String&st),必须返回,ostream&,以便实现连续输出。不能返回对象,因,ostream,没有公有得复制构造函数。也不能就是,const,因为流对象得值在改变。,String&String:operator=(const String&st),可以返回对象或引用,都可起到连续赋值作用,但返回引用效率高。,返回对象,如果函数中要返回得就是个局部对象,则不能返回引用,因为返回时该对象已消失,引用指向得对象将不存在。,例如重载得算术运算符,Vector Vector:operator+(const Vector&b)const,return Vector(x+b、x,y+b、y);,返回,const,对象,前面得,operator+,允许以下操作,Vector step1(50,40);,Vector step2(38,77);,Vector total;,total=step1+step2;,step1+step2=total;,if(step1+step2=step1)cout“step2=0、”,解决方法:将,operator+,得类型声明为,const,对象,指向对象得指针,在,C+,中,经常使用指向对象得指针,下面得例子演示了这种用法。,(,sayings2、cpp,),可以使用普通得指针方法来使用对象指针,例如,String*shortest=/initialize to/first object,String*first=,之后便可以利用指针间接访问,if(sayingsi、length()length(),或者对指针解除引用,if(sayingsi *first),使用new初始化对象,如果,className,就是类,value,得类型为,typeName,则语句,className*pclass=new className(value);,将调用构造函数,className(typeName);,例如,String*pst=new String(“Hello、”);,将调用,String(const char*s);,而,String*pst=new String;,将调用默认构造函数,String();,一个特殊情况,String*favorite=new String(sayingschoice);,所给得参数就是一个本类对象得名字,则调用得就是复制构造函数,String(const String /copy constructor,刚才得例子中在两个层次上使用了,new,和,delete,操作符:,一就是为创建每一个对象包含得字符串分配存储空间,这就是在构造函数中进行得,需要在析构函数中释放该空间,String:String(const char*s)/construct String from/C string,len=std:strlen(s);/set size,str=new charlen+1;/allot storage,std:strcpy(str,s);/initialize pointer,num_strings+;/set object count,String:String(),-num_strings;,delete str;,第二个层次就是为类对象分配空间,保存得就是类对象得成员,str,指针值和字符串得长度值,lenString*favorite=new String(sayingschoice);,cout My favorite saying:n *favorite JustTesting();/destroy object pointed/to by pc3,pc1-JustTesting();/destroy object pointed/to by pc1,需要注意,以创建对象相反得顺序删除之,并且删除完对象之后再释放所在得缓冲区。,12、2 队列模拟,问题:一家银行想在一家超市中建立ATM机,超市担心排队会影响其经营。编程模拟ATM对超市造成得影响。,使用队列描述排队顾客:队列中得项为顾客。假设,1/3得顾客1分钟接受服务,1/3为2分钟,另外1/3为3分钟;顾客得到达时间随机,但每小时到达得顾客数恒定。,设计队列类,应有如下特征:,存储有序项序列,容纳得项目数有一定限制,能够创建空队列,能够判断队列为空或为满,可以从队尾添加项,可以从队首删除项,能够确定队列中得项目数,队列类得接口:,class Queue,enum Q_SIZE=10;,public:,Queue(int qs=Q_SIZE);/create queue with a qs limit,Queue();,bool isempty()const;,bool isfull()const;,int queuecount()const;,bool enqueue(const Item /add item to end,bool dequeue(Item /remove item from front,;,队列数据得表示,使用链表表示。链表由节点构成,每个节点中保存相应信息以及指向下一个节点得指针,队尾得指针通常指向,NULL,。,struct Node,Item item;,struct Node*next;,;,此外还要有链表得起始位置,末尾位置,可存储得最大项数以及当前存储得项数。可以如下设计队列类得数据成员:,class Queue,private:,/class scope definitions,/Node is a nested structure definition local to this class,struct Node Item item;struct Node*next;,enum Q_SIZE=10;,/private class members,Node*front;/pointer to front of Queue,Node*rear;/pointer to rear of Queue,int items;/current number of items in Queue,const int qsize;/maximum number of items in Queue,队列最初就是空得,所以,Queue:Queue(int qs):qsize(qs),front=rear=NULL;,items=0;,注意:非静态常量数据成员和引用数据成员不能赋值,但又不能在类定义中初始化。只能使用成员初始化列表方式初始化,归纳:,static,const,成员得初始化问题,static const成员就是类中得常数,在类定义中初始化即可:class Test static const int size=10;,static 成员在类中声明,其初始化要放在类定以外,通常就是在实现文件中,例如 class Test static int size;,/,实现文件,int Test:size=10;/,成员定义,const,成员,在类定义中声明,在构造函数得成员初始化列表中初始化,如前面得例子。,判断队列为空、为满和确定队列中项目个数得成员函数如下:,bool Queue:isempty()const,return items=0;,bool Queue:isfull()const,return items=qsize;,int Queue:queuecount()const,return items;,bool Queue:enqueue(const Item&item),if(isfull(),return false;/,如果为满,不许加入,Node*add=new Node;/create node,if(add=NULL),return false;/,节点没有创建成功,add-item=item;/set node pointers,add-next=NULL;,items+;,if(front=NULL)/if queue is empty,front=add;/place item at front,else,rear-next=add;/else place at rear,rear=add;/have rear point to new node,return true;,
展开阅读全文