c++---对象和类

目录

类的构造函数和析构函数

声明和定义构造函数

使用构造函数

默认构造函数

析构函数

改进Stock类

this指针

对象数组

抽象数据类型-栈


类声明类似结构声明,可以包括数据成员和成员函数。声明有私有部分,在其中声明的成员只能通过成员函数进行访问;声明还具有共有部分,在其中声明的成员可被使用类对象的程序直接访问。通常,数据成员被放在私有部分中,成员函数被放在共有部分中,因此典型的类声明的格式如下:

calss className
{
private:
    data member declarations
public:
    member function prototypes
};
//stock00.h --Stock class interface
//version 00
#ifndef STOCK00_H_
#define STPCK00_H_
#include <string>
using namespace std;
class Stock //class declaration
{
private:
    string company;
    long shares;
    double share_val;
    double total_val;
    void set_tot() {total_val = shares * share_val;}
public:
    void acquire(const string &co, long num, double price);
    void buy(long num, double price);
    void sell(long num, double price);
    void update(double price);
    void show();
};//note semicolon at the end
#endif // STOCK00_H_
//stock00.cpp--implementing the Stock class
//version 00
#include <iostream>
#include "stock00.h"

void Stock::acquire(const string &co, long num, double price)
{
    company = co;
    if (num < 0)
    {
        cout << "Number of shares can't be negative; "
             << company << "shares set to 0.\n";
        shares = 0;
    }
    else
        shares = num;

    share_val = price;
    set_tot();
}

void Stock::buy(long num, double price)
{
    if (num < 0)
    {
        cout << "Number of shares purchased can't be negative. "
             << "Transaction is aborted.\n";
    }
    else
    {
        shares += num;
        share_val = price;
        set_tot();
    }
}

void Stock::sell(long num, double price)
{
    if (num < 0)
    {
        cout << "Number of shares sold can't be negative. "
             << "Transaction is aborted.\n";
    }
    else if (num > shares)
    {
        cout << "You can't sell more that you have! "
             << "Transaction is aborted.\n";
    }
    else
    {
        shares -= num;
        share_val = price;
        set_tot();
    }
}

void Stock::update(double price)
{
    share_val = price;
    set_tot();
}

void Stock::show()
{
    cout << "Company: " << company
         << " Shares: " << shares << '\n'
         << " Share Price: $" << share_val
         << " Total Worth: $" << total_val << '\n';
}
/* use stock00.cpp -- the client program*/
#include <iostream>
#include "stock00.h"
int main()
{
    Stock fluffy_the_cat;

    fluffy_the_cat.acquire("NanoSmart", 20, 12.50);
    fluffy_the_cat.show();

    fluffy_the_cat.buy(15, 18.125);
    fluffy_the_cat.show();

    fluffy_the_cat.sell(400, 20.00);
    fluffy_the_cat.show();

    fluffy_the_cat.buy(300000, 40.125);
    fluffy_the_cat.show();

    fluffy_the_cat.sell(300000, 0.125);
    fluffy_the_cat.show();
    return 0;
}

Company: NanoSmart Shares: 20
 Share Price: $12.5 Total Worth: $250
Company: NanoSmart Shares: 35
 Share Price: $18.125 Total Worth: $634.375
You can't sell more that you have! Transaction is aborted.
Company: NanoSmart Shares: 35
 Share Price: $18.125 Total Worth: $634.375
Company: NanoSmart Shares: 300035
 Share Price: $40.125 Total Worth: $1.20389e+007
Company: NanoSmart Shares: 35
 Share Price: $0.125 Total Worth: $4.375

类的构造函数和析构函数

构造函数是一种特殊的类成员函数,在创建类对象时被调用。构造函数的名称和类名相同,但通过函数重载,可以创建多个同名的构造函数,条件是每个函数的特征标(参数列表)都不同。另外,构造函数没有声明类型。通常,构造函数用于初始化类对象的成员,初始化应与构造函数的参数列表匹配。

声明和定义构造函数

//constructor definition
Stock(const string &co, long num = 0, double price = 0.0);

下面是构造函数的一种可能定义:

//constructor definition
void Stock::Stock(const string &co, long num, double price)
{
    company = co;
    if (num < 0)
    {
        cout << "Number of shares can't be negative; "
             << company << "shares set to 0.\n";
        shares = 0;
    }
    else
        shares = num;

    share_val = price;
    set_tot();
}

