赞
踩
stringbad.h
#ifndef PRIMERPLUS_STRINGBAD_H #define PRIMERPLUS_STRINGBAD_H #include <iostream> class StringBad { private: char *str; // 类声明没有为字符串本身分配存储空间 int len; static int num_strings; // 无论创建了多少对象,程序都只创建一个静态类变量副本。 public: // 在构造函数中使用new来为字符串分配空间。 StringBad(); // 默认构造函数 StringBad(const char * s); // 自定义构造函数 StringBad(const StringBad & s); // 复制构造函数 StringBad & operator=(const StringBad & st); // 赋值构造函数 ~StringBad(); friend std::ostream & operator<<(std::ostream & os, const StringBad & st); }; #endif //PRIMERPLUS_STRINGBAD_H
stringbad.cpp
#include <cstring> #include "stringbad.h" using std::cout; int StringBad::num_strings = 0; // 初始化静态变量,用于记录创建的类数量 StringBad::StringBad(const StringBad & st) { num_strings++; // handle static member update len = st.len; // same length str = new char [len + 1]; // allot space std::strcpy(str, st.str); // copy string to new location cout << num_strings << ": \"" << str << "\" object created\n"; // For Your Information } StringBad & StringBad::operator=(const StringBad & st) { if (this == &st) // object assigned to itself return *this; // all done delete [] str; // free old string len = st.len; str = new char [len + 1]; // get space for new string std::strcpy(str, st.str); // copy the string return *this; // return reference to invoking object } StringBad::StringBad() // 在构造函数中使用new来为字符串分配空间 { len = 6; str = new char[6]; std::strcpy(str, "happy"); num_strings++; cout << num_strings << " : \"" << str << "\" object created.\n"; } StringBad::StringBad(const char * s) { // str = s; // 这只保存了地址,而没有创建字符串副本。 len = std::strlen(s); // 不包括末尾的空字符 str = new char[len+1]; // 使用new分配足够的空间来保存字符串,然后将新内存的地址赋给str成员。 std::strcpy(str, s); num_strings++; cout << num_strings << " : \"" << str << "\" object created.\n"; } StringBad::~StringBad() { cout << "\"" << str << "\" object delete, "; --num_strings; cout << num_strings << " left.\n"; delete [] str; } std::ostream & operator<<(std::ostream & os, const StringBad & st) { os << st.str; return os; }
usestringbad.cpp
#include "stringbad.h" using std::cout; void callme1(StringBad & rsb); void callme2(StringBad sb); int main(void) { using std::endl; { cout << "Starting an inner block.\n"; StringBad headline1("Celery Stalks at Midnight"); StringBad headline2("Lettuce Prey"); StringBad sports("Spinach Leaves Bowl for Dollars"); cout << "headline1: " << headline1 << endl; cout << "headline2: " << headline2 << endl; cout << "sports: " << sports << endl; callme1(headline1); cout << "headline1: " << headline1 << endl; callme2(headline2); // 复制构造函数被用来初始化 callme2()的形参 cout << "headline2: " << headline2 << endl; cout << "Initialize one object to another:\n"; StringBad sailor = sports; // 复制构造函数,StringBad sailor = StringBad(sports); cout << "sailor: " << sailor << endl; cout << "Assign one object to another:\n"; StringBad knot; knot = headline1; // 赋值构造函数,knot.operator=(headline1); cout << "knot: " << knot << endl; cout << "Exiting the block.\n"; } // 该代码块执行完调用析构函数,否则要main函数执行完调用析构函数。 cout << "End of main()\n"; return 0; } void callme1(StringBad & rsb) { cout << "String passed by reference:"; cout << " \"" << rsb << "\"\n"; } void callme2(StringBad sb) { cout << "String passed by value:"; cout << " \"" << sb << "\"\n"; }
out:
Starting an inner block. 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 4: "Lettuce Prey" object created String passed by value: "Lettuce Prey" "Lettuce Prey" object delete, 3 left. headline2: Lettuce Prey Initialize one object to another: 4: "Spinach Leaves Bowl for Dollars" object created sailor: Spinach Leaves Bowl for Dollars Assign one object to another: 5 : "happy" object created. knot: Celery Stalks at Midnight Exiting the block. "Celery Stalks at Midnight" object delete, 4 left. "Spinach Leaves Bowl for Dollars" object delete, 3 left. "Spinach Leaves Bowl for Dollars" object delete, 2 left. "Lettuce Prey" object delete, 1 left. "Celery Stalks at Midnight" object delete, 0 left. End of main()
C++自动提供了下面这些成员函数:
如果定义了构造函数,C++将不会定义默认构造函数。如果希望在创建对象时不显式地对它进行初始化,则必须显式地定义默认构造函数。
带参数的构造函数也可以是默认构造函数,只要所有参数都有默认值。但只能有一个默认构造函数。
复制构造函数用于将一个对象复制到新创建的对象中。
例如:StringBad类的复制构造函数原型:StringBad(const StringBad &);
何时调用:新建一个对象并将其初始化为同类现有对象时,复制构造函数都将被调用。
// 以下四种情况都将调用复制构造函数,假设motto是一个StringBad对象
StringBad ditto(motto);
StringBad metoo = motto; // 可能直接创建,也可能生成一个临时对象
StringBad also = StringBad(motto); // 可能直接创建,也可能生成一个临时对象
StringBad * pStringBad = new StringBad(motto); // 初始化一个匿名对象,并将新对象的地址赋给pstring指针。
每当程序生成了对象副本时,编译器都将使用复制构造函数。(当函数按值传递对象或函数返回对象时,都将使用复制构造函数。)
由于按值传递对象将调用复制构造函数,因此应该按引用传递对 象。这样可以节省调用构造函数的时间以及存储新对象的空间。
有何作用:默认的复制构造函数逐个复制非静态成员(成员复制也称为浅复制),复制的是成员的值。
如果成员本身就是类对象,则将使用这个类的复制构造函数来复制成员对象。静态函数(如num_strings)不受影响,因为它们属于整个类,而不是各个对象。
必须定义复制构造函数的原因:
// StringBad类的显式复制函数
StringBad::StringBad(const StringBad & s)
{
num_strings++; // 静态数据成员
...// important stuff to go here
}
赋值构造函数的特点:
// 赋值操作并不创建新的对象,因此不需要调整静态数据成员num_strings的值。
StringBad & StringBad::operator=(const StringBad & st)
{
if (this == &st) // object assigned to itself
return *this; // all done
delete [] str; // free old string
len = st.len;
str = new char [len + 1]; // get space for new string
std::strcpy(str, st.str); // copy the string
return *this; // return reference to invoking object
}
char *str;
str = nullptr; // 空指针
str = 0; // 空指针
str = NULL; // 空指针
要实现字符串比较函数,最简单的方法是使用标准的trcmp()函数:
bool operator<(const String &st1, const String &st2)
{
return (std::strcmp(st1.str, st2.str) < 0);
}
bool operator>(const String &st1, const String &st2)
{
return st2 < st1;
}
bool operator==(const String &st1, const String &st2)
{
return (std::strcmp(st1.str, st2.str) == 0);
}
将比较函数作为友元,有助于将String对象与常规的C字符串进行比较。
对于中括号运算符,一个操作数位于第一个中括号的前面,另一个操作数位于两个中括号之间。
char & String::operator[](int i) { return str[i]; } // 调用 String opera("The Magic Flute"); cout << opera[4]; // cout << opera.operator[](4); // 将返回类型声明为char &,便可以给特定元素赋值。 String means("might"); means[0] = 'r'; // means.operator[](0) = 'r'; means.str[0] = 'r'; // 提供另一个仅供const String对象使用的operator版本: const char & String::operator[](int i) const { return str[i]; } // 有了const版本可以读/写常规String对象,对于const String对象,则只能读取其数据: String text("Once upon a time"); const String answer("futile"); cout << text[1]; // ok, uses non-const version of operator[]() cout << answer[1]; // ok, uses const version of operator[]() cin >> text[1]; // ok, uses non-const version of operator[]() //cin >> answer[1]; // compile-time error
// 在String类声明中添加如下原型
static int HowMany() { return num_strings; }
// 调用
int count = String::HowMany(); // invoking a static member function
重载赋值运算符,使之能够直接将常规字符串复制到String对象中。这样就不用创建和删除临时对象了。
String & String::operator=(const char * s)
{
delete [] str;
len = std::strlen(s);
str = new char[len + 1];
std::strcpy(str, s);
return *this;
}
string1.h
#ifndef PRIMERPLUS_STRING1_H #define PRIMERPLUS_STRING1_H #include <iostream> using std::ostream; using std::istream; class String { private: char * str; // pointer to string int len; // length of string static int num_strings; // number of objects static const int CINLIM = 80; // cin input limit public: // constructors and other methods String(const char * s); // constructor String(); // default constructor String(const String &); // copy constructor ~String(); // destructor int length () const { return len; } // overloaded operator methods String & operator=(const String &); String & operator=(const char *); char & operator[](int i); const char & operator[](int i) const; // overloaded operator friends friend bool operator<(const String &st, const String &st2); friend bool operator>(const String &st1, const String &st2); friend bool operator==(const String &st, const String &st2); friend ostream & operator<<(ostream & os, const String & st); friend istream & operator>>(istream & is, String & st); // static function static int HowMany(); }; #endif //PRIMERPLUS_STRING1_H
string1.cpp
// string1.cpp -- String class methods #include <cstring> // string.h for some #include "string1.h" // includes <iostream> using std::cin; using std::cout; // initializing static class member int String::num_strings = 0; // static method int String::HowMany() { return num_strings; } // class methods String::String(const char * s) // construct String from C string { len = std::strlen(s); // set size str = new char[len + 1]; // allot storage std::strcpy(str, s); // initialize pointer num_strings++; // set object count } String::String() // default constructor { len = 4; str = new char[1]; str[0] = '\0'; // default string num_strings++; } String::String(const String & st) { num_strings++; // handle static member update len = st.len; // same length str = new char [len + 1]; // allot space std::strcpy(str, st.str); // copy string to new location } String::~String() // necessary destructor { --num_strings; // required delete [] str; // required } // overloaded operator methods // assign a String to a String String & String::operator=(const String & st) { if (this == &st) return *this; delete [] str; len = st.len; str = new char[len + 1]; std::strcpy(str, st.str); return *this; } // assign a C string to a String String & String::operator=(const char * s) { delete [] str; len = std::strlen(s); str = new char[len + 1]; std::strcpy(str, s); return *this; } // read-write char access for non-const String char & String::operator[](int i) { return str[i]; } // read-only char access for const String const char & String::operator[](int i) const { return str[i]; } // overloaded operator friends bool operator<(const String &st1, const String &st2) { return (std::strcmp(st1.str, st2.str) < 0); } bool operator>(const String &st1, const String &st2) { return st2 < st1; } bool operator==(const String &st1, const String &st2) { return (std::strcmp(st1.str, st2.str) == 0); } // simple String output ostream & operator<<(ostream & os, const String & st) { os << st.str; return os; } // quick and dirty String input // 重载>>运算符提供了一种将键盘输入行读入到String对象中的简单方法。 // 它假定输入的字符数不多于String::CINLIM的字符数,并丢弃多余的字符。 // 在if条件下,如果由于某种原因 // (如到达文件尾或get(char *, int)读取的是一个空行)导致输入失败,istream对象的值将置为 false。 istream & operator>>(istream & is, String & st) { char temp[String::CINLIM]; is.get(temp, String::CINLIM); if (is) st = temp; while (is && is.get() != '\n') continue; return is; }
usestring1.cpp
// sayings1.cpp -- using expanded String class // compile with string1.cpp // 程序首先提示用户输入,然后将用户输入的字符串存储到 String对象中,并显示它们, // 最后指出哪个字符串最短、哪个字符串按 字母顺序排在最前面。 #include <iostream> #include "string1.h" const int ArSize = 10; const int MaxLen = 81; int main() { using std::cout; using std::cin; using std::endl; String name; cout <<"Hi, what's your name?\n>>"; cin >> name; cout << name << ", please enter up to " << ArSize << " short sayings <empty line to quit>:\n"; String sayings[ArSize]; // array of objects char temp[MaxLen]; // temporary string storage int i; for (i = 0; i < ArSize; i++) { cout << i+1 << ":"; cin.get(temp, MaxLen); while (cin && cin.get() != '\n') continue; if (!cin || temp[0] == '\0') // empty line? break; // i not incremented else sayings[i] = temp; // overloaded assignment } int total = i; // total # of lines read if ( total > 0) { cout << "Here are your sayings:\n"; for (i = 0; i < total; i++) cout << sayings[i][0] << ": " << sayings[i] << endl; int shortest = 0; int first = 0; for (i = 1; i < total; i++) { if (sayings[i].length() < sayings[shortest].length()) shortest = i; if (sayings[i] < sayings[first]) first = i; } cout << "Shortest saying:\n" << sayings[shortest] << endl;; cout << "First alphabetically:\n" << sayings[first] << endl; cout << "This program used "<< String::HowMany() << " String objects. Bye.\n"; } else cout << "No input! Bye.\n"; return 0; }
String::String() { len = 0; str = new char[1]; // uses new with [] str[0] = '\0'; } String::String() { len = 0; str = 0; // or, with C++11, str = nullptr; } String::String() { static const char * s = "C++"; // initialized just once len = std::strlen(s); str = new char[len + 1]; // uses new with [] std::strcpy(str, s); }
假设类成员的类型为String类或标准string类:
class Magazine
{
private:
String title;
string publisher;
...
};
当成员函数或独立的函数返回对象时:可以返回指向对象的引用、指向对象的const引用或const对象。
如果函数返回(通过调用对象的方法或将对象作为参数)传递给它的对象,可以通过返回引用来提高其效率。
// 例如,假设要编写函数Max(),它返回两个Vector对象中较大的一个, // 其中Vector 是第11章开发的一个类。 Vector force1(50,60); Vector force2(10,70); Vector max; max = Max(force1, force2); // version 1 Vector Max(const Vector & v1, const Vector & v2) { if (v1.magval() > v2.magval()) return v1; else return v2; } // version 2 const Vector & Max(const Vector & v1, const Vector & v2) { if (v1.magval() > v2.magval()) return v1; else return v2; }
两种常见的返回非const对象情形是,重载赋值运算符以及重载与cout一起使用的<<运算符。前者这样做旨在提高效率,而后者必须这样做。
Operator<<()的返回值用于串接输出:返回类型必须是ostream &,而不能仅仅是ostream。如果使用返回类型ostream,将要求调用ostream类的复制构造函数,而ostream没有公有的复制构造函数。幸运的是,返回一个指向cout的引用不会带来任何问题,因为cout已经在调用函数的作用域内。
如果被返回的对象是被调用函数中的局部变量,则不应按引用方式返回它,因为在被调用函数执行完毕时,局部对象将调用其析构函数。 因此,当控制权回到调用函数时,引用指向的对象将不再存在。在这种情况下,应返回对象而不是引用。
下面的例子:
构造函数调用Vector(x + b.x,y + b.y)创建一个方法operator+()能够访问的对象;
而返回语句引发的对复制构造函数的隐式调用创建一个调用程序能够访问的对象。
Vector force1(50,60);
Vector force2(10,70);
Vector net;
net = force1 + force2;
// 返回的不是force1,也不是force2,force1和force2在这个过程中应该保持不变。
// 因此,返回值不能是指向在调用函数中已经存在的对象的引用。
// 在Vector::operator+( )中计算得到的两个矢量的和被存储在一个新的临时对象中,
// 该函数也不应返回指向该临时对象的引用,
// 而应该返回实际的Vector对象,而不是引用。
Vector Vector::operator+(const Vector & b) const
{
return Vector(x + b.x, y + b.y);
}
上述返回对象的+运算符重载函数可以这样使用:
net = force1 + force2; // 1: three Vector objects
force1 + force2 = net; // 2: dyslectic programming
cout << (force1 + force2 = net).magval() << endl; // 3: demented programming
如果Class_name是类,value的类型为Type_name,则下面的语句:
Class_name * pclass = new Class_name(value);
将调用如下构造函数:
Class_name(Type_name);
或者Class_name(const Type_name &);
另外,如果不存在二义性,则将发生由原型匹配导致的转换(如从int到double)。下面的初始化方式将调用默认构造函数:
Class_name * ptr = new Class_name;
usestring2.cpp
// compile with string1.cpp // 最初,shortest指针指向数组中的第一个对象。 // 每当程序找到比指向的字符串更短的对象时,就把shortest重新设置为指向该对象。 // 同样,first指针跟踪按字母顺序排在最前面的字符串。 // 这两个指针并不创建新的对象,而只是指向已有的对象。因此,这些指针并不要求使用new来分配内存。 #include <iostream> #include <cstdlib> // (or stdlib.h) for rand(), srand() #include <ctime> // (or time.h) for time() #include "string1.h" const int ArSize = 10; const int MaxLen = 81; int main() { using namespace std; String name; cout <<"Hi, what's your name?\n>>"; cin >> name; cout << name << ", please enter up to " << ArSize << " short sayings <empty line to quit>:\n"; String sayings[ArSize]; char temp[MaxLen]; // temporary string storage int i; for (i = 0; i < ArSize; i++) { cout << i+1 << ":"; cin.get(temp, MaxLen); while (cin && cin.get() != '\n') continue; if (!cin || temp[0] == '\0') // empty line? break; // i not incremented else sayings[i] = temp; // overloaded assignment } int total = i; // total # of lines read if (total > 0) { cout << "Here are your sayings:\n"; for (i = 0; i < total; i++) cout << sayings[i] << "\n"; // use pointers to keep track of shortest, first strings String * shortest = &sayings[0]; // initialize to first object String * first = &sayings[0]; for (i = 1; i < total; i++) { if (sayings[i].length() < shortest->length()) shortest = &sayings[i]; if (sayings[i] < *first) // compare values first = &sayings[i]; // assign address } cout << "Shortest saying:\n" << * shortest << endl; cout << "First alphabetically:\n" << * first << endl; srand(time(0)); int choice = rand() % total; // pick index at random // use new to create, initialize new String object String * favorite = new String(sayings[choice]); // 指针favorite指向new创建的未被命名对象。 cout << "My favorite saying:\n" << *favorite << endl; delete favorite; } else cout << "Not much to say, eh?\n"; cout << "Bye.\n"; return 0; }
String * favorite = new String(sayings[choice]);
这不是为要存储的字符串分配内存,而是为对象分配内存;也就是说,为保存字符串地址的str指针和len成员分配内存(程序并没有给num_string成员分配内存,这是因为num_string成员是静态成员,它独立于对象被保存)。创建对象将调用构造函数,后者分配用于保存字符串的内存,并将字符串的地址赋给str。然后,当程序不再需要该对象时,使用delete删除它。对象是单个的,因此,程序使用不带中括号的delete。与前面介绍的相同,这将只释放用于保存str指针和len成员的空间,并不释放str指向的内存,而该任务将由析构函数来完成。
在下述情况下析构函数将被调用(参见图12.4):
都在这两张图里了。
定位new运算符让您能够在分配内存时能够指定内存位置。
以下程序使用了定位new运算符和常规new运算符给对象分配内存。
// placenew1.cpp -- new, placement new, no delete // 使用了定位new运算符和常规new运算符给对象分配内存. // 该程序使用new运算符创建了一个512字节的内存缓冲区, // 然后使用new运算符在堆中创建两个JustTesting对象, // 并试图使用定位new运算符在内存缓冲区中创建两个JustTesting对象。 // 最后,它使用delete来释放使用new分配的内存。 #include <iostream> #include <string> #include <new> using namespace std; const int BUF = 512; class JustTesting { private: string words; int number; public: JustTesting(const string & s = "Just Testing", int n = 0) {words = s; number = n; cout << words << " constructed\n"; } ~JustTesting() { cout << words << " destroyed\n";} void Show() const { cout << words << ", " << number << endl;} }; int main() { char * buffer = new char[BUF]; // get a block of memory JustTesting *pc1, *pc2; pc1 = new (buffer) JustTesting; // place object in buffer pc2 = new JustTesting("Heap1", 20); // place object on heap cout << "Memory block addresses:\n" << "buffer: " << (void *) buffer << " heap: " << pc2 <<endl; cout << "Memory contents:\n"; cout << pc1 << ": "; pc1->Show(); cout << pc2 << ": "; pc2->Show(); JustTesting *pc3, *pc4; pc3 = new (buffer) JustTesting("Bad Idea", 6); pc4 = new JustTesting("Heap2", 10); cout << "Memory contents:\n"; cout << pc3 << ": "; pc3->Show(); cout << pc4 << ": "; pc4->Show(); delete pc2; // free Heap1 delete pc4; // free Heap2 delete [] buffer; // free buffer cout << "Done\n"; return 0; }
out:
Just Testing constructed
Heap1 constructed
Memory block addresses:
buffer: 0x1ff73f715b0 heap: 0x1ff73f71260
Memory contents:
0x1ff73f715b0: Just Testing, 0
0x1ff73f71260: Heap1, 20
Bad Idea constructed
Heap2 constructed
Memory contents:
0x1ff73f715b0: Bad Idea, 6
0x1ff73f717c0: Heap2, 10
Heap1 destroyed
Heap2 destroyed
Done
在使用定位new运算符时存在两个问题:
解决方法:
程序员必须负责管用定位new运算符用从中使用的缓冲区内存单元。
要使用不同的内存单元,程序员需要提供两个位于缓冲区的不同地址,并确保这两个内存单元不重叠。
// 其中指针pc3相对于pc1的偏移量为JustTesting对象的大小。
pc1 = new (buffer) JustTesting;
pc3 = new (buffer + sizeof (JustTesting)) JustTesting("Better Idea", 6);
如果使用定位new运算符来为对象分配内存,必须确保其析构函数被调用。即显式地为使用定位new运算符创建的对象调用析构函数。
指针pc1指向的地址与 buffer相同,但buffer是使用new []初始化的,因此必须使用delete [ ]而不 是delete来释放。即使buffer是使用new而不是new []初始化的,delete pc1 也将释放buffer,而不是pc1。这是因为new/delete系统知道已分配的512 字节块buffer,但对定位new运算符对该内存块做了何种处理一无所知。
显式地调用析构函数时,必须指定要销毁的对象。由于有指向对象的指针,因此可以使用这些指针:
pc3->~JustTesting(); // destroy object pointed to by pc3
pc1->~JustTesting(); // destroy object pointed to by pc1
以下为改进的程序:
对定位new运算符使用的内存单元进行管理,加入到合适的delete和显式析构函数调用。
对于使用定位new运算符创建的对象,应以与创建顺序相反的顺序进行删除。原因在于,晚创建的对象可能依赖于早创建的对象。另外,仅当所有对象都被销毁后,才能释放用于存储这些对象的缓冲区。
// placenew2.cpp -- new, placement new, no delete // 该程序使用定位new运算符在相邻的内存单元中创建两个对象,并调用了合适的析构函数。 #include <iostream> #include <string> #include <new> using namespace std; const int BUF = 512; class JustTesting { private: string words; int number; public: JustTesting(const string & s = "Just Testing", int n = 0) {words = s; number = n; cout << words << " constructed\n"; } ~JustTesting() { cout << words << " destroyed\n";} void Show() const { cout << words << ", " << number << endl;} }; int main() { char * buffer = new char[BUF]; // get a block of memory JustTesting *pc1, *pc2; pc1 = new (buffer) JustTesting; // place object in buffer pc2 = new JustTesting("Heap1", 20); // place object on heap cout << "Memory block addresses:\n" << "buffer: " << (void *) buffer << " heap: " << pc2 <<endl; cout << "Memory contents:\n"; cout << pc1 << ": "; pc1->Show(); cout << pc2 << ": "; pc2->Show(); JustTesting *pc3, *pc4; // fix placement new location pc3 = new (buffer + sizeof (JustTesting)) JustTesting("Better Idea", 6); pc4 = new JustTesting("Heap2", 10); cout << "Memory contents:\n"; cout << pc3 << ": "; pc3->Show(); cout << pc4 << ": "; pc4->Show(); delete pc2; // free Heap1 delete pc4; // free Heap2 // explicitly destroy placement new objects pc3->~JustTesting(); // destroy object pointed to by pc3 pc1->~JustTesting(); // destroy object pointed to by pc1 delete [] buffer; // free buffer cout << "Done\n"; return 0; }
out:
Just Testing constructed Heap1 constructed Memory block addresses: buffer: 0x231c1ea15b0 heap: 0x231c1ea1260 Memory contents: 0x231c1ea15b0: Just Testing, 0 0x231c1ea1260: Heap1, 20 Better Idea constructed Heap2 constructed Memory contents: 0x231c1ea15d8: Better Idea, 6 0x231c1ea17c0: Heap2, 10 Heap1 destroyed Heap2 destroyed Better Idea destroyed Just Testing destroyed Done
要重新定义 << 运算符,以便将它和cout一起用来显示对象的内容,请定义下面的友元运算符函数:
ostream & operator<<(ostream & os, const c_name & obj)
{
os << ... ; // display object contents
return os;
}
其中c_name是类名。如果该类提供了能够返回所需内容的公有方法,则可在运算符函数中使用这些方法,这样便不用将它们设置为友元函数了。
c_name(type_name value);
operator type_name();
className(const className &)
c_name & c_name::operator=(const c_name & cn)
{
if (this == & cn)
return *this; // done if self-assignment
delete [] c_pointer;
// set size number of type_name units to be copied
c_pointer = new type_name[size];
// then copy data pointed to by cn.c_pointer to
// location pointed to by c_pointer
...
return *this;
}
队列是一种抽象的数据类型(Abstract Data Type,ADT),可以存储有序的项目序列。
新项目被添加在队尾,并可以删除队首的项目。先进先出(FIFO)。
例子:Heather银行希望对顾客排队等待的时间进行估测。
通常,三分之一的顾客只需要一分钟便可获得服务,三分之一的顾客需要两分钟,另外三分之一的顾客需要三分钟。另外,顾客到达的时间是随机的,但每个小时使用自动柜员机的顾客数量相当稳定。工程的另外两项任务是:设计一个表示顾客的类;编写一个程序来模拟顾客和队列之间的交互。
定义一个Queue类的特征:
class Queue
{
enum {Q_SIZE = 10};
private:
// private representation to be developed later
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 &item); // 入队
bool dequeue(Item &item); // 出队
};
链表由节点序列构成。每一个节点中都包含要保存到链表中的信息以及一个指向下一个节点的指针。
struct Node
{
Item item; // 当前节点的数据信息
struct Node * next; // 指向下一个节点的位置
};
单向链表,每个节点都只包含一个指向其他节点的指针。
知道第一个节点的地址后,就可以沿指针找到后面的每一个节点。
通常,链表最后一个节点中的指针被设置为NULL(或0),以指出后面没有节点了。
让Queue类的一个数据成员指向链表的起始位置。
还可以使用数据成员来跟踪队列可存储的最大项目数以及当前的项目数。
typedef Customer Item; // Customer 是一个类 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 ... public: //... };
嵌套结构和类
在类声明中声明的结构、类或枚举被称为是被嵌套在类中,其作用域为整个类。这种声明不会创建数据对象,而只是指定了可以在类中使用的类型。如果声明是在类的私有部分进行的,则只能在这个类使用被声明的类型;如果声明是在公有部分进行的,则可以从类的外部通过作用域解析运算符使用被声明的类型。例如,如果Node是在Queue类的公有部分声明的,则可以在类的外面声明Queue::Node类型的变量。
构造函数—成员初始化列表:
// 队列最初是空的,因此队首和队尾指针都设置为NULL(0或nullptr),
// 并将items设置为0。另外,还应将队列的最大长度qsize设置为构造函数参数qs的值。
Queue::Queue(int qs) : qsize(qs) // initialize qsize to qs
{
front = rear = NULL;
items = 0;
}
// 初值可以是常量或构造函数的参数列表中的参数。
Queue::Queue(int qs) : qsize(qs), front(NULL), rear(NULL), items(0)
{}
为什么需要成员初始化列表?
一般,调用构造函数时,对象将在括号中的代码执行之前被创建。因此,调用Queue(int qs)
构造函数将导致程序首先给成员变量分配内存(就相当于初始化)。然后,程序流程进入到括号中,使用常规的赋值方式将值存储到内存中。这时候就不能再对const常量赋值。
将项目添加到队尾(入队):
bool Queue::enqueue(const Item & item) { if (isfull()) return false; Node * add = new Node; // create node // on failure, new throws std::bad_alloc exception add->item = item; // set node pointers add->next = NULL; // or nullptr; 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; }
删除队首项目(出队):
bool Queue::dequeue(Item & item)
{
if (front == NULL)
return false;
item = front->item; // set item to first item in queue
items--;
Node * temp = front; // save location of first item
front = front->next; // reset front to next item
delete temp; // delete former first item
if (items == 0)
rear = NULL;
return true;
}
显示析构函数:
向队列中添加对象将调用new来创建新的节点。通过删除节点的方式,dequeue( )方法确实可以清除节点,但这并不能保证队列在到期时为空(队列不为空的时候解散队列)。因此,类需要一个显式析构函数——该函数删除剩余的所有节点。
Queue::~Queue()
{
Node * temp;
while (front != NULL) // while queue is not yet empty
{
temp = front; // save address of front item
front = front->next;// reset pointer to next item
delete temp; // delete former front
}
}
最后,要克隆或复制队列,必须提供复制构造函数和执行深度复制的赋值构造函数。
客户何时进入队列以及客户交易所需的时间。
当模拟生成新客户时,程序将创建一个新的客户对象,并在其中存储客户的到达时间以及一个随机生成的交易时间。当客户到达队首时,程序将记录此时的时间,并将其与进入队列的时间相减,得到客户的等候时间。
class Customer { private: long arrive; // arrival time for customer int processtime; // processing time for customer public: Customer() { arrive = processtime = 0; } // 默认构造函数创建一个空客户。 void set(long when); long when() const { return arrive; } int ptime() const { return processtime; } }; // set()成员函数将到达时间设置为参数,并将处理时间设置为1~3中的一个随机值。 void Customer::set(long when) { processtime = std::rand() % 3 + 1; arrive = when; }
程序允许用户输入3个数:队列的最大长度、程序模拟的持续时间(单位为小时)以及平均每小时的客户数。程序将使用循环——每次循环代表一分钟。在每分钟的循环中,程序将完成下面的工作:
queue.h
#ifndef PRIMERPLUS_QUEUE_H #define PRIMERPLUS_QUEUE_H // This queue will contain Customer items class Customer { private: long arrive; // arrival time for customer int processtime; // processing time for customer public: Customer() { arrive = processtime = 0; } // 内联函数 void set(long when); long when() const { return arrive; } int ptime() const { return processtime; } }; typedef Customer Item; 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 // preemptive definitions to prevent public copying Queue(const Queue & q) : qsize(0) { } Queue & operator=(const Queue & q) { return *this;} 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 &item); // 入队 bool dequeue(Item &item); // 出队 }; #endif //PRIMERPLUS_QUEUE_H
queue.cpp
// queue.cpp -- Queue and Customer methods #include "queue.h" #include <cstdlib> // (or stdlib.h) for rand() // Queue methods Queue::Queue(int qs) : qsize(qs) { front = rear = NULL; // or nullptr items = 0; } // 析构函数:把原来开辟的内存空间都释放掉,队列不为空的时候解散队列 Queue::~Queue() { Node * temp; while (front != NULL) // while queue is not yet empty { temp = front; // save address of front item front = front->next;// reset pointer to next item delete temp; // delete former front } } bool Queue::isempty() const { return items == 0; } bool Queue::isfull() const { return items == qsize; } int Queue::queuecount() const { return items; } // Add item to queue bool Queue::enqueue(const Item & item) { if (isfull()) return false; Node * add = new Node; // create node,存放新的队列节点 // on failure, new throws std::bad_alloc exception add->item = item; // set node pointers add->next = NULL; // or nullptr;队列的最后是空的 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; } // Place front item into item variable and remove from queue bool Queue::dequeue(Item & item) { if (front == NULL) return false; item = front->item; // set item to first item in queue items--; // 节点个数-- Node * temp = front; // save location of first item front = front->next; // reset front to next item delete temp; // delete former first item if (items == 0) rear = NULL; return true; } // customer method // when is the time at which the customer arrives // the arrival time is set to when and the processing // time set to a random value in the range 1 - 3 void Customer::set(long when) { processtime = std::rand() % 3 + 1; // 记录操作了多长时间 arrive = when; // 记录何时开始操作 }
usequeue.cpp
// 入队列,队列入满就出队列 #include "queue.h" #include <iostream> using namespace std; int main(void) { Item temp; int qs; int i = 0; int customers = 0; cout << "Enter max size of queue:"; cin >> qs; Queue line(qs); while(!line.isfull()) { temp.set(i++); // 填当前进入队列的时间long类型的整数 line.enqueue(temp); customers++; } cout << "enqueue, customers :" << customers << endl; while(!line.isempty()) { line.dequeue(temp); customers--; } cout << "dequeue, now customers :" << customers << endl; return 0; }
out:
Enter max size of queue:12
enqueue, customers :12
dequeue, now customers :0
bank.cpp
// bank.cpp -- using the Queue interface // compile with queue.cpp #include <iostream> #include <cstdlib> // for rand() and srand() #include <ctime> // for time() #include "queue.h" const int MIN_PER_HR = 60; bool newcustomer(double x); // is there a new customer? int main() { using std::cin; using std::cout; using std::endl; using std::ios_base; // setting things up std::srand(std::time(0)); // random initializing of rand() cout << "Case Study: Bank of Heather Automatic Teller\n"; cout << "Enter maximum size of queue:"; int qs; cin >> qs; Queue line(qs); // line queue holds up to qs people cout << "Enter the number of simulation hours:"; int hours; // hours of simulation cin >> hours; // simulation will run 1 cycle per minute long cyclelimit = MIN_PER_HR * hours; // # of cycles cout << "Enter the average number of customers per hour:"; double perhour; // average # of arrival per hour cin >> perhour; double min_per_cust; // average time between arrivals min_per_cust = MIN_PER_HR / perhour; Item temp; // new customer data long turnaways = 0; // turned away by full queue long customers = 0; // joined the queue long served = 0; // served during the simulation long sum_line = 0; // cumulative line length int wait_time = 0; // time until autoteller is free long line_wait = 0; // cumulative time in line // running the simulation for (int cycle = 0; cycle < cyclelimit; cycle++) { if (newcustomer(min_per_cust)) // have newcomer { if (line.isfull()) turnaways++; else { customers++; temp.set(cycle); // cycle = time of arrival line.enqueue(temp); // add newcomer to line } } if (wait_time <= 0 && !line.isempty()) { line.dequeue (temp); // attend next customer wait_time = temp.ptime(); // for wait_time minutes line_wait += cycle - temp.when(); served++; } if (wait_time > 0) wait_time--; sum_line += line.queuecount(); } // reporting results if (customers > 0) { cout << "customers accepted: " << customers << endl; cout << "customers served: " << served << endl; cout << "turnaways: " << turnaways << endl; cout << "average queue size: "; cout.precision(2); cout.setf(ios_base::fixed, ios_base::floatfield); cout << (double) sum_line / cyclelimit << endl; cout << "average wait time: " << (double) line_wait / served << " minutes\n"; } else cout << "No customers!\n"; cout << "Done!\n"; return 0; } // x = average time, in minutes, between customers // return value is true if customer shows up this minute // 值RAND_MAX是在cstdlib文件(以前是 stdlib.h)中定义的, // 值RAND_MAX是是rand( )函数可能返回的最大值(0是最小值)。 bool newcustomer(double x) { return (std::rand() * x / RAND_MAX < 1); }
out:
Case Study: Bank of Heather Automatic Teller
Enter maximum size of queue:10
Enter the number of simulation hours:4
Enter the average number of customers per hour:30
customers accepted: 113
customers served: 108
turnaways: 0
average queue size: 2.27
average wait time: 4.74 minutes
Done!
queue(int qs) : qsize(qs), items(0), front(NULL), rear(NULL) { }
如果数据成员是非静态const成员或引用,则必须采用这种格式,但可将C++11新增的类内初始化用于非静态const成员。class Queue
{
private:
...
Node * front = NULL;
enum {Q_SIZE = 10};
Node * rear = NULL;
int items = 0;
const int qsize = Q_SIZE;
...
};
1、
#include <cstring> using namespace std; class String { private: char * str; int len; }; // 构造函数 // 需要给str指针指向一个内存空间,不能不管它 String::String(const char *s) { len = strlen(s); str = new char[len+1]; strcpy(str, s); }
2、如果您定义了一个类,其指针成员是使用new初始化的,请指出可能出现的3个问题以及如何纠正这些问题。
4、
#include <iostream> #include <cstring> using namespace std; class Nifty { private: // 可以省略 char *personality; int talents; public: // 不可以省略 Nifty(); Nifty(const char * s); // 加const不希望被修改 ~Nifty(){delete [] personality;} // 释放构造函数中开辟的内存空间 friend ostream & operator<<(ostream & os, const Nifty & n); }; Nifty::Nifty() { personality = NULL; talents = 0; } Nifty::Nifty(const char * s) { int len; len = strlen(s); personality = new char[len+1]; strcpy(personality, s); talents = 0; } ostream & operator<<(ostream & os, const Nifty & n) { os << "personality : " << n.personality << endl; os << "talents = " << n.talents << endl; return os; }
5、下列各条语句将调用哪些类方法?
class Golfer { private: char * fullname; // points to string containing golfer's name int games; // holds number of golf games played int * scores; // points to first element of array of golf scores public: Golfer(); Golfer(const char * name, int g= 0); // creates empty dynamic array of g elements if g > 0 Golfer(const Golfer & g); ~Golfer(); }; // 下列各条语句将调用哪些类方法? Golfer nancy; // #1 Golfer lulu(“Little Lulu”); // #2 Golfer roy(“Roy Hobbs”, 12); // #3 Golfer * par = new Golfer; // #4,new开辟类这么大的内存空间,调用默认构造函数。 Golfer next = lulu; // #5,一个类的对象初始化另一个类对象,复制构造函数。 Golfer hazzard = “Weed Thwacker”; // #6,用字符串初始化类的对象,将某一种类型转化为类的类型时将使用带字符串参数的构造函数。 *par = nancy; // #7,两边对象都存在不会调用构造函数,调用默认赋值运算符。 nancy = “Nancy Putter”; // #8,右边调用带字符串的构造函数,然后调用默认的赋值运算符。
12p1.h
#ifndef PRIMERPLUS_12P1_H #define PRIMERPLUS_12P1_H class Cow { char name[20]; char * hobby; double weight; public: Cow(); Cow(const char * nm, const char * ho, double we); Cow(const Cow & c); ~Cow(); Cow & operator=(const Cow & c); void ShowCow() const; }; #endif //PRIMERPLUS_P1_H
12p1.cpp
#include "12p1.h" #include <cstring> #include <iostream> using namespace std; Cow::Cow() { name[0] = '\0'; hobby = nullptr; // NULL weight = 0.0; } Cow::Cow(const char * nm, const char * ho, double wt) { strncpy(name, nm, 20); // strncpy()和strnpy()略有不同 if (strlen(nm) >= 20) name[19] = '\0'; hobby = new char[strlen(ho) + 1]; // 深度拷贝,指针指向开辟的空间 strcpy(hobby, ho); weight = wt; } Cow::Cow(const Cow & c) { strcpy(name, c.name); hobby = new char[strlen(c.hobby) + 1]; // 深度拷贝,指针指向开辟的空间 strcpy(hobby, c.hobby); weight = c.weight; } Cow::~Cow() { delete [] hobby; } Cow & Cow::operator=(const Cow & c) { if (this == &c) return *this; delete [] hobby; // 释放成员指针以前指向的内存 strcpy(name, c.name); hobby = new char[strlen(c.hobby) + 1]; // 深度拷贝,指针指向开辟的空间 strcpy(hobby, c.hobby); weight = c.weight; return *this; } void Cow::ShowCow() const { cout << "Name : " << name << endl; cout << "Hobby : " << hobby << endl; cout << "Weight: " << weight << endl; }
use12p1.cpp
#include "12p1.h"
int main(void)
{
Cow cow1;
Cow cow2("cow2", "cccc", 123.4);
Cow cow3(cow2);
cow1 = cow2;
cow1.ShowCow();
cow2.ShowCow();
cow3.ShowCow();
return 0;
}
string2.h
#ifndef PRIMERPLUS_STRING2_H #define PRIMERPLUS_STRING2_H #include <iostream> using std::ostream; using std::istream; class String { private: char * str; // pointer to string int len; // length of string static int num_strings; // number of objects static const int CINLIM = 80; // cin input limit public: // constructors and other methods String(const char * s); // constructor String(); // default constructor String(const String &); // copy constructor ~String(); // destructor int length () const { return len; } // overloaded operator methods String & operator=(const String &); String & operator=(const char *); char & operator[](int i); const char & operator[](int i) const; // overloaded operator friends friend bool operator<(const String &st, const String &st2); friend bool operator>(const String &st1, const String &st2); friend bool operator==(const String &st, const String &st2); friend ostream & operator<<(ostream & os, const String & st); friend istream & operator>>(istream & is, String & st); // static function static int HowMany(); friend String operator+(const char * s, const String & st); String operator+(const String & st); void stringlow(); void stringup(); int has(char ch) const; }; #endif //PRIMERPLUS_STRING1_H
string2.cpp
// string1.cpp -- String class methods #include <cstring> // string.h for some #include "string2.h" // includes <iostream> #include <cctype> using namespace std; // initializing static class member int String::num_strings = 0; // static method int String::HowMany() { return num_strings; } // class methods String::String(const char * s) // construct String from C string { len = std::strlen(s); // set size str = new char[len + 1]; // allot storage std::strcpy(str, s); // initialize pointer num_strings++; // set object count } String::String() // default constructor { len = 4; str = new char[1]; str[0] = '\0'; // default string num_strings++; } String::String(const String & st) { num_strings++; // handle static member update len = st.len; // same length str = new char [len + 1]; // allot space std::strcpy(str, st.str); // copy string to new location } String::~String() // necessary destructor { --num_strings; // required delete [] str; // required } // overloaded operator methods // assign a String to a String String & String::operator=(const String & st) { if (this == &st) return *this; delete [] str; len = st.len; str = new char[len + 1]; std::strcpy(str, st.str); return *this; } // assign a C string to a String String & String::operator=(const char * s) { delete [] str; len = std::strlen(s); str = new char[len + 1]; std::strcpy(str, s); return *this; } // read-write char access for non-const String char & String::operator[](int i) { return str[i]; } // read-only char access for const String const char & String::operator[](int i) const { return str[i]; } // overloaded operator friends bool operator<(const String &st1, const String &st2) { return (std::strcmp(st1.str, st2.str) < 0); } bool operator>(const String &st1, const String &st2) { return st2 < st1; } bool operator==(const String &st1, const String &st2) { return (std::strcmp(st1.str, st2.str) == 0); } // simple String output ostream & operator<<(ostream & os, const String & st) { os << st.str; return os; } // quick and dirty String input // 重载>>运算符提供了一种将键盘输入行读入到String对象中的简单方法。 // 它假定输入的字符数不多于String::CINLIM的字符数,并丢弃多余的字符。 // 在if条件下,如果由于某种原因 // (如到达文件尾或get(char *, int)读取的是一个空行)导致输入失败,istream对象的值将置为 false。 istream & operator>>(istream & is, String & st) { char temp[String::CINLIM]; is.get(temp, String::CINLIM); if (is) st = temp; while (is && is.get() != '\n') continue; return is; } String operator+(const char * s, const String & st) { String temp; temp.len = strlen(s) + st.len; temp.str = new char[temp.len+1]; strcpy(temp.str, s); strcat(temp.str, st.str); return temp; } String String::operator+(const String & st) { String temp; temp.len = len + st.len; temp.str = new char[temp.len+1]; strcpy(temp.str, str); strcat(temp.str, st.str); return temp; } void String::stringlow() { for (int i=0; i < len; i++) str[i] = tolower(str[i]); } void String::stringup() { for (int i=0; i < len; i++) str[i] = toupper(str[i]); } int String::has(char ch) const { int count = 0; for (int i=0; i < len; i++) { if (str[i] == ch) count++; } return count; }
usestring2.cpp
#include <iostream> using namespace std; #include "string2.h" int main() { String s1(" and I am a C++ student."); String s2 = "Please enter your name: "; String s3; cout << s2; // overloaded << operator cin >> s3; // overloaded >> operator s2 = "My name is " + s3; // overloaded =, + operators cout << s2 << ".\n"; s2 = s2 + s1; s2.stringup(); // converts string to uppercase cout << "The string\n" << s2 << "\ncontains " << s2.has('A') << " 'A' characters in it.\n"; s1 = "red"; // String(const char *), // then String & operator=(const String&) String rgb[3] = { String(s1), String("green"), String("blue")}; cout << "Enter the name of a primary color for mixing light: "; String ans; bool success = false; while (cin >> ans) { ans.stringlow(); // converts string to lowercase for (int i = 0; i < 3; i++) { if (ans == rgb[i]) // overloaded == operator { cout << "That's right!\n"; success = true; break; } } if (success) break; else cout << "Try again!\n"; } cout << "Bye\n"; return 0; }
out:
Please enter your name: Fretta Farbo
My name is Fretta Farbo.
The string
MY NAME IS FRETTA FARBO AND I AM A C++ STUDENT.
contains 6 'A' characters in it.
Enter the name of a primary color for mixing light: yellow
Try again!
BLUE
That's right!
Bye
12p4.h
#ifndef PRIMERPLUS_12P4_H #define PRIMERPLUS_12P4_H typedef unsigned long Item; // 起别名,为存放不同的数据类型 class Stack { private: // 私有部分放成员变量 enum {MAX = 10}; // 枚举类型的符号常量 Item * pitems; int size; int top; // 顶部堆栈项的索引,栈顶指针 public: Stack(int n = MAX); // 默认构造函数 Stack(const Stack & st); ~Stack(); Stack & operator=(const Stack & st); bool isempty() const; // 判断是否为空 bool isfull() const; // 判断是否满了 // push() returns false if stack already is full, true otherwise bool push(const Item & item); // 入栈 // pop() returns false if stack already is empty, true otherwise bool pop(Item & item); // 出栈 }; #endif //PRIMERPLUS_STACK_H
12p4.cpp
// stack.cpp -- Stack member functions #include "12p4.h" Stack::Stack(int n) // create an empty stack { pitems = new Item[n]; size = n; top = 0; // 初始化栈顶指针 } Stack::Stack(const Stack & st) { pitems = new Item[st.size]; for (int i=0; i<st.size; i++) pitems[i] = st.pitems[i]; size = st.size; top = st.top; } Stack::~Stack() { delete [] pitems; } bool Stack::isempty() const { return top == 0; // 是否等于最底层 } bool Stack::isfull() const { return top == MAX; // 是否等于最高层 } bool Stack::push(const Item & item) { if (top < MAX) // 入栈条件 { pitems[top++] = item; return true; } else return false; } bool Stack::pop(Item & item) { if (top > 0) { item = pitems[--top]; return true; } else return false; } Stack & Stack::operator=(const Stack & st) { if (this == &st) return *this; delete [] pitems; pitems = new Item[st.size]; for (int i=0; i<st.size; i++) pitems[i] = st.pitems[i]; size = st.size; top = st.top; return * this; }
use12p4.cpp
// stacker.cpp -- testing the Stack class #include <iostream> #include <cctype> // or ctype.h #include "12p4.h" const int MAX = 5; int main() { using namespace std; Stack st(MAX); // create an empty stack Item item; for (int i=0; i<MAX; i++) { cout << "Enter a number you want to push to stack:"; cin >> item; while(cin.get() != '\n'); st.push(item); } Stack st_new(st); // 复制构造函数 for (int i=0; i < MAX; i++) { st_new.pop(item); cout << item << " is poped." << endl; } return 0; }
out:
Enter a number you want to push to stack:1
Enter a number you want to push to stack:2
Enter a number you want to push to stack:3
Enter a number you want to push to stack:4
Enter a number you want to push to stack:5
5 is poped.
4 is poped.
3 is poped.
2 is poped.
1 is poped.
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。