第十二章 类和动态内存分配
2020-02-24 10:39:50 4 举报AI智能生成
C++ plus
c++
模版推荐
作者其他创作
大纲/内容
动态内存和类
12.1
代码
类声明
<div>class StringBad{</div><div>private:</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>char *str;//用指针。 </div><div><span class="Apple-tab-span" style="white-space:pre"> </span>int len;</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>static int num_strings;<b>//静态变量</b>。 </div><div>public:</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>StringBad( const char *s );</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>StringBad();</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>~StringBad();// 析构函数。 </div><div><span class="Apple-tab-span" style="white-space:pre"> </span></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>friend std::ostream & operator<<(std::ostream & os,const StringBad &st);</div><div>};</div>
类方法定义
<div>//构造函数 </div><div>StringBad::StirngBad( const char *s)</div><div>{</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>len = std::strlen(s);//strlen()在std中!! </div><div><span class="Apple-tab-span" style="white-space:pre"> </span>str = new char[len+1];</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>std::strcpy(str,s);</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>num_string++;</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>std::cout<<num_strings<<":\""<<str<<"\"object created\n";<span class="Apple-tab-span" style="white-space:pre"> </span></div><div>} </div><div><br></div><div>StringBad::StringBad()</div><div>{</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>len=4;</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>str=new char[4];</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>std::strcpy(str,C++);</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>num_string++;</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>cout<<num_strings<<":\""<<str<<"\"default object created";</div><div><span class="Apple-tab-span" style="white-space:pre"> </span></div><div>}</div><div><br></div><div>//析构函数</div><div>String Bad::~StringBad()</div><div>{</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>--numstrings;</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>cout<<numstrings<<"left\n";</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>delete [] str; </div><div>} </div>
说明
静态类成员<br>
类声明中:(声明)<br>static int num_strings;
无论创建多少对象,程序只创建一个静态类<br>副本。所用对象共享一个静态类变量副本。
类定义文件中:(初始化)<br>int StringBad::num_strings = 0;<br>
要指出类型,作用域,不用指明关键字static
在方法定义文件中声明,不在头文件中声明原因
会出现多个初始化语句副本,引起错误。
静态数据成员在类声明中声明,在包含类方法的文件(成员函数定义)中初始化
不能在类声明中初始化,但是静态常量除外
即在类声明中,可以有<br>const static int a=1;
原因
不能在类声明中初始化
声明描述了如何分配内存,不分配内存
类声明外使用单独语句初始化
是单独存储,不是对象的组成部分。
如果num_strings为公有数据成员,<br>在类外使用静态数据。
直接String::num_string <br>不能用对象引用
构造函数
字符串不保存在对象中,单独保存在堆内存中,str存储的是它的地址。
不能直接<br>str=s<br>这样保存的是地址,没有创建字符串副本。<br>要创建字符串,所以最好new + strcpy
析构函数
必要性
当对象过期的时候,对象被删除,但是str指向的内存没有释放,仍被分配,所以需要delete [] str;
问题
如果是系统默认,它是怎么形式??//是什么内容都没有的析构函数
上代码不合理之处
复制构造函数
问题
int main()<br>{<br> StringBad headline1("don`t be worry");<br> StringBad headline2=headline;<br>}
默认的复制构造函数,是按值传递,所以headline2是浅复制,<br>headline1与headline2指向的是同一个地址,当对象无效时,析构<br>函数释放一个内存两次,产生问题
解决
显式定义一个复制构造函数
StringBad::StringBad(<font face="黑体"><b>const</b></font> StirngBad &st )<br>{<br> num_strings++;<br> len = st.len;<br> str = new char [len+1];<br> std::strcpy(str ,st.str);<br> ...<br>}
赋值运算符
问题
int main()<br>{<br> StringBad headline1("i am ok");<br> StringBad headline2;<br> headline2=headline1;<br>}
同复制构造函数一样
解决
显式定义赋值运算符
StringBad & StringBad::operator=(const StirngBad & st)<br>{<br> if (this=&that)<br> return *thid;<br> delete [] str;<br> len=st.len;<br> str=new char [len+1];<br> std::strcpy(str,st.str);<br> return *this;<br>}
说明
同复制构造函数有不同
步骤
避免将对象赋值给自身,否则释放内存操作可能删除对象的内容
目标对象(被赋值的对象)可能之前分配了数据,所用有delete [] str 删除之前的。<br>//有些可以不删除,直接把值覆盖过去。
不删除会造成浪费
函数返回一个指向调用对象的引用
可以S0 = S1 = S2;
特殊成员函数
说明
这些函数没有定义的时候。C++自动提供
内容
默认构造函数,如果没有定义构造函数
默认析构函数,如果没有定义析构函数
复制构造函数,如果没有定义复制构造函数
赋值运算符,如果没有定义赋值运算符
地址运算符,如果没有定义地址运算符//??&?
返回调用对象的地址,this??
C++11
移动构造函数
移动赋值函数
默认构造函数
系统默认<br>(隐式默认)
Klunk::Klunk(){ };
不能delete[]其动态分配的数据?
显式默认<br>(用户自定义)
Klunk::Klunk()<br>{<br> klunk_ct=0;<br> ........<br> };
默认参数类型
Klunk::Klunk(int n = 0)<br>{<br> klunk_ct=n;<br>}
注意
这两个不可以同时存在
复制构造函数
原型
StringBad( const StringBad& );
注意const
何时调用
总体
用于将一个对象复制到新创建的对象中,当程序生成<br>对象副本的时候。
具体
将新对象显式初始化为现有对象<br>(假设motto是现有对象)
StringBad ditto(motto);
StringBad meto=motto;
如果是<br>StringBad meto;<br>meto=motto;<br>不会调用复制构造函数。而是直接用赋值函数。
StringBad also=StringBad(motto);
String *pStringBad=new StringBad(motto);
说明
中间两种可能使用复制构造函数直接创建metoo,also,<br>也可能使用复制构造函数生成一个临时对象,然后临时对象的<br>内容赋给meto和also<br>//一般是前者
函数按值传递
原型:StringBad callme2( StringBad sb);<br>调用:callme2(metto);<br>metto 传递给sb的时候。
函数返回对象(不是对象的引用)
默认复制函数的功能
逐个复制非静态成员的值(浅复制)
赋值运算符
原型
Class_name & Class_name::operator = (const Class_name & );<br>StingBad & StringBad::operator = (const Class_name & );
注意const
何时使用
总体
将已有对象赋值给另一个对象
具体
StringBad headline1("cat");<br><br>StringBad sports;<br>sports = headline1;//使用赋值运算符
说明
StringBad sports = headline1;
不一定使用赋值运算符。<br>可能使用复制构造函数直接创建sports——不使用赋值运算符<br>可能创建临时对象——使用赋值运算符
默认系统赋值运算符
浅复制
改进后的新string类
前言
String类
类声明
<div>// string1.h -- fixed and augmented string class definition</div><div><br></div><div>#ifndef STRING1_H_</div><div>#define STRING1_H_</div><div>#include <iostream></div><div>using std::ostream;</div><div>using std::istream;</div><div><br></div><div>class String</div><div>{</div><div>private:</div><div> char * str; // pointer to string</div><div> int len; // length of string</div><div> static int num_strings; // number of objects</div><div> static const int CINLIM = 80; // cin input limit</div><div>public:</div><div>// constructors and other methods</div><div> String(const char * s); // constructor</div><div> String(); // default constructor</div><div> String(const String &); // copy constructor</div><div> ~String(); // destructor</div><div> int length () const { return len; }</div><div>// overloaded operator methods </div><div> String & operator=(const String &);</div><div> String & operator=(const char *);</div><div> char & operator[](int i);</div><div> const char & operator[](int i) const;</div><div>// overloaded operator friends</div><div> friend bool operator<(const String &st, const String &st2);</div><div> friend bool operator>(const String &st1, const String &st2);</div><div> friend bool operator==(const String &st, const String &st2);</div><div> friend ostream & operator<<(ostream & os, const String & st);</div><div> friend istream & operator>>(istream & is, String & st);</div><div>// static function</div><div> static int HowMany();</div><div>};</div><div>#endif</div>
类定义
<div>// string1.cpp -- String class methods</div><div>#include <cstring> // string.h for some</div><div>#include "string1.h" // includes <iostream></div><div>using std::cin;</div><div>using std::cout;</div><div><br></div><div>// initializing static class member</div><div><br></div><div>int String::num_strings = 0;</div><div><br></div><div>// static method</div><div>int String::HowMany()</div><div>{</div><div> return num_strings;</div><div>}</div><div><br></div><div>// class methods</div><div>String::String(const char * s) // construct String from C string</div><div>{</div><div> len = std::strlen(s); // set size</div><div> str = new char[len + 1]; // allot storage</div><div> std::strcpy(str, s); // initialize pointer</div><div> num_strings++; // set object count</div><div>}</div><div><br></div><div>String::String() // default constructor</div><div>{</div><div> len = 4;</div><div> str = new char[1];</div><div> str[0] = '\0'; // default string</div><div> num_strings++;</div><div>}</div><div><br></div><div>String::String(const String & st)</div><div>{</div><div> num_strings++; // handle static member update</div><div> len = st.len; // same length</div><div> str = new char [len + 1]; // allot space</div><div> std::strcpy(str, st.str); // copy string to new location</div><div>}</div><div><br></div><div>String::~String() // necessary destructor</div><div>{</div><div> --num_strings; // required</div><div> delete [] str; // required</div><div>}</div><div><br></div><div>// overloaded operator methods </div><div><br></div><div> // assign a String to a String</div><div>String & String::operator=(const String & st)</div><div>{</div><div> if (this == &st)</div><div> return *this;</div><div> delete [] str;</div><div> len = st.len;</div><div> str = new char[len + 1];</div><div> std::strcpy(str, st.str);</div><div> return *this;</div><div>}</div><div><br></div><div> // assign a C string to a String</div><div>String & String::operator=(const char * s)</div><div>{</div><div> delete [] str;</div><div> len = std::strlen(s);</div><div> str = new char[len + 1];</div><div> std::strcpy(str, s);</div><div> return *this;</div><div>}</div><div><br></div><div> // read-write char access for non-const String</div><div>char & String::operator[](int i)</div><div>{</div><div> return str[i];</div><div>}</div><div><br></div><div> // read-only char access for const String</div><div>const char & String::operator[](int i) const</div><div>{</div><div> return str[i];</div><div>}</div><div><br></div><div>// overloaded operator friends</div><div><br></div><div>bool operator<(const String &st1, const String &st2)</div><div>{</div><div> return (std::strcmp(st1.str, st2.str) < 0);</div><div>}</div><div><br></div><div>bool operator>(const String &st1, const String &st2)</div><div>{</div><div> return st2 < st1;</div><div>}</div><div><br></div><div>bool operator==(const String &st1, const String &st2)</div><div>{</div><div> return (std::strcmp(st1.str, st2.str) == 0);</div><div>}</div><div><br></div><div> // simple String output</div><div>ostream & operator<<(ostream & os, const String & st)</div><div>{</div><div> os << st.str;</div><div> return os; </div><div>}</div><div><br></div><div> // quick and dirty String input</div><div>istream & operator>>(istream & is, String & st)</div><div>{</div><div> char temp[String::CINLIM];</div><div> is.get(temp, String::CINLIM);</div><div> if (is)</div><div> st = temp;</div><div> while (is && is.get() != '\n')</div><div> continue;</div><div> return is; </div><div>}</div><div><br></div>
新的默认构造函数
String::String()<br>{<br> len=0;<br> str=new char[1];<br> str[0]='\0';<br>}
str=new char[1]<br>不是 str=new char;原因
与类析构函数兼容,<br>既delete [] str
//也可以<br>str = new char[1];<br>str[0] = '\0';<br>//改成<br>str = 0;
因为delete [] 与使用new[]初始化的指针<br>和空指针都兼容。
其他
空指针
0
字面值0有两个意思,数值和空指针,<br>用(void*)0——标识空指针<br>
NULL
C宏定义
C++11<br>nullptr
eg:<br>str=nullptr;
比较成员函数
代码
bool operator<(const String &str1,const String &str2 )<br>{<br> return ( std::strcmp(st1.str , st2.str) < 0 );<br>}
bool operator>(const String &str1,const String &str2 )<br>{<br> return str2<str1;<br>}
bool operator==(const String &str1,const String &str2 )<br>{<br> return std::strcmp(st1.str , st2.str) ==0;<br>}
说明
可以作为内联函数
bool operator<(const String &str1 )
可以作为友元函数
友元函数有助于将String对象与常规的C字符串进行比较<br>//(因为是两个参数)
if("love" == answer )<br>转换为<br>if( operator==( “love”,answer ) )<br>if( operator==(String("love") , answer ) )
中括号表示法访问字符
操作数位置
city[0] // city 在前,0在操作数中间
操作数位置能代表什么??
代码
可修改
char & String::operator[](int i)<br>{<br> return str[i];<br>}
不可修改版本
const <b>char &</b> String::operator[]( int i ) const<br>{<br> return str[i];<br>}
说明
提供了访问私有函数方法。
返回引用
作用
给特定函数赋值
例子
String means("might");<br>means[0]='r';
问题
const String answer("fytile");<br>....<br>cout<<answer[1];// error
原因
answer 是const 不能返回不是const 的引用
//定义了不可修改版本就可以了。!!!!
解决问题
//重载一个[]运算符函数<br>const char & String::operator[](int i) const<br>{<br> return str[i];<br>}<br>
静态类成员函数
前言
成员函数可以声明为静态。独立定义函数(包括友元)不可以是静态的
代码
类声明中
class Strings{<br>......<br>public:<br> static int Howmany();<br>};
类定义中
int <b>String::</b>Howmany()<br>{<br> return num_strings;<br>}
调用方法
int count = String::Howmany();
不用带对象。
后果
对象不能调用函数
String str1;<br>str1.howmany();X不可行<br>//调用方法如上
不与特定的对象相关联<br>只能使用静态数据成员。
其他功能
使用静态成员函数设置类级标记,控制某些类接口的行为
类级标记可以控制显示类内容的方法所使用的格式。
进一步重载<br>赋值运算符
代码
原来代码
StringBad & StringBad::operator=(const StirngBad & st)<br>{<br> if (this=&that)<br> return *thid;<br> delete [] str;<br> len=st.len;<br> str=new char [len+1];<br> std::strcpy(str,st.str);<br> return *this;<br>}
问题
String name;<br>char temp[40];<br>cin.getline(temp,40);<br>name = temp;
temp是c-风格字符串,运行name = temp 时会产生<br>右列结果:<br>总结果:<br>//处理效率不高。
1. 构造函数String (const char *)创建一个临时String对象
2.使用String & String::operator=(const String &) 将临时对象复制到name对象中
3.调用析构函数~String删除临时对象
重载=赋值运算符<br>代码
StringBad & StringBad::operator=(const char * s)<br>{<br> delete [] str;<br> len=std::strlen(s);<br> str=new char [len+1];<br> std::strcpy(str,st.str);<br> return *this;<br>}
可以直接name = temp (c-风格字符串)了。
重载+运算符<br>//??
代码
MyString operator +(MyString &s)<br> {<br> MyString s1;<br> if(m_data==NULL)//可以不要这部吗?<br> {<br> s1.m_data=new char[strlen(s.m_data)+1];<br> strcpy(s1.m_data,s.m_data);<br> }<br> else<br> {<br> s1.m_data=new char[strlen(m_data)+strlen(s.m_data)+1];<br> strcpy(s1.m_data,m_data);<br> strcpy(<b>s1.m_data+strlen(m_data)</b>,s.m_data);<br> }<br> return s1;<br> }
重载+=运算符??
重载>>运算符
代码
//友元函数。<br>istream & operator>>( istream & is ,String &st )<br>{<br><div> char temp[String::CLNLIM];</div><div> is.get(temp,String::CINLIM);</div><div> if (is)//如果is(cin/文件)没有错误。错误:1.输入的是空行 。2.其它 </div><div><span class="Apple-tab-span" style="white-space:pre"> </span> st=temp;</div><div> while (is&&is.get()!='\n')</div><div><span class="Apple-tab-span" style="white-space:pre"> </span> continue;</div><div> return is; </div>}
说明
不直接<br>is.get(st, String::CLNLIM ),原因:输入可能是错误的。
早期的get(char * ,int )版本在读取空行后,返回的值部位false。<br>所以:<br>while( is && is.get()!='\n' )
在构造函数中使用new<br>时应注意的事项
注意事项
构造函数——new初始化指针成员。<br>析构函数——应该使用delete
new和delete要兼容。new--delete, new[]--delete[]
有多个构造函数,要都使用[]或者都不使用。<br>也可以把指针初始化为空指针(或直接=0)
应定义一个复制构造函数,通过深度复制将一个对象初始化为另一个对象
StringBad::String(const Stirng &st )<br>{<br> num_strings++;<br> len = st.len;<br> str = new char [len+1];<br> std::strcpy(str ,st.str);<br> ...<br>}
应定义一个赋值运算符,通过深度复制将一个对象初始化为另一个对象
StringBad & String::operator=(const Stirng & st)<br>{<br> if (this=&that)<br> return *thid;//检查自我赋值<br> delete [] str;//释放成员以前指向<br> len=st.len;<br> str=new char [len+1];//分配内存<br> std::strcpy(str,st.str);//<br> return *this;//返回引用<br>}
应该不应该
不应该1
String::String()<br>{<br> str="default string";<br> len=std::strlen(str);<br>}
delete[]释放不使用new初始化的指针,结果是不确定的
修改
String::String()<br>{<br> len = 0;<br> str = new char[1];<br> str[0] = '\0';<br>}
String::String()<br>{<br> len = 0;<br> str[0] = 0;//是str[0]=0还是str=0??都可以??个人感觉是后者<br>}
不应该2
String::String(const char *s)<br>{<br> len = std::strlen(s);<br> str = new char;//no room<br> strcpy(str, s);<br>}
只复制到s字符的第一个字
包含类成员的类的逐成员复制
class Magine<br>{<br>private:<br> String title;<br> String publisher;<br>...<br>};
将一个Magine对象赋值给另一个Magzine对象,<br>(默认的逐成员复制)会使用成员的类型定义的复制构造函数。<br>(默认的赋值行为也是这样)。
如果Magine中还有需要new分配的成员数据,需要定义复制构造函数<br>(赋值函数),情况会复杂点。13章
有关返回对象的说明
返回指向const对象的引用
好处
提高效率
要求
返回的对象引用,要在主程序中存在。<br>例如,返回的对象作为函数的参数形式传递给被调函数。<br>不能返回在被调函数中声明的对象。
只能作为右值
说明
返回对象将调用复制构造函数,返回引用不调用
返回指向非const对象的引用
常见情形
重载赋值运算符
原因
提高效率//(相对于非引用来说),<br> 赋值的数一般为变量,所以是非const引用。
重载与cout一起使用的<<运算符
原因
必须这样做
ostream & operator<<(ostream & os, string s)<br>{<br> ....<br>}<br>ostream 类没有公用的复制构造函数
返回对象
情形
返回的对象是被调用函数中的局部变量
返回const对象
情形
Vector类:<br>Vector force1(50,60);<br>Vector force2(10,70);<br>Vector net;<br>force1+force2=net;//valid,no meaningful
分析
复制构造函数创建一个临时对象表示force1+force2的结果。<br>net把值赋给了临时对象。<br>使用完临时对象后,将它丢弃。
避免这种问题返回const对象,使引用或者指针的返回值不能用于修改对象中的数据。
对象此后能够修改???
const修饰函数返回值。<br> 1、函数返回const指针,表示该指针不能被改动,只能把该指针赋给const修饰的同类型指针变量。<br> 2、函数返回值为值传递,函数会把返回值赋给外部临时变量,用const无意义!不管是内部还是非内部数据类型。<br> 3、函数采用引用方式返回的场合不多,只出现在类的赋值函数中,目的是为了实现链式表达。
使用指向对象的指针
应用
不用new
声明
String *ptr ;
初始化
String sayings ;<br>String *pclass = &saying;
引用
*pclass
cout<<*pclass;//重载了<<运算符。
pclass->length();<br>(*pclass).length()
new
初始化
String sayings ;<br>String *pclass = new String(sayings) ;
用复制构造函数。此时saying与pclass无关系。(虽然值是一样的)<br>即pclass不是指向saying。
String *pclass = new String(" my my my ")
String sayings ;<br>String *pclass = new String;<br>//使用默认构造函数String( );
引用
ditto
释放动态分配对象,<br>释放对象的动态分配数据成员
String *favourite = new String( saying s[choice] );<br>delete favourite;
原理
delete favourite 时释放整个对象,(即释放保存str指针和len成员空间)。<br>再调用对象析构函数释放str(内部数据成员)的空间
析构函数被调用的情况
对象是动态变量
执行完定义该对象的程序块
对象是静态变量<br>(外部、静态、静态外部或来自名称空间)
程序结束时
对象时new创建的
<b>显式使用delete删除对象时</b>,析构函数才被调用
定位new运算符
代码
class JustTesting<br>{<br>private:<br> string words;<br> int number;<br>public:<br> JustingTesting(const string & s = "Justing Testing",int n = 0 )<br> {words = s; number = n; cout<<words<<"constructed\n";}<br> ~JustTesting(){cout<<words<<"destoryed\n";}<br> void Show() const {cout<<words<<","<<number<<endl;} <br>};<br><br>int main()<br>{<br> .......<br> char * buffer = new char[BUF];<br> JustTesting *pc1,*pc2;<br> <br> pc1=new (buffer) JustTesting;//会得到 pc1=buffer,即他们指向的地址是一样的。(指向同一个堆内存单位)<br> pc2=new Justing("Heap1",20);<br> .........<br> pc3=new (buffer) JustTesting("Bad Idea",6);<br> pc4=new JustTesting("Heap2",10);<br> delete pc2;<br> delete pc4;<br> delete [] buffer;<br>}
问题
一
代码
pc1=new(buffer)JustTesting;<br>pc3=new(buffer)JustTesting("BadIdea",6)
原因
定位new使用一个新对象pc3来覆盖用于第一个对象pc1的内存单元。<br>如果类动态地为其数据成员分配内存,会出现问题(指向丢失)。
修改
pc1=new(buffer)JustTesting;<br>pc3=new(buffer + sizeof(JustTesting))JustTesting("BadIdea",6);
二
代码
delete pc2;<br> delete pc4;<br> delete [] buffer;<br>//<br>delete pc1/pc3;//invalid
原因
pc1,pc3不能delete,无法调用析构函数,无法释放数据成员动态分配的对象。<br>因为pc1、pc2已经释放(因为buffer已经释放)
子主题
修改
方法
显式地为使用定位new运算符创建的对象调用析构函数。
格式
<b>pc3->~JustTesting();</b><br>pc1->~JustTesting();
如果Pc3不是指针:<br>&pc3->JustTesting
说明
注意顺序:<br>用创建顺序相反的顺序删除。<br>
析构函数不用有什么特殊变化
队列的模拟
代码
头文件
//queue.h<br>#ifudef QUEUE_H_<br>#define QUEUE_H_<br><br>//contain Customer items<br>class Customer{<br>private:<br> long arrive;//到达时间 <br> int processtime;//到柜员机处理的时间 <br>public:<br> Customer(){ arrivate = processtime = 0; }<br> void set(long when);<br> long when() const{return arrive;}<br> int ptime() const {return processtime;}<br>};<br><br>typedef Customer Item;<br><br>class Queue{<br>private:<br> struct Node{<br> Item item;<br> struct Node *next;<br> };<br> enum{Q_SIZE = 10};<br> Node * front;<br> Node * rear;<br> int items;//计算队列的数。<br> const int qsize;//私有函数常量<br> <br> Queue(const Queue&q):qsize(0){ }//,私有函数接口。 <br> Queue & operator = (const Queue & q){ return *this; }<br><br>public:<br> Queue(int qs = Q_SIZE);<br> ~Queue();<br> bool isempty() const;//判断是否空 <br> bool isfull() const;//判断是否满 <br> int queuecount() const;//队列数 <br> bool enqueue(const Item & item);//加入队列 <br> bool dequeue(Item &item);<br>}; <br>#endif<br>
源代码1
#include"queue.h"<br>#include<cstdlib><br><br>Queue::Queue(int qs):qsize(qs)//常量只能这样初始化 <br>{<br> front = rear = NULL;<br> iterms = 0;<br>}<br><br>Queue::~Queue()<br>{<br> Node *temp;<br> while (front != NULL)//当队列还不是空的时候 ,要删除队列的每一个节点。 <br> {<br> temp = front;<br> front = front->next;<br> delete temp;<br> }<br>}<br><br>//判断队列是否空 <br>bool Queue::isempty() const<br>{<br> return iterms == 0;<br>}<br><br>//判断队列是否满 <br>bool Queue::isfull() const<br>{<br> return iterms;<br>}<br><br>//返回队列的数 <br>int Queue::queuecount()const<br>{<br> return iterms;<br>}<br>bool Queue::enqueue(const Item &item)<br>{<br> if ( isfull() )<br> return false;<br> <br> //创建一个新节点<br> Node *add = new Node;<br> <br> // 设置新节点的信息 <br> add->item = item; <br> add->next = NULL;<br> <br> iterm++;<br> <br> //插入新节点到队列中 <br> if (front == NULL)<br> front = add;<br> else <br> rear->next = add;<br> rear = add;<br> return true; <br> <br>}<br><br><br>bool Queue::dequeue(Item &item)<br>{<br> if (front == NULL)<br> return false;<br> item = front->item;//返回iterm(顾客) 的信息 <br> items--;<br> Node *temp = front; <br> front = front ->next;<br> delete temp;<br> <br> /* <br> 注意不可以直接 <br> front = front ->next;要有temp,删除动态分配的空间。 <br> */<br> <br> if (items==0)<br> rear = NULL;<br> return ture;<br> <br>}<br><br>void Customer::set(long when)<br>{<br> processtime = std::rand %3+1;//顾客在柜员机处理的时间随机。 <br> arrive = when ;//when 表示顾客到队列的时间 <br>}
源代码2
#include<isotream><br>#include<cstdlib><br>#include<ctime><br>#include"queue.h"<br>const int MIN_PER_HR = 60;<br><br>bool newcustomer(double x);<br><br>int main()<br>{<br> using std::cin;<br> using std::cout;<br> using std::endl;<br> using std::ios_basel<br> <br> std::srand(std::time(0));<br> <br> cout<<"Case Study: Bank of Heather Automatic Teller\n";<br> cout<<"Enter maximum size od queue:";<br> int qs;//队列的大小。(用户自定义设置) <br> cout >>qs;<br> Queue line(qs);<br> <br> cout<<"Enter the number of simulation hours:";<br> int hours;//模拟的时间 ,小时为单位 <br> cin>>hours; <br> <br> long cyclelimit = MIN_PER_HR *hours;//模拟的时间变为分钟 <br> <br> cout<<"Enter the average number of customers per hour:";<br> double perhour;//每小时的平均人流量 <br> cin>>perhour; <br> double min_per_cust;<br> min_per_cust = MIN_PER_HR/perhour;// 每小时的平均人流量变为每分钟的平均人流量。<br> <br> Item temp; // 新顾客的数据 <br> long turnaways = 0; //超过最大队列返回的顾客人数 <br> long customers = 0;//加入队列的顾客数(不一定在ATM机处理过数据?) <br> long served = 0;// 在ATM机处理过的顾客数 <br> long sum_line = 0;//??感觉与customers差不多? <br> int wait_time = 0;// 下一个顾客等待在ATM机处理的时间。 <br> long line_wait = 0;// 在队列的顾客一共得等待时间 <br>} <br><br>for (int cycle =0 ;cycle<cyclelimit; cycle++) //用循环次数来模拟的时刻 <br>{<br> //顾客加入队列 <br> if (newcustomer(min_per_cust)) //newcustomer(min_per_cust) 模拟顾客到来的时间<br> {<br> if (line.isfull())<br> turnaways++;<br> else<br> {<br> customers++;<br> temp.set(cycle);//设置顾客到来的时刻 <br> line.enqueue(temp);//加入队列 <br> } <br> } <br> <br> //顾客在ATM机上处理<br> if (wait_time<=0 &&!line.isempty())<br> {<br> line.dequeue(temp);//执行完后temp变成了队首的temp; <br> wait_time = temp.ptime();//更新wait_time, 队首temp的处理时间变成了下一个顾客的等待时间 <br> line_wait += cycle - temp.when(); //一个顾客的等待时间 = 处理时间-到来的时间 <br> served++;<br> } <br> if (wait_time > 0 )<br> wait_time--;<br> <br> sum_line += line.queuecount();<br> <br> //显示结果<br> if (customers > 0 )<br> {<br> cout<<"customers accepted:"<<customers<<endl;<br> cout<<" customers served:"<<served<<endl;<br> cout<<" turnaways:"<<turnaways<<endl;<br> cout<<"average queue size:"<br> cout.precision(2);<br> cout.setf(ios_base::fixed,ios_base::floatfield);<br> cout<<(double) sum_line/cyclelimit<<endl;<br> cout<<" average wait time:"<br> <<(double)line_wait/served<<" minutes\n"; <br> } <br> else <br> cout<<"No customers!\n";<br> cout<<"Done!\n";<br> <br> return 0;<br>}<br><br><br>bool newcustomer(double x)<br>{<br> return (std::rand()*x/RAND_MAX <1);//0 < std::rand()*x/RAND_MAX < x; RAND_MAX是循环中能产生的最大数。rand()% x这样也可以吧? <br>}
queue类
功能<br>(特征)
队列存储有序的项目序列
队列能容纳的项目数有一定的限制
应当能够创建空队列
应当能够检查队列是否为空
应当能够检查队列是否是满的
应当能够在队尾添加项目
应当能够从队首删除项目
应当能够确定队列中的项目数
公有接口
内容
public:<br> Queue(int qs = Q_SIZE);<br> ~Queue();<br> bool isempty() const;//判断是否空 <br> bool isfull() const;//判断是否满 <br> int queuecount() const;//队列数 <br> bool enqueue(const Item & item);//加入队列 <br> bool dequeue(Item &item);
私有数据
内容
private:<br> struct Node{<br> Item item;<br> struct Node *next;<br> };<br> enum{Q_SIZE = 10};<br> Node * front;//指向队首的指针<br> Node * rear;//指向队尾的指针<br> int items;//计算队列的数。<br> const int qsize;//私有函数常量<br><br> Queue(const Queue & q ):qsize(0){ }//只有构造函数才能这样子 ,私有函数接口。 <br> Queue & operator = (const Queue & q){ return *this; }
struct Node{ .. }
结构在类的私有部分声明,只能在类中使用Node.<br>在公有部分声明,可以在类外面声明Queue::Node类型的变量。
struct Node 不是一个变量!!!
类方法
Queue()构造函数
功能
初始化qsize,给队首队尾赋0值。
代码
Queue::Queue(int qs):qsize(qs)//常量只能这样初始化 <br>{<br> front = rear = NULL;<br> iterms = 0;<br>}<br>
qsize常量
qsize是常量,不能给它赋值
Queue::Queue(int qs)<br>{<br> front = rear = NULL;<br> iterms = 0;<br> qsize = qs;//invalid;<br>}
原理
调用析构函数时,对象已经创建了(程序为数据成员分配内存)。<br>再执行析构函数的内容,使用常规赋值方式将值存储到内存中。<br>所以不可以直接qsize = qs
成员初始化列表
功能原理
能够在创建对象时初始化数据变量/常量
语法
Classy::Classy(int n , int m):mem1(n), mem2(0),men( n*m + 2 )<br>{ }
例子
Queue::Queue(int qs) : qsize(qs),front(NULL),rear(NULL),item(0)<br>{ }
注意
这种格式只能用于构造函数
也不能用于原型
必须用这种格式来初始化非静态const数据成员(在C++11之前要这样)
必须用这种格式来初始化引用数据成员.<br>//引用数据成员只能初始化
class Agency{ .... }; <br>class Agent<br>{<br>private:<br> Agency & belong;<br> ....<br>};<br><br>Agent::Agent(Agency & a ) : belong(a) {...}
选择
对于简单数据成员,没什么区别。<br>对象本身是类对象成员,使用成员初始化列表效率更高。
C++11类的初始化
class Classy<br>{<br> int men1 = 10;<br> const int men2 = 20;<br>//...<br>}
=
Classy::Classy( ):mem1(10),mem2(20) { .... }
Classy::Classy( ):mem1(n) { .... }
men2被赋予了10,mem2以让是20
入队
代码
bool Queue::enqueue(const Item &item)<br>{<br>//判断是否满<br>if ( isfull() )<br> return false;<br><br> //创建一个新节点<br> Node *add = new Node;<br><br> // 设置新节点的信息 <br> add->item = item; <br> add->next = NULL;<br><br> iterm++;//项目数加1<br><br> //插入新节点到队列中 <br><br>/情况1:<br> if (front == NULL)<br> front = add;<br> else <br> rear->next = add;<br><br> rear = add;<br> return true; <br><br>}
出队
代码
说明
temp 是必不可少的。要freefront指向的动态分配的内存空间。
析构函数
前
构造函数没有用new分配内存,所以看上去析构函数不用使用 delete。(这是错误的)<br>因为front 结构变量有*,new分配的对象,所以要用delete
代码
Queue::~Queue()<br>{<br> Node *temp;<br> while (front != NULL)//当队列还不是空的时候 ,要删除队列的每一个节点。 <br> {<br> temp = front;<br> front = front->next;<br> delete temp;<br> }<br>}
复制构造函数\赋值函数
说明
不能使用系统的默认的复制构造函数和赋值函数。<br>因为包含了使用new的数据成员(front / rear ).<br>如果使用了默认的。。会共享同一个链表。结果会很严重
这里不给出深层的复制构造函数和赋值函数。给出避免产生错误的方法
方法
1.放在私有函数中
private:<br> ......<br> Queue(const Queue&q):qsize(0){ }//私有函数接口。 <br> Queue & operator = (const Queue & q){ return *this; }
Queue snick(nip);// no ok<br>tuck = nip ;//no ok
2.当对象按值传递(或返回)时,复制构造函数还被用于创建其他的临时对象。
尽量按引用传递
3.如果重载了加法运算符,会导致创建临时对象操作。
customer类
调用
用循环cycle 来模拟时刻
for (int cycle =0 ;cycle<cyclelimit; cycle++)
子主题
bool newcustomer(double x)<br>{<br> return ( std::rand()*x/RAND_MAX <1 );//0 < std::rand()*x/RAND_MAX < x; RAND_MAX是循环中能产生的最大 数。rand()% x这样也可以吧? <br>}
Collect
Get Started
Collect
Get Started
Collect
Get Started
Collect
Get Started
评论
0 条评论
下一页