上述代码和acquire函数相同。区别在于,程序声明对象时,将自动调用构造函数。

使用构造函数

C++提供了两种使用构造函数来初始化对象的方式。

1.显示地调用构造函数

Stock food = Stock("World Cabbage", 250, 1.25);

2.隐式地调用构造函数

Stock garment("Furry Mason", 50, 2.5);

这种方式更紧凑,它与下面的显示调用等价:

Stock garment = Stock("Furry Mason", 50, 2,5);

 每次创建类对象(甚至使用new动态分配内存)时,c++都使用类构造函数。

Stock *pstock = new Stock("Electroshock Games", 18, 19.0);

这条语句创建一个Stock对象,将其初始化为参数提供的值,并将该对象的地址赋给pstock指针。在这种情况下,对象没有名称,但可以使用指针来管理该对象。

构造函数的使用方式不同于其他类方法。一般来说,使用对象来调用方法:

stock1.show(); //stock1 object invokes show() method

但无法使用对象来调用构造函数,因为在构造函数构造出对象之前,对象是不存在的。因此构造函数被用来创建对象,而不能通过对象来调用。

默认构造函数

默认构造函数是在未提供显式初始值时,用来创建对象的构造函数。也就是说,它是用于下面这种声明的构造函数:

Stock fluffy_the_cat; //uses the default constructor

当且仅当没有定义任何构造函数时,编译器才会提供默认构造函数。为类定义了构造函数后,程序员就必须为它提供默认构造函数。如果提供了非默认构造函数(如Stock(const string &co, long num, double price)),但没有提供默认构造函数,则下面的声明将出错:

Stock stock1; //not possible with current constructor

定义默认构造函数的方式有两种:

  • 给已有构造函数的所有参数提供默认值
Stock(const string &co = "Error", long num = 0, double price = 0.0);
  • 通过函数重载来定义另一个构造函数---一个没有参数的构造函数
Stock();

下面是为Stock类定义的一个默认构造函数:

Stock::Stock()
{
    company = "no name";
    shares = 0;
    share_val = 0.0;
    total_val = 0.0;
}

定义上述默认构造函数后,在main函数中创建一个Stock对象:

    Stock fluffy_the_cat;
    fluffy_the_cat.show();

运行结果: 

Company: no name Shares: 0
 Share Price: $0 Total Worth: $0
Stock first; //calls default constructor implicitly
Stock first = Stock(); //calls it explicitly
Stock *prelief = new Stock; //calls it implicitly

Stock first("Concrete Conglomerate"); //calls constructor
Stock second(); //declares a function
Stock third; //calls default constructor

隐式地调用默认构造函数时,不要使用圆括号。

析构函数

用构造函数创建对象后,程序负责跟踪该对象,直到其过期为止。对象过期时,程序将自动调用一个特殊的成员函数---析构函数。

析构函数完成清理工作。

例如,如果构造函数使用new来分配内存,则析构函数将使用delete来释放这些内存。

与构造函数不同的是,析构函数没有参数,因此Stock析构函数的原型必须是这样的:

~Stock();

为了看出析构函数何时被调用,这样编写代码:

Stock::~Stock()
{
    cout << "Bye, " << company << "!\n";
}

什么时候应调用析构函数呢?

这由编译器决定,通常不应在代码中显式地调用析构函数。

  • 如果创建的是静态存储类对象,则其析构函数将在程序结束时自动被调用。
  • 如果创建的是自动存储类对象,则其析构函数将在程序执行完代码块(该对象是在其中定义的)时自动被调用。
  • 如果对象是通过new创建的,则它将驻留在栈内存或自由存储区中,当使用delete来释放内存时,则析构函数将自动被调用。
  • 程序可以创建临时对象来完成特定的操作,在这种情况下,程序将在结束对该对象的使用时自动调用其析构函数。

由于在类对象过期时析构函数将自动被调用,因此必须有一个析构函数。如果程序员没有提供析构函数,编译器将隐式地声明一个默认析构函数,并在发现导致对象被删除的代码时,提供默认析构函数的定义。

改进Stock类

下面将构造函数和析构函数加入到类和方法的定义中。

1.头文件

它将构造函数和析构函数的原型加入到原来的类声明中。另外,它还删除了acquire函数--现在已经不需要它了,因为有构造函数:

//stock10.h --Stock class interface with constructors, destructor added
//version 10
#ifndef STOCK10_H_
#define STOCK10_H_
#include <string>
using namespace std;
class Stock //class declaration
{
private:
    string company;
    long shares;
    double share_val;
    double total_val;
    void set_tot() {total_val = shares * share_val;}
public:
    //two constructors
    Stock(); //default constructor
    Stock(const string &co, long num = 0, double price = 0.0);
    ~Stock();//destructor
    void buy(long num, double price);
    void sell(long num, double price);
    void update(double price);
    void show();
};//note semicolon at the end
#endif // STOCK10_H_

2.实现文件

//stock10.cpp--implementing the Stock class with constructors, destructor added
//version 10
#include <iostream>
#include "stock10.h"
//constructors
Stock::Stock() //default constructor
{
    cout << "Default constructor called\n";
    company = "no name";
    shares = 0;
    share_val = 0.0;
    total_val = 0.0;
}

Stock::Stock(const string &co, long num, double price)
{
    cout << "Constructor using " << co << " called\n";
    company = co;
    if (num < 0)
    {
        cout << "Number of shares can't be negative; "
             << company << "shares set to 0.\n";
        shares = 0;
    }
    else
        shares = num;

    share_val = price;
    set_tot();
}

//class destructor
Stock::~Stock()
{
    cout << "Bye, " << company << "!\n";
}

//other methods
void Stock::buy(long num, double price)
{
    if (num < 0)
    {
        cout << "Number of shares purchased can't be negative. "
             << "Transaction is aborted.\n";
    }
    else
    {
        shares += num;
        share_val = price;
        set_tot();
    }
}

void Stock::sell(long num, double price)
{
    if (num < 0)
    {
        cout << "Number of shares sold can't be negative. "
             << "Transaction is aborted.\n";
    }
    else if (num > shares)
    {
        cout << "You can't sell more that you have! "
             << "Transaction is aborted.\n";
    }
    else
    {
        shares -= num;
        share_val = price;
        set_tot();
    }
}

void Stock::update(double price)
{
    share_val = price;
    set_tot();
}

void Stock::show()
{
    //set format to #.###
    ios_base::fmtflags orig = cout.setf(ios_base::fixed, ios_base::floatfield);
    std::streamsize prec = cout.precision(3);

    cout << "Company: " << company
         << " Shares: " << shares << '\n';
    cout << " Share Price: $" << share_val;
    //set format to #.##
    cout.precision(2);
    cout << " Total Worth: $" << total_val << '\n';

    //restore original format
    cout.setf(orig, ios_base::floatfield);
    cout.precision(prec);

}

3.客户文件

/* use stock01.cpp -- the client program, using the Stock class*/
//compile with stock10.cpp
#include <iostream>
#include "stock10.h"
int main()
{
    {
        cout << "Using constructors to create new objects\n";
        Stock stock1("NanoSmart", 12, 20.0);
        stock1.show();
        Stock stock2 = Stock("Boffo Objects", 2, 2.0);
        stock2.show();

        cout << "Assigning stock1 to stock2:\n";
        stock2 = stock1;
        cout << "Listing stock1 and stock2:\n";
        stock1.show();
        stock2.show();

        cout << "Using a constructor to reset an object\n";
        stock1 = Stock("Nifty Foods", 10, 50.0); //temp object
        cout << "Revised stock1:\n";
        stock1.show();
        cout << "Done\n";
    }

    return 0;
}

运行结果:

Using constructors to create new objects
Constructor using NanoSmart called
Company: NanoSmart Shares: 12
 Share Price: $20.000 Total Worth: $240.00
Constructor using Boffo Objects called
Company: Boffo Objects Shares: 2
 Share Price: $2.000 Total Worth: $4.00
Assigning stock1 to stock2:
Listing stock1 and stock2:
Company: NanoSmart Shares: 12
 Share Price: $20.000 Total Worth: $240.00
Company: NanoSmart Shares: 12
 Share Price: $20.000 Total Worth: $240.00
Using a constructor to reset an object
Constructor using Nifty Foods called
Bye, Nifty Foods!
Revised stock1:
Company: Nifty Foods Shares: 10
 Share Price: $50.000 Total Worth: $500.00
Done
Bye, NanoSmart!
Bye, Nifty Foods!

程序说明:

(1).

Stock stock1("NanoSmart", 12, 20.0);

创建一个名为stock1的Stock对象,并将其数据成员初始化为指定的值:

Constructor using NanoSmart called
Company: NanoSmart Shares: 12
 Share Price: $20.000 Total Worth: $240.00

(2).下面的语句使用另一种语法创建并初始化一个名为stock2的对象:

Stock stock2 = Stock("Boffo Objects", 2, 2.0);

C++标准允许编译器使用两种方式来执行第二种语法。

  • 使其行为和第一种语法完全相同
Constructor using Boffo Objects called
Company: Boffo Objects Shares: 2
 Share Price: $2.000 Total Worth: $4.00
  • 允许调用构造函数来创建一个临时对象,然后将该临时对象复制到stock2中,并丢弃它。如果编译器使用的是这种方式,则将为临时对象调用析构函数,因此生成下面的输出:
Constructor using Boffo Objects called
Bye, Boffo Objects!
Company: Boffo Objects Shares: 2
 Share Price: $2.000 Total Worth: $4.00

生成上述输出的编译器可能立刻删除临时对象,但也可能会等一段时间,在这种情况下,析构函数的消息将会过一段时间才显示。

(3).下面的语句表明可以将一个对象赋给同类型的另一个对象:

stock2 = stock1;

与给结构赋值一样,在默认情况下,给类对象赋值时,将把一个对象的成员复制给另一个。在这个例子中,stock2原来的内容将被覆盖。

在默认情况下,将一个对象赋给同类型的另一个对象时,C++将源对象的每个数据成员的内容复制到目标对象中相应的数据成员中。

(4). 构造函数不仅仅可用于初始化新对象。例如:

stock1 = Stock("Nifty Foods", 10, 50.0); //temp object

stock1对象已经存在,因此这条语句不是对stock1进行初始化,而是将新值赋给它。这是通过让构造函数创建一个新的、临时的对象,然后将其内容复制给stock1来实现的。随后程序调用析构函数,以删除该临时对象,如下面经过注释后的输出所示:

Using a constructor to reset an object
Constructor using Nifty Foods called //temporary object created
Bye, Nifty Foods! //temporary object destroyed
Revised stock1:
Company: Nifty Foods Shares: 10 //data now copied to stock1
 Share Price: $50.000 Total Worth: $500.00

有些编译器可能要过一段时间才删除临时对象,因此析构函数的调用将延迟。

(5).最后,程序显示了下面的内容:

Done
Bye, NanoSmart!
Bye, Nifty Foods!

函数main结束后,其局部变量(stock1和stock2)将消失。由于这种自动变量被放在栈中,因此最后创建的对象将最先被删除,最先创建的对象将最后被删除。("NanoSmart"最初位于stock1中,但随后被传输到stock2中,然后stock1被重置为"Nifty Foods").

(6).输出表明,下面两条语句有根本性的差别:

Stock stock2 = Stock("Boffo Objects", 2, 2.0);
stock1 = Stock("Nifty Foods", 10, 50.0); //temp object

第一条语句是初始化,它创建有指定值的对象,可能会创建临时对象(也可能不会);

第二条语句是赋值,像这样在赋值语句中使用构造函数总会导致在赋值前创建一个临时对象。

如果既可以通过初始化,也可以通过赋值来设置对象的值,则应采用初始化方式。通常这种方式的效率更高。

4.const成员函数

请看下面的代码片段:

const Stock land = Stock("Kludgehorn Properties");
land.show();

对于当前的C++来说,编译器将拒绝第二行。这是什么原因呢?

因为show()的代码无法确保调用对象不被修改---调用对象和const一样,不应被修改。我们以前通过将函数参数声明为const引用或指向const的指针来解决这个问题。但这里存在语法问题:show()方法没有任何参数。相反,它所使用的对象是由方法调用隐式提供的。

需要一种新的语法---保证函数不会修改调用对象。C++的解决办法是将const关键字放在函数的括号后面。也就是说,show()声明应像这样:

void show() const; //promises not to change invoking object

同样,函数定义的开头应像这样:

void Stock::show() const //promises not to change  invoking object
{

...

}

以这种方式声明和定义的类函数被称为const成员函数

就像应尽可能将const引用和指针有座函数形参一样,只要类方法不修改调用对象,就应将其声明为const。

this指针

每个成员函数(包括构造函数和析构函数)都有一个this指针。this指针指向调用对象,如果方法需要引用整个调用对象,则可以使用表达式*this。在函数的括号后面使用const限定符将this限定为const,这样将不能使用this来修改对象的值。

然而,要返回的并不是this,因为this是对象的地址,而是对象本身,即*this。

对于Stock类,还有很多工作要做。到目前为止,每个类成员函数都只涉及一个对象,即调用它的对象。但有时候方法可能涉及到两个对象,在这种情况下需要使用C++的this指针。

例如,在Stock类中添加成员函数topval()用于返回股价较高的对象:

const Stock & Stock::topval(const Stock& s) const
{
    if (s.total_val > this->total_val)
        return s;//argument object
    else
        return *this; //invoking object
}

返回类型为引用意味着返回的是调用对象本身,而不是其副本。

对象数组

下面为修订后的类方法文件,包括新的topval()方法。

(1)头文件

//stock20.h --Stock class interface with constructors, destructor added
//version 20
#ifndef STOCK20_H_
#define STOCK20_H_
#include <string>
using namespace std;
class Stock //class declaration
{
private:
    string company;
    long shares;
    double share_val;
    double total_val;
    void set_tot() {total_val = shares * share_val;}
public:
    //two constructors
    Stock(); //default constructor
    Stock(const string &co, long num = 0, double price = 0.0);
    ~Stock();//destructor
    void buy(long num, double price);
    void sell(long num, double price);
    void update(double price);
    void show() const;
    const Stock & topval(const Stock& s) const;
};//note semicolon at the end
#endif // STOCK20_H_

(2).实现文件

//stock20.cpp--implementing the Stock class with constructors, destructor added
//version 20
#include <iostream>
#include "stock20.h"
//constructors
Stock::Stock() //default constructor
{
    cout << "Default constructor called\n";
    company = "no name";
    shares = 0;
    share_val = 0.0;
    total_val = 0.0;
}

Stock::Stock(const string &co, long num, double price)
{
    cout << "Constructor using " << co << " called\n";
    company = co;
    if (num < 0)
    {
        cout << "Number of shares can't be negative; "
             << company << "shares set to 0.\n";
        shares = 0;
    }
    else
        shares = num;

    share_val = price;
    set_tot();
}

//class destructor
Stock::~Stock()
{
    cout << "Bye, " << company << "!\n";
}

//other methods
void Stock::buy(long num, double price)
{
    if (num < 0)
    {
        cout << "Number of shares purchased can't be negative. "
             << "Transaction is aborted.\n";
    }
    else
    {
        shares += num;
        share_val = price;
        set_tot();
    }
}

void Stock::sell(long num, double price)
{
    if (num < 0)
    {
        cout << "Number of shares sold can't be negative. "
             << "Transaction is aborted.\n";
    }
    else if (num > shares)
    {
        cout << "You can't sell more that you have! "
             << "Transaction is aborted.\n";
    }
    else
    {
        shares -= num;
        share_val = price;
        set_tot();
    }
}

void Stock::update(double price)
{
    share_val = price;
    set_tot();
}

void Stock::show() const
{
    //set format to #.###
    ios_base::fmtflags orig = cout.setf(ios_base::fixed, ios_base::floatfield);
    std::streamsize prec = cout.precision(3);

    cout << "Company: " << company
         << " Shares: " << shares << '\n';
    cout << " Share Price: $" << share_val;
    //set format to #.##
    cout.precision(2);
    cout << " Total Worth: $" << total_val << '\n';

    //restore original format
    cout.setf(orig, ios_base::floatfield);
    cout.precision(prec);

}

const Stock & Stock::topval(const Stock& s) const
{
    if (s.total_val > this->total_val)
        return s; //argument object
    else
        return *this; //invoking object
}

(3).客户文件

/* use stock20.cpp -- the client program, using the Stock class*/
//compile with stock20.cpp
#include <iostream>
#include "stock20.h"
const int STKS = 4;
int main()
{
    {
        //create an array of initialized objects
        Stock stocks[STKS] = {
                                Stock("NanoSmart", 12, 20.0),
                                Stock("Boffo Objects", 2, 2.0),
                                Stock("Monolithic Obelisks", 130, 3.25),
                                Stock("Fleep Enterprises", 60, 6.5)
                             };

        cout << "Stock holdings:\n";
        int st;
        for (st = 0; st < STKS; st++)
            stocks[st].show();

        //set poinetr to first element
        const Stock *top = &stocks[0];
        for (st = 1; st < STKS; st++)
            top = &top->topval(stocks[st]);
        //now top points to the most valuable holding
        cout << "\nMost valuable holding:\n";
        top->show();
    }

    return 0;
}

Constructor using NanoSmart called
Constructor using Boffo Objects called
Constructor using Monolithic Obelisks called
Constructor using Fleep Enterprises called
Stock holdings:
Company: NanoSmart Shares: 12
 Share Price: $20.000 Total Worth: $240.00
Company: Boffo Objects Shares: 2
 Share Price: $2.000 Total Worth: $4.00
Company: Monolithic Obelisks Shares: 130
 Share Price: $3.250 Total Worth: $422.50
Company: Fleep Enterprises Shares: 60
 Share Price: $6.500 Total Worth: $390.00

Most valuable holding:
Company: Monolithic Obelisks Shares: 130
 Share Price: $3.250 Total Worth: $422.50
Bye, Fleep Enterprises!
Bye, Monolithic Obelisks!
Bye, Boffo Objects!
Bye, NanoSmart!

抽象数据类型-栈

使用类实现抽象数据类型-栈。

1.stack.h

//stack.h--class definition for the stack ADT
#ifndef STACK_H_
#define STACK_H_
typedef unsigned long Item;
class Stack
{
private:
    enum {MAX = 10};
    Item items[MAX]; //hold stack items
    int top; //index for top stack item
public:
    Stack();
    bool isempty() const;
    bool isfull() const;
    //push() returns false if stack already is full, true otherwise
    bool push(const Item &item);//add item to stack
    //pop() returns false if stack already is empty, true otherwise
    bool pop(Item & item); //pop top into item
};
#endif // STACK_H_

2.stack.cpp

//stack.cpp --Stack member functions
#include "stack.h"
Stack::Stack()
{
    top = 0;
}

bool Stack::isempty() const
{
    return top == 0;
}

bool Stack::isfull() const
{
    return top == MAX;
}

bool Stack::push(const Item &item)
{
    if (top < MAX)
    {
        items[top++] = item;
        return true;
    }
    else
        return false;
}

bool Stack::pop(Item &item)
{
    if (top > 0)
    {
        item = items[--top];
        return true;
    }
    else
        return false;
}

3.stacker.cpp

//stacker.cpp --testing the stack class
#include <iostream>
#include <cctype>
#include "stack.h"
using namespace std;

int main()
{
    Stack st; //create an empty stack
    char ch;
    unsigned long po;
    cout << "Please enter A to add a purchase order,\n"
         << "p to process a PO, or Q to quit.\n";
    while (cin >> ch && toupper(ch) != 'Q')
    {
        while (cin.get() != '\n')
            continue;
        if (!isalpha(ch))
        {
            cout << '\a';
            continue;
        }
        switch(ch)
        {
            case 'A':
            case 'a':
                cout << "Enter a PO number to add: ";
                cin >> po;
                if (st.isfull())
                    cout << "stack is already full\n";
                else
                    st.push(po);
                break;
            case 'P':
            case 'p':
                if (st.isempty())
                    cout << "stack is already empty\n";
                else
                {
                    st.pop(po);
                    cout << "PO #" << po << " popped\n";
                }
                break;

        }
        cout << "Please enter A to add a purchase order,\n"
             << "p to process a PO, or Q to quit.\n";
    }
    cout << "Bye\n";
    return 0;
}

运行结果:

Please enter A to add a purchase order,
p to process a PO, or Q to quit.
A
Enter a PO number to add: 17885
Please enter A to add a purchase order,
p to process a PO, or Q to quit.
p
PO #17885 popped
Please enter A to add a purchase order,
p to process a PO, or Q to quit.
A
Enter a PO number to add: 17965
Please enter A to add a purchase order,
p to process a PO, or Q to quit.
A
Enter a PO number to add: 18002
Please enter A to add a purchase order,
p to process a PO, or Q to quit.
p
PO #18002 popped
Please enter A to add a purchase order,
p to process a PO, or Q to quit.
p
PO #17965 popped
Please enter A to add a purchase order,
p to process a PO, or Q to quit.
p
stack is already empty
Please enter A to add a purchase order,
p to process a PO, or Q to quit.
A
Enter a PO number to add: 1
Please enter A to add a purchase order,
p to process a PO, or Q to quit.
p
PO #1 popped
Please enter A to add a purchase order,
p to process a PO, or Q to quit.
5
p
stack is already empty
Please enter A to add a purchase order,
p to process a PO, or Q to quit.
Q
Bye

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值