• C++第十章__类和对象定义的基础知识__类的构造函数和析构函数__const成员函数__书中的构造函数和析构函数的总结__this指针 & 类对象作为类方法的参数 & 返回值为类对象的引用 __对象数组__类作用域_作用域为类的常量(在类中定义常量)__类似栈的C++实现方法


    目录

    类和对象定义的基础知识 

    //通过一个例子来说明如何创建类和对象
    //类声明通常在h文件完成,通常将类的首字母大写

    //stock00.h 程序说明
    01)下述程序用关键字class定义了一个类Stock,这种语法指出Stock是一个新类的类型名。该声明让我们能够
      声明Stock类型的变量---成为对象或实例。例如:
      Stock sally; //声明一个类对象sally
      Stock solly; //声明一个类对象solly
    02)要存储的数据以类数据成员(如company和shares)的形式出现。
      要执行的操作以类函数成员(如set_tot())的形式出现。成员函数可以就地定义(如set_tot()),也可以用原型
      表示。
    03)将数据和方法(函数)组成一个单元是类最吸引人的地方。
    04)类和对象都可以直接访问共有部分(public部分),但是对于私有部分,只能通过共有成员函数(如update()和show())
      来访问。
    05)类设计尽可能将共有接口(public中定义的函数)和实现细节(private中声明的数据)分开
    06)OOP特性:
      A 首先将数据表示和函数原型放在一个类声明中(而不是一个文件中),通过类声明将所有的内容放在一个来声明中
          来使描述成为一个整体
      B 让数据表示成为私有,使得数据只能通过被授权函数来访问,被授权函数即public中声明的函数
    07)由于隐藏数据是OOP追求的目标之一,所以一般是将数据放在private中,将函数声明放在public中。但是也可以
      在private中声明和定义函数,该函数就只能在共有方法(public中声明的函数)中使用
    08)OOP和结构的区别:
      结构默认的访问方式是pulic,而类为private。c++程序员一般通过使用类来实现类描述,而把结构限制为只表示
      纯粹的数据对象

     1 #ifndef STOCK00_H_    //编译器只编译一次
     2 #define STOCK00_H_  
     3 
     4 #include <string>
     5 
     6 class Stock  //类声明,只能用class
     7 {
     8 private:     //private可以不用写,因为这是类对象默认的访问控制,但是也可以写上的
     9     std::string company;  //定义string类变量company
    10     long shares;
    11     double share_val;
    12     double total_val;
    13     void set_tot()  //定义函数set_tot(),在类声明中定义的函数,将自动成为内联函数
    14     {
    15         total_val = share_val * shares;
    16     }
    17 public:
    18     void acquire(const std::string & co, long n, double pr);  //声明函数
    19     void buy(long num, double price);  //声明函数
    20     void sell(long num, double price);  //声明函数
    21     void update(double price);  //声明函数
    22     void show();  //声明函数
    23 };  //不要忘记分号
    24 
    25 #endif
    stock.h

    stock00.cpp程序说明

    01)定义成员函数时,使用作用域解析运算符::来表示函数所属的类
     例如:
     void Stock::update(double price)
     void Buffoon::update()
     其中这两个update()是不一样的,且这样声明和定义不会报错
    02)类方法可以访问类的private中的组件(声明的变量和定义的函数)
      如在上面的public中声明的成员函数中都使用了Stock中private下声明的变量
    03)使数据私有并限于对公有函数访问的技术允许我们能够控制数据如何被使用

    /* 内联方法 */
    /*
    01)其定义位于类声明中的函数都将自动成为内联函数
    02)也可以在类声明之外定义成员函数,并使之成为内联函数,为此只需使用关键字inline即可
      class Stock
      {
      private:
        ...
        void set_tot(); //只是声明,没有定义,此时set_tot()还并未是内联函数
      public:
        ...
      };

      inline void Stock::ste_tot()
      {
        total = shares * share_val;
      }

     1 #include <iostream>
     2 #include "Stock00.h"
     3 
     4 //定义成员函数时,使用作用域解析运算符::来表示函数所属的类
     5 //类方法可以访问类的private中的组件(声明的变量和定义的函数)
     6 void Stock::acquire(const std::string & co, long n, double pr) //使用::来表示Stock类下的acquire()函数
     7 {
     8     company = co;  //为Stock类中private中的数据变量company赋值
     9     if (n < 0)
    10     {
    11         std::cout << "Number of shares can't be negative; " << company << " shares set to 0" << std::endl;
    12         shares = 0;  //为Stock类中private中的数据变量shares赋值
    13     }
    14     else
    15         shares = n;  //为Stock类中private中的数据变量shares赋值
    16     share_val = pr;  //为Stock类中private中的数据变量share_val赋值
    17     set_tot();  //调用在Stock类中private下定义的函数,且该函数只能是在公有成员函数中调用,在别的地方调用的话会报错
    18 }
    19 
    20 void Stock::buy(long num, double price)
    21 {
    22     if (num < 0)
    23     {
    24         std::cout << "Number of shares purchased can't be negative; "
    25             << "Transaction is aborted" << std::endl;
    26     }
    27     else
    28     {
    29         shares += num;  //为Stock类中private中的数据变量shares赋值
    30         share_val = price;  //为Stock类中private中的数据变量share_val赋值
    31         set_tot();  //调用在Stock类中private下定义的函数
    32     }
    33 }
    34 
    35 void Stock::sell(long num, double price)
    36 {
    37     using std::cout;
    38     using std::endl;
    39     if (num < 0)
    40     {
    41         cout << "Number of shares sold can't be negetive. "
    42             << "Transaction is aborted" << endl;
    43     }
    44     else if(num>shares)
    45     {
    46         cout << "You can't sell more than you have. "
    47             << "Transaction is aborted. " << endl;
    48     }
    49     else
    50     {
    51         shares -= num;  //为Stock类中private中的数据变量shares赋值
    52         share_val = price;  //为Stock类中private中的数据变量share_val赋值
    53         set_tot();  //调用在Stock类中private下定义的函数
    54     }
    55 }
    56 
    57 void Stock::update(double price)
    58 {
    59     share_val = price;   //为Stock类中private中的数据变量share_val赋值
    60     set_tot();  //调用在Stock类中private下定义的函数
    61 }
    62 
    63 //原版本如下
    64 //void Stock::show()
    65 //{
    66 //    using std::cout;
    67 //    using std::endl;
    68 //    cout << "Company: " << company
    69 //        << "Shares: " << shares<<'
    '      //'
    '表示输出换行
    70 //        << "share Price: $" << share_val
    71 //        << "Total Worth" << total_val << endl;
    72 //}
    73 
    74 //修改版本如下
    75 void Stock::show()
    76 {
    77     using std::cout;
    78     using std::endl;
    79     using std::ios_base;
    80     ios_base::fmtflags orig = cout.setf(ios_base::fixed, ios_base::floatfield); //定义变量orig
    81     //fmtflags是一种在ios_base中的数据类型
    82     std::streamsize prec = cout.precision(3);  //定义变量prec 
    83     //orig和prec都是为了保持原始的输出状态
    84     cout << "Company: " << company
    85         << "Shares: " << shares << '
    ';      //'
    '表示输出换行
    86 
    87     cout.precision(2);  //set format to #.##
    88 
    89     cout << "share Price: $" << share_val
    90          << "Total Worth" << total_val << endl;
    91     
    92     //恢复原始的输出状态
    93     cout.setf(orig, ios_base::floatfield);
    94     cout.precision(prec);
    95 }
    stock00.cpp
     1 //使用类
     2 //stock_main()
     3 
     4 #include <iostream>
     5 #include "stock00.h"
     6 
     7 int main()
     8 {
     9     Stock fluffy_the_cat;  //使用类Stock创建类对象fluffy_the_cat
    10 
    11     fluffy_the_cat.acquire("NanoSmart", 20, 12.50); //使用类对象fluffy_the_cat调用类Stock中的acquire()方法
    12     fluffy_the_cat.show();  //使用类对象fluffy_the_cat调用类Stock中的show()方法
    13 
    14     fluffy_the_cat.buy(15, 18.125);  //使用类对象fluffy_the_cat调用类Stock中的buy()方法
    15     fluffy_the_cat.show();
    16 
    17     fluffy_the_cat.sell(400, 20.00);  //使用类对象fluffy_the_cat调用类Stock中的sell()方法
    18     fluffy_the_cat.show();
    19 
    20     fluffy_the_cat.buy(300000, 40.125);  //Stock类中的buy()方法第一个形参为long型,第二个为double型
    21     fluffy_the_cat.show();
    22 
    23     fluffy_the_cat.sell(300000, 0.125);  //使用类对象fluffy_the_cat调用类Stock中的sell()方法
    24     fluffy_the_cat.show();
    25 
    26     system("pause");
    27     return 0;
    28 }
    stcok_main.cpp

    程序执行结果:

     类的构造函数和析构函数 

    01)问题的提出:Stock类不能像标准类型(如int、double)那样初始化变量,例如:
      int year = 2019;
      struct thing
      {
       char* pn;
       int m;
      };
      thing amabob = {"wodget", -23}; //合法
      Stock hot = {"Sukie's Autos, Inc", 20, 5.25}; //不合法,由于数据部分是私有的,于是提出了构造函数
    02)方法:创建对象时,使构造函数自动对私有数据进行初始化,构造函数的名称必须是要与类名字相同。
      构造函数的原型和函数头有一个有趣的特征-虽然没有返回值,但没有被声明为void类型。实际上,构造函数没有
      声明类型
    03)现在为Stock声明一个构造函数,由于需要为Stock对象提供3个值,所以也应为构造函数提供3个值(第四个值total_val
      成员,是根据shares和share_val计算得到的,因此不必为构造函数提供这个值),在public中的声明构造函数方法如下:

    //声明构造函数
      Stock(const string & co, long n = 0, double pr = 0.0); //在public部分声明构造函数,对n和pr赋初值,只能在h文件中写上初值

    //构造函数定义
      Stock::Stock(const string & co, long n = 0, double pr = 0.0)  //这样写是会报错的因为初值只能是在h文件中

             Stock::Stock(const string & co, long n, double pr)  //这样写才是可以得
      {
      company = co;
      if(n<0)
      {
        std::cout<<"Number of shares can't be negative; "
             <<company<<" shares set to 0"<<std::endl;
        shares = 0;
      }
      else
        share_val = n;
      share_val = pr;
      set_tot();
      }
    04)使用构造函数
      第一种方法(显式的调用构造函数):
      Stock food = Stock("World Cabbage", 220, 1.25); //将food对象的company设置为World Cabbage,
      //shares成员设置为220,share_val设置为1.25
      另一种方法(隐式的调用构造函数):
      Stock garment("Furry Mason", 50, 2.5);
      //上局等价于Stock garment = Stock("Furry Mason", 50, 2.5);
    05)类、对象、指针、new一起使用的例子:
      Stock* pstock = new Stock("Electroshock Games", 18, 19.0);
      //该句创建一个Stock对象,将其初始化为参数提供的值,并将该对象的地址赋给pstock指针。在这种情况下
      //对象没有名称,但是可以通过该对象的地址(即指针)来管理该对象
    06)无法通过对象来调用构造函数,因为在构造函数在构造出对象之前,对象时不存在的。因此构造函数被用来
       创建对象,而不腻通过对象来调用。(自己的理解:构造函数只是对对象的私有数据进行一种初始化操作)
    07)默认构造函数
       为类定义了构造函数后,程序员就必须为它提供默认构造函数,
       声明默认构造函数的方法一:
       Stock(const string & co = "Error", int n=0, double pr=0.0);  //给已有的构造函数所有的参数提供默认值
       //上一句和构造函数的声明(57句)不同的地方是,默认构造函数中的每一个形参必须有默认值
      //但是在构造函数的声明中(57句),形参可以没有默认值
       声明默认构造函数的方法二:
       Stock();    //通过函数重载来定义另一个构造函数-- 一个没有参数的构造函数
       由于只能有一个默认构造函数,因此不要同时采用这两种方式。
       实际上,通常应初始化所有的对象,以确保所有成员一开始就有已知的合理值。因此用户定义的默认构造函数
       通常给所有成员提供隐式初始化值。例如,下面为Stock类定义的一个默认构造函数:
       Stock::Stock()
       {
       company = "no name";
       shares = 0;
       share_val = 0.0;
       total_val = 0.0;
       }
    08总结

     1 //声明构造函数(形参可以没有默认值)
     2 Stock(const string & co, long n = 0, double pr = 0.0); //在public部分声明构造函数
     3 
     4 //构造函数定义
     5 Stock::Stock(const string & co, long n = 0, double pr = 0.0)
     6 {
     7 company = co;
     8 share_val = n;
     9 share_val = pr;
    10 sts_tot();
    11 }
    12 
    13 //调用构造函数
    14 Stock food = Stock("World Cabbage", 220, 1.25); //显式的调用构造函数
    15 Stock garment("Furry Mason", 50, 2.5); //隐式的调用构造函数
    16 
    17 // 默认构造函数 
    18 //声明默认构造函数的方法一(必须为每一个形参提供默认值):
    19 Stock(const string & co = "Error", int n=0, double pr=0.0);//给已有的构造函数所有的参数提供默认值
    20 //声明默认构造函数的方法二:
    21 Stock(); //通过函数重载来定义另一个构造函数-- 一个没有参数的构造函数
    22 //默认构造函数的定义方法
    23 Stock::Stock()
    24 {
    25 company = "no name";
    26 shares = 0;
    27 share_val = 0.0;
    28 total_val = 0.0;
    29 }
    总结:构造函数声明、定义;构造函数的使用;默认函数的声明和定义,调用方法;析构函数定义和声明

    09)在设计类时,通常提供对所有类成员做隐式初始化的默认构造函数
    10)使用上述任何一种方式(没有参数或所有参数都有默认值)创建了默认构造函数之后,便可以声明对象变量,而
      不对他们进行显示初始化;
      Stock first; //隐式的调用默认构造函数
      Stock first = Stock(); //显式的调用默认构造函数
      Stock* prelief = new Stock; //隐式的调用默认构造函数
      Stock first("Concreat Conglomerate"); //隐式的调用非默认构造函数
      Stock second(); //声明一个函数second(),该函数返回值为Stock对象
      Stock third; //隐式的调用默认构造函数

    /* 析构函数 */
    /*
    01)析构函数的由来:用构造函数创建对象后,程序负责跟踪该对象,直到其过期为止。对象过期时,程序将自动
       调用析构函数。析构函数负责完成清理工作,例如,如果构造函数使用new来分配内存,则析构函数将使用delete
       来释放内存。Stock的构造函数没有使用new,因此析构函数时间上没有需要完成的任务。
    02)析构函数的名称:在类名前加上~即可。因此Stock类的析构函数为~Stock()。析构函数没有返回值、没有形参
       也没有声明类型,因此Stock析构函数的原型必须是这样的: ~Stock();
    03)析构函数的定义:由于析构函数不承担任何重要的工作,因此可以将它编写为不执行任何操作的函数:
      Stock::~Stock()
      {

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

    05)什么时候执行析构函数?
       A 如果由类创建的对象为静态存储类对象,则析构函数将在程序结束时自动被调用;
       B 如果由类创建的对象为自动存储类对象,则析构函数将在程序执行完代码块时被调用;
       C 如果对象是通过new创建的,则它将驻留在栈内或自由存储区中,当使用delete来释放内存时,其析构函数将
         自动被调用。
    06)静态存储类对象即用static创建的对象
      自动存储类对象即在函数内部创建的对象

      1 /*  类的构造函数和析构函数  */
      2 
      3 //通过一个例子来说明如何创建类的构造函数和析构函数
      4 //类声明通常在h文件完成,通常将类的首字母大写
      5 //stock10.h
      6 //去掉了acquire()函数
      7 //增加了默认构造函数、构造函数、析构函数
      8 #ifndef STOCK10_H_    //编译器只编译一次
      9 #define STOCK10_H_  
     10 
     11 #include <string>
     12 
     13 class Stock  //类声明,只能用class
     14 {
     15 private:     //private可以不用写,因为这是类对象默认的访问控制,但是也可以写上的
     16     std::string company;  //定义string类变量company
     17     long shares;
     18     double share_val;
     19     double total_val;
     20     void set_tot()  //定义函数set_tot(),在类声明中定义的函数,将自动成为内联函数
     21     {
     22         total_val = share_val * shares;
     23     }
     24 public:
     25     Stock();  //声明默认构造函数
     26     Stock(const std::string & co, long n = 0, double pr = 0.0);  //声明构造函数,在这里变量n和pr就相当于默认参数了,注意默认值只能在h文件中添加,在定义出就得删除
     27     ~Stock();  //声明析构函数
     28     void buy(long num, double price);  //声明函数
     29     void sell(long num, double price);  //声明函数
     30     void update(double price);  //声明函数
     31     void show();  //声明函数
     32 };  //不要忘记分号
     33 
     34 #endif
     35 
     36 
     37 
     38 
     39 /*   类的构造函数和析构函数  */
     40 /*
     41 01)问题的提出:Stock类不能像标准类型(如int、double)那样初始化变量,例如:
     42    int year = 2019;
     43    struct thing
     44    {
     45        char* pn;
     46        int m;
     47    };
     48    thing amabob = {"wodget", -23};  //合法
     49    Stock hot = {"Sukie's Autos, Inc", 20, 5.25};  //不合法,由于数据部分是私有的,于是提出了构造函数
     50 02)方法:创建对象时,使构造函数自动对私有数据进行初始化,构造函数的名称必须是要与类名字相同。
     51    构造函数的原型和函数头有一个有趣的特征-虽然没有返回值,但没有被声明为void类型。实际上,构造函数没有
     52    声明类型
     53 03)现在为Stock声明一个构造函数,由于需要为Stock对象提供3个值,所以也应为构造函数提供3个值(第四个值total_val
     54    成员,是根据shares和share_val计算得到的,因此不必为构造函数提供这个值),在public中的声明构造函数方法如下:
     55    
     56    //声明构造函数
     57    Stock(const string & co, long n = 0, double pr = 0.0);  //在public部分声明构造函数
     58    
     59    //构造函数定义
     60    Stock::Stock(const string & co, long n = 0, double pr = 0.0)
     61    {
     62       company = co;
     63       if(n<0)
     64       {
     65           std::cout<<"Number of shares can't be negative; "
     66                     <<company<<" shares set to 0"<<std::endl;
     67           shares = 0;
     68       }
     69       else
     70            share_val = n;
     71       share_val = pr;
     72       set_tot();
     73    }
     74 04)使用构造函数
     75    第一种方法(显式的调用构造函数):
     76    Stock food = Stock("World Cabbage", 220, 1.25); //将food对象的company设置为World Cabbage,
     77    //shares成员设置为220,share_val设置为1.25
     78    另一种方法(隐式的调用构造函数):
     79    Stock garment("Furry Mason", 50, 2.5);
     80    //上局等价于Stock garment = Stock("Furry Mason", 50, 2.5);
     81 05)类、对象、指针、new一起使用的例子:
     82    Stock* pstock  = new Stock("Electroshock Games", 18, 19.0);
     83    //该句创建一个Stock对象,将其初始化为参数提供的值,并将该对象的地址赋给pstock指针。在这种情况下
     84    //对象没有名称,但是可以通过该对象的地址(即指针)来管理该对象
     85 06)无法通过对象来调用构造函数,因为在构造函数在构造出对象之前,对象时不存在的。因此构造函数被用来
     86    创建对象,而不腻通过对象来调用。(自己的理解:构造函数只是对对象的私有数据进行一种初始化操作)
     87 07)默认构造函数
     88    为类定义了构造函数后,程序员就必须为它提供默认构造函数,
     89    声明默认构造函数的方法一:
     90    Stock(const string & co = "Error", int n=0, double pr=0.0);//给已有的构造函数所有的参数提供默认值
     91    //上一句和构造函数的声明(57句)不同的地方是,默认构造函数中的每一个形参必须有默认值
     92    //但是在构造函数的声明中(57句),形参可以没有默认值
     93    声明默认构造函数的方法二:
     94    Stock();  //通过函数重载来定义另一个构造函数-- 一个没有参数的构造函数
     95    由于只能有一个默认构造函数,因此不要同时采用这两种方式。
     96    实际上,通常应初始化所有的对象,以确保所有成员一开始就有已知的合理值。因此用户定义的默认构造函数
     97    通常给所有成员提供隐式初始化值。例如,下面为Stock类定义的一个默认构造函数:
     98    Stock::Stock()
     99    {
    100      company = "no name";
    101      shares = 0;
    102      share_val = 0.0;
    103      total_val = 0.0;
    104    }
    105 08总结
    106    //声明构造函数(形参可以没有默认值)
    107    Stock(const string & co, long n = 0, double pr = 0.0);  //在public部分声明构造函数
    108 
    109    //构造函数定义
    110    Stock::Stock(const string & co, long n = 0, double pr = 0.0)
    111    {
    112       company = co;
    113       share_val = n;
    114       share_val = pr;
    115       sts_tot();
    116    }
    117 
    118    //调用构造函数
    119    Stock food = Stock("World Cabbage", 220, 1.25); //显式的调用构造函数
    120    Stock garment("Furry Mason", 50, 2.5);  //隐式的调用构造函数
    121 
    122    //  默认构造函数  
    123    //声明默认构造函数的方法一(必须为每一个形参提供默认值):
    124    Stock(const string & co = "Error", int n=0, double pr=0.0);//给已有的构造函数所有的参数提供默认值
    125    //声明默认构造函数的方法二:
    126    Stock();  //通过函数重载来定义另一个构造函数-- 一个没有参数的构造函数
    127    //默认构造函数的定义方法
    128    Stock::Stock()
    129    {
    130      company = "no name";
    131      shares = 0;
    132      share_val = 0.0;
    133      total_val = 0.0;
    134    }
    135 09)在设计类时,通常提供对所有类成员做隐式初始化的默认构造函数
    136 10)使用上述任何一种方式(没有参数或所有参数都有默认值)创建了默认构造函数之后,便可以声明对象变量,而
    137    不对他们进行显示初始化;
    138    Stock first;  //隐式的调用默认构造函数
    139    Stock first = Stock();  //显式的调用默认构造函数
    140    Stock* prelief = new Stock;  //隐式的调用默认构造函数
    141    Stock first("Concreat Conglomerate");  //隐式的调用非默认构造函数
    142    Stock second();  //声明一个函数second(),该函数返回值为Stock对象
    143    Stock third;  //隐式的调用默认构造函数
    144 */
    145 
    146 /*  析构函数  */
    147 /*
    148 01)析构函数的由来:用构造函数创建对象后,程序负责跟踪该对象,直到其过期为止。对象过期时,程序将自动
    149    调用析构函数。析构函数负责完成清理工作,例如,如果构造函数使用new来分配内存,则析构函数将使用delete
    150    来释放内存。Stock的构造函数没有使用new,因此析构函数时间上没有需要完成的任务。
    151 02)析构函数的名称:在类名前加上~即可。因此Stock类的析构函数为~Stock()。析构函数没有返回值、没有形参
    152    也没有声明类型,因此Stock析构函数的原型必须是这样的: ~Stock();
    153 03)析构函数的定义:由于析构函数不承担任何重要的工作,因此可以将它编写为不执行任何操作的函数:
    154    Stock::~Stock()
    155    {
    156    
    157    }
    158 04)由于类对象过期时,西沟函数将自动被调用,因此必须有一个西沟函数。如果程序员没有提供析构函数,编译器
    159    将隐式的声明一个析构函数,并在发现导致对象被删书的代码后,提供默认析构函数的定义。
    160 05)什么时候执行析构函数?
    161    A 如果由类创建的对象为静态存储类对象,则析构函数将在程序结束时自动被调用;
    162    B 如果由类创建的对象为自动存储类对象,则析构函数将在程序执行完代码块时被调用;
    163    C 如果对象是通过new创建的,则它将驻留在栈内或自由存储区中,当使用delete来释放内存时,其析构函数将
    164      自动被调用。
    165 06)静态存储类对象即用static创建的对象
    166    自动存储类对象即在函数内部创建的对象
    167 */
    stock10.h
      1 //stock10.cpp
      2 
      3 #include <iostream>
      4 #include "stock10.h"
      5 
      6 
      7 //默认构造函数的定义
      8 Stock::Stock()
      9 {
     10     std::cout << "执行默认构造函数:" << std::endl;
     11     company = "no name";
     12     shares = 0;
     13     share_val = 0.0;
     14     total_val = 0.0;
     15 }
     16 
     17 //构造函数定义
     18 Stock::Stock(const std::string & co, long n, double pr)  //注意默认值只能在h文件中添加,在定义出就得删除
     19 {
     20     std::cout << "构造函数使用 " << co << " 被调用" << std::endl;
     21     company = co;
     22     if (n < 0)
     23     {
     24         std::cout << "Number of shares can't be negative; "
     25             << company << " shares set to 0" << std::endl;
     26         shares = 0;
     27     }
     28     else
     29         shares = n;
     30     share_val = pr;
     31     set_tot();
     32 }
     33 
     34 //析构函数定义
     35 Stock::~Stock()
     36 {
     37     std::cout << "析构函数已被执行" << std::endl;
     38     std::cout << "Bye " << company << std::endl;
     39 }
     40 
     41 void Stock::buy(long num, double price)
     42 {
     43     if (num < 0)
     44     {
     45         std::cout << "Number of shares purchased can't be negative; "
     46             << "Transaction is aborted" << std::endl;
     47     }
     48     else
     49     {
     50         shares += num;  //为Stock类中private中的数据变量shares赋值
     51         share_val = price;  //为Stock类中private中的数据变量share_val赋值
     52         set_tot();  //调用在Stock类中private下定义的函数
     53     }
     54 }
     55 
     56 //其他的函数定义
     57 void Stock::sell(long num, double price)
     58 {
     59     using std::cout;
     60     using std::endl;
     61     if (num < 0)
     62     {
     63         cout << "Number of shares sold can't be negetive. "
     64             << "Transaction is aborted" << endl;
     65     }
     66     else if (num > shares)
     67     {
     68         cout << "You can't sell more than you have. "
     69             << "Transaction is aborted. " << endl;
     70     }
     71     else
     72     {
     73         shares -= num;  //为Stock类中private中的数据变量shares赋值
     74         share_val = price;  //为Stock类中private中的数据变量share_val赋值
     75         set_tot();  //调用在Stock类中private下定义的函数
     76     }
     77 }
     78 
     79 void Stock::update(double price)
     80 {
     81     share_val = price;   //为Stock类中private中的数据变量share_val赋值
     82     set_tot();  //调用在Stock类中private下定义的函数
     83 }
     84 
     85 void Stock::show()
     86 {
     87     using std::cout;
     88     using std::endl;
     89     using std::ios_base;
     90     ios_base::fmtflags orig = cout.setf(ios_base::fixed, ios_base::floatfield); //定义变量orig
     91     //fmtflags是一种在ios_base中的数据类型
     92     std::streamsize prec = cout.precision(3);  //定义变量prec 
     93     //orig和prec都是为了保持原始的输出状态
     94     cout << "Company: " << company
     95         << " Shares: " << shares << '
    ';      //'
    '表示输出换行
     96 
     97     cout.precision(2);  //set format to #.##
     98 
     99     cout << "share Price: $" << share_val
    100         << " Total Worth" << total_val << endl;
    101 
    102     //恢复原始的输出状态
    103     cout.setf(orig, ios_base::floatfield);
    104     cout.precision(prec);
    105 }
    stock10.cpp
     1 //suer_main()
     2 
     3 #include <iostream>
     4 #include "stock10.h"
     5 
     6 int main()
     7 {
     8     {
     9         using std::cout;
    10         using std::endl;
    11 
    12         /* 隐式的调用构造函数 */
    13         Stock stock1("Nanosmart", 12, 20.0);//隐式的调用构造函数,创建stock1对象,并未stock1对象中的私有数据赋初值
    14         //等价于Stock stock1 = Stock("Nanpsmart", 12, 20.0);  此句是显式的调用构造函数
    15         //而且上句在创建对象stock1时,自动执行stock1.cpp中构造函数的定义
    16         stock1.show();
    17 
    18         /* 隐式的调用默认构造函数 */
    19         cout << endl;
    20         Stock first;  //用类Stock创建对象,(自动的调用)隐式的调用默认构造函数,默认的构造函数被执行
    21         first.show();
    22 
    23         /*  对象之间的操作  */
    24         cout << endl;
    25         cout << "将一个对象赋给两外一个对象,并分别显示他们" << endl;
    26         first = stock1;  //将对象stock1赋给对象first
    27         first.show();  //显示的是对象stock1中的信息
    28         stock1.show();  //显示的是对象stock1中的信息
    29 
    30         /*  对对象stock1进行重新赋数据  */
    31         cout << endl;
    32         cout << "对对象stock1进行重新赋数据" << endl;
    33         stock1 = Stock("Nifty Foods", 10, 50.0);  //由于stock1是存在 的对象,所以此处直接对其进行重新赋值就可以
    34         stock1.show();
    35         //上句会调用构造函数的定义所在的语句
    36     }
    37 
    38     system("pause");
    39     return 0;
    40 }
    41 
    42 /*  程序说明  */
    43 /*
    44 01)可以看到在mian()的开头和末尾多了一个大括号。诸如stock1和first等自动变量在陈鼓型退出其定义所属的
    45    代码块时消失,在stock1和first等对象消失的时候,会自动的执行析构函数。如果没有这些大括号,代码块
    46    将为整个main()函数,因此仅当main()执行完毕后才会调用析构函数。在窗口环境中,这意味着将两个析构
    47    函数在调用前,DOS窗口将会被关闭,导致自己无法看到最后析构函数执行的结果,因此加上一个大括号,从
    48    而显示相应的信息。
    49 02)对于stock1 = Stock("Nifty Foods", 10, 50.0);
    50    stock1已存在,因此该句不是对stock1进行初始化,而是将新指赋给它。这是通过让构造程序创建一个新的、
    51    临时的对象,然后将临时对象的内容复制给stock1实现的。随后该临时对象将会消失,即调用析构函数。
    52    注意:有些编译器可能要过一段时间才会删除临时数据,因此析构函数的调用将会延迟。
    53 03)代码块中的程序执行结束时,其局部变量(stock1和first)将消失,由于这种变量被放在栈中,因此最后创建的
    54    变量将会被删除,最先创建的对象将最后被删除。
    55 04)使用对象的赋值的时候,在赋值之前,总会导致创建一个临时对象,通过该临时对象将数据复制给被传递的对象,
    56    然后改临时对象将消失。最后调用析构函数。
    57 05)如果既可以通过初始化,也可以通过赋值来设置对象的值,则应采用初始化的方式,这样的效率会更高
    58 */
    user_main.cpp

    user_main()程序说明

    01)可以看到在mian()的开头和末尾多了一个大括号。诸如stock1和first等自动变量在陈鼓型退出其定义所属的
      代码块时消失,在stock1和first等对象消失的时候,会自动的执行析构函数。如果没有这些大括号,代码块
      将为整个main()函数,因此仅当main()执行完毕后才会调用析构函数。在窗口环境中,这意味着将两个析构
      函数在调用前,DOS窗口将会被关闭,导致自己无法看到最后析构函数执行的结果。因此加上一个大括号,从
      而显示相应的信息。
    02)对于stock1 = Stock("Nifty Foods", 10, 50.0);
      stock1已存在,因此该句不是对stock1进行初始化,而是将新指赋给它。这是通过让构造程序创建一个新的、
      临时的对象,然后将临时对象的内容复制给stock1实现的。随后该临时对象将会消失,即调用析构函数。
      注意:有些编译器可能要过一段时间才会删除临时数据,因此析构函数的调用将会延迟。
    03)代码块中的程序执行结束时,其局部变量(stock1和first)将消失,由于这种变量被放在栈中,因此最后创建的
      变量将会被删除,最先创建的对象将最后被删除。
    04)使用对象的赋值的时候,在赋值之前,总会导致创建一个临时对象,通过该临时对象将数据复制给被传递的对象,
      然后改临时对象将消失。最后调用析构函数。
    05)如果既可以通过初始化,也可以通过赋值来设置对象的值,则应采用初始化的方式,这样的效率会更高

     执行结果:

     const成员函数

     问题的提出:
     对于下面的代码段:
     const Stock land = Stock("Klug,Proerties"); //其余两个参数使用默认参数
     land.show(); //这一句是会报错的
     因为show()的代码无法确保land对象中的数据不被修改。C++解决这一问题的关键是将const关键字方在函数括号的后面
     也就是说,show()应该这样声明:
     void show() const; //声明show()函数,保证不会修改调用对象。即当类对象调用show()方法时,show()方法不会修改对象中的数据
     同样函数的定义应该是这样的:
     void Stock::show() const //定义show()函数,保证不会修改调用对象
     {
       ...
     }

    书中的构造函数和析构函数的总结  m4

    A 构造函数是一种特殊的函数,在创建类对象的时候被调用。构造函数和类名相同,通常用于初始化类对象的成员。
     初始化应与构造函数的参数列表匹配。
    B 假设Bozo类的构造函数的原型如下:
     Bozo(const char* fname, const char* lname);
     则可以使用它来初始化新对象:
     Bozo bozetta = Bozo("Bizetta", "BIggens"); //显式的调用构造函数 (使用小括号)
     Bozo fufu("Fufu","Deep"); //隐式的调用构造函数 (使用小括号)
     Bozo* pc = new Bozo("Popo","Le Peu"); //使用类Bozo创建一个指针,指向成员为Popo Le Peu的对象
     如果编译器支持C++11,则可以使用列表初始化:
     Bozo bozetta = {"Bizetta", "BIggens"}; //C++11(使用大括号) 进行列表初始化
     Bozo fufu = {"Fufu","Deep"}; //C++11(使用大括号)进行列表初始化
     Bozo* pc = new Bozo{"Popo","Le Peu"}; //C++11(使用大括号)进行列表初始化
    C 如果一个构造函数只有一个参数,则可以使用赋值语句对该对象进行初始化:
     例如有一个构造函数的原型:
     Bozo(const int n);
     则可以使用下面的任何一种方法来初始化该对象:
     Bozo dribble = Bozo(44); //原来使用的方法,显式的调用构造函数
     Bozo roon(66); //原来使用的方法,隐式的调用构造函数
     Bozo tubby = 32; //新的方式,使用赋值语句对tubby对象进行初始化。
    D 默认构造函数没有参数,因此如果创建对象时没有进行显式的初始化,则将调用默认构造函数。
     对于未被初始化的对象,程序将使用默认构造函数来创建该对象:
     Bozo bubi; //调用默认构造函数
     Bozo* pb = new Bozo; //调用默认构造函数
    E 如果构造函数使用了new,则必须提供使用delete的析构函数
     每个类只能有一个析构函数

     this指针 & 类对象作为类方法的参数 & 返回值为类对象的引用

    01)可以声明下面的一个函数:
       const Stock & topval(const Stock & s) const;
       A 声明一个类方法topval(),其中返回值为一个const类型指向类Stock的引用,即const Stock &部分
       B 类方法topval()的参数为const类型指向类Stock的引用s
       C 最后一个const表明类方法topval()中的代码不可修改调用对象中的数据
    02)该函数的定义方法
      const Stock & Stock::topval(const Stock & s) const
      {
        if (s.total_val > total_val)
          return s; //这里的s就是作为参数传入进来的类对象
        else
          return *this; //这里的this是一个指针,指向用来调用成员函数(这里的成员函数为top_val)的对象
      }
      //该程序段的作用就是比较两个对象,以对象中的total_val数据为依据,那个对象中的该数据大,
      //最后返回那一个对象
    03)该函数的调用方法为线面两种方法之一:
       top = stock1.top_val(stock2); //上面的this指针此时指向对象stock1,s(类方法的形参)即为stock2
       top = stock2.top_val(stock1); //上面的this指针此时指向对象stock2,s(类方法的形参)即为stock1
    04)每个成员函数(包括构造函数和析构函数)都有一个this指针。this指针指向调用对象。如果方法需要引用整个
       调用对象,则可以使用表达式*this。
       在函数的括号后面使用const限定符将this限定为sonst,这样就不能使用this来修改对象的值。

    对象数组 

    01)声明方法:Stock my stuff[4]; //声明包含4个Stock对象的数组
      如果在类Stock中定义了默认构造函数,那么上述语句将会自动的调用默认构造函数
    02)每个元素(mystuff[0]和mystuff[1]等)都是Stock的对象,可以使用Stcok中的方法:
      mystuff[0].show();
      mystuff[1].update(); 等
      const Stock* tops = mystuff[2].topval(mystuff[1]);  //调用const Stock & Stock::topval(const Stock & s) const函数
    03)可以用构造函数来初始数组元素。在这种情况下,必须为每个元素调用构造函数:
      const int STKS = 10;
      Stock stocks[STKS] =
      {
        Stock("Nanosmart",12.5,20),
        Stock(), //使用默认构造函数对stocks[1]进行初始化
        Stock("Monolithic Obelisks", 130, 3.25),
        Stock("Fleep Enterises", 60, 6.5)
      };
      上述代码使用Stock(const string & co,long n, double pr)初始化stocks[0]、stocks[2]和stocks[3],
      使用默认构造函数Stock()初始化stocks[1],由于stocks对象数组内有10个元素,现在只是初始化了4个元素
      其余的六个元素将使用默认构造函数进行初始化
    04)要创建对象数组,那么这个类必须要有默认构造函数。因为创建对象数组的时候,如果没有初始化,则首先
      使用默认构造函数进行初始化的,如果有初始化,才不用默认构造函数的

     1 /*  类的构造函数和析构函数  */
     2 
     3 //通过一个例子来说明如何创建类的构造函数和析构函数
     4 //类声明通常在h文件完成,通常将类的首字母大写
     5 //stock10.h
     6 //去掉了acquire()函数
     7 //增加了默认构造函数、构造函数、析构函数
     8 #ifndef STOCK20_H_    //编译器只编译一次
     9 #define STOCK20_H_  
    10 
    11 #include <string>
    12 
    13 class Stock  //类声明,只能用class
    14 {
    15 private:     //private可以不用写,因为这是类对象默认的访问控制,但是也可以写上的
    16     std::string company;  //定义string类变量company
    17     long shares;
    18     double share_val;
    19     double total_val;
    20     void set_tot()  //定义函数set_tot(),在类声明中定义的函数,将自动成为内联函数
    21     {
    22         total_val = share_val * shares;
    23     }
    24 public:
    25     Stock();  //声明默认构造函数
    26     Stock(const std::string & co, long n = 0, double pr = 0.0);  //声明构造函数,在这里变量n和pr就相当于默认参数了,注意默认值只能在h文件中添加,在定义出就得删除
    27     ~Stock();  //声明析构函数
    28     void show() const;  //声明函数,最后加一个conts的目的是让show()方法不能修改对象中的数据
    29     const Stock & top_val(const Stock & s) const;
    30 };  //不要忘记分号
    31 
    32 #endif
    33 
    34 
    35 
    36 
    37 /*   this指针 & 类对象作为类方法的参数 & 返回值为类对象的引用  */
    38 /*
    39 01)可以声明下面的一个函数:
    40    const Stock & topval(const Stock & s) const;
    41    A 声明一个类方法topval(),其中返回值为一个const类型指向类Stock的引用,即const Stock &部分
    42    B 类方法topval()的参数为const类型指向类Stock的引用s
    43    C最后一个const表明类方法topval()中的代码不可修改调用对象中的数据
    44 02)该函数的定义方法:
    45    const Stock & Stock::topval(const Stock & s) const
    46    {
    47        if (s.total_val > total_val)
    48            return s;  //这里的s就是作为参数传入进来的类对象
    49        else
    50            return *this; //这里的this是一个指针,指向用来调用成员函数(这里的成员函数为top_val)的对象
    51    }
    52    //该程序段的作用就是比较两个对象,以对象中的total_val数据为依据,那个对象中的该数据大,
    53    //则返回哪一个对象
    54 03)该函数的调用方法为线面两种方法之一:
    55    top = stock1.top_val(stock2);  //上面的this此时指向对象stock1,s(类方法的形参)即为stock2
    56    top = stock2.top_val(stock1);  //上面的this此时指向对象stock2,s(类方法的形参)即为stock1
    57 04)每个成员函数(包括构造函数和析构函数)都有一个this指针。this指针指向调用对象。如果方法需要引用整个
    58    调用对象,则可以使用表达式*this。
    59    在函数的括号后面使用const限定符将this限定为sonst,这样就不能使用this来修改对象的值。
    60 */
    61 
    62 /*  对象数组  */
    63 /*
    64 01)声明方法:Stock my stuff[4];  //声明包含4个Stock对象的数组
    65    如果在类Stock中定义了默认构造函数,那么上述语句将会自动的调用默认构造函数
    66 02)每个元素(mystuff[0]和mystuff[1]等)都是Stock的对象,可以使用Stcok中的方法:
    67    mystuff[0].show();
    68    mystuff[1].update(); 等
    69    const Stock* tops = mystuff[2].topval(mystuff[1]);
    70 03)可以用构造函数来初始数组元素。在这种情况下,必须为每个元素调用构造函数:
    71    const int STKS = 10;
    72    Stock stocks[STKS] = 
    73    {
    74      Stock("Nanosmart",12.5,20),
    75      Stock(),  //使用默认构造函数对stocks[1]进行初始化
    76      Stock("Monolithic Obelisks", 130, 3.25),
    77      Stock("Fleep Enterises", 60, 6.5)
    78    };
    79    上述代码使用Stock(const string & co,long n, double pr)初始化stocks[0]、stocks[2]和stocks[3],
    80    使用默认构造函数Stock()初始化stocks[1],由于stocks对象数组内有10个元素,现在只是初始化了4个元素
    81    其余的六个元素将使用默认构造函数进行初始化
    82 04)要创建对象数组,那么这个类必须要有默认构造函数。因为创建对象数组的时候,如果没有初始化,则首先
    83    使用默认构造函数进行初始化的,如果有初始化,才不用默认构造函数的
    84 05)
    85 */
    stock20.h
     1 //stock10.cpp
     2 
     3 #include <iostream>
     4 #include "stock20.h"
     5 
     6 
     7 //默认构造函数的定义
     8 Stock::Stock()
     9 {
    10     std::cout << "执行默认构造函数:" << std::endl;
    11     company = "no name";
    12     shares = 0;
    13     share_val = 0.0;
    14     total_val = 0.0;
    15 }
    16 
    17 //构造函数定义
    18 Stock::Stock(const std::string & co, long n, double pr)  //注意默认值只能在h文件中添加,在定义出就得删除
    19 {
    20     std::cout << "构造函数使用 " << co << " 被调用" << std::endl;
    21     company = co;
    22     if (n < 0)
    23     {
    24         std::cout << "Number of shares can't be negative; "
    25             << company << " shares set to 0" << std::endl;
    26         shares = 0;
    27     }
    28     else
    29         shares = n;
    30     share_val = pr;
    31     set_tot();
    32 }
    33 
    34 //析构函数定义
    35 Stock::~Stock()
    36 {
    37     std::cout << "析构函数已被执行" << std::endl;
    38     std::cout << "Bye " << company << std::endl;
    39 }
    40 
    41 //其他的函数定义
    42 
    43 void Stock::show() const
    44 {
    45     using std::cout;
    46     using std::endl;
    47     using std::ios_base;
    48     ios_base::fmtflags orig = cout.setf(ios_base::fixed, ios_base::floatfield); //定义变量orig
    49     //fmtflags是一种在ios_base中的数据类型
    50     std::streamsize prec = cout.precision(3);  //定义变量prec 
    51     //orig和prec都是为了保持原始的输出状态
    52     cout << "Company: " << company
    53         << " Shares: " << shares << '
    ';      //'
    '表示输出换行
    54 
    55     cout.precision(2);  //set format to #.##
    56 
    57     cout << "share Price: $" << share_val
    58         << " Total Worth" << total_val << endl;
    59 
    60     //恢复原始的输出状态
    61     cout.setf(orig, ios_base::floatfield);
    62     cout.precision(prec);
    63 }
    64 
    65 //const Stock & top_val(const Stock & s) const  //刚刚写成这样了,导致出错
    66 const Stock & Stock::top_val(const Stock & s) const  
    67 {
    68     if (s.total_val > total_val)
    69         return s;  //这里的s就是作为参数传入进来的类对象
    70     else
    71         return *this;  //这里的this是一个指针,指向用来调用成员函数(这里的成员函数为top_val)的对象
    72 }
    73 //该函数返回值为const类型指向Stock类的引用,参数为const类型指向Stock类的引用
    74 //最后一个const表示类方法top_val()不可以修改调用该方法的对象内的数据
    stock20.cpp
     1 //suer_main()
     2 
     3 #include <iostream>
     4 #include "stock20.h"
     5 
     6 const int STKS = 4;
     7 
     8 int main()
     9 {
    10     using std::cout;
    11     using std::endl;
    12 
    13     Stock stocks[STKS]   //创建一个包含4个元素(对象)的对象数组,并调用构造函数进行初始化
    14     {
    15         Stock("Nanosmart", 12, 20),
    16         Stock("Boffo Objects", 200, 2.0),  //使用默认构造函数对stocks[1]进行初始化
    17         Stock("Monolithic Obelisks", 130, 3.25),
    18         Stock("Fleep Enterises", 60, 6.5)
    19     };  //勿忘分号
    20     cout << "显示对象数组内对象的数据:" << endl;
    21     for (int i = 0; i < STKS; i++)
    22         stocks[i].show();  //使用类对象调用类方法
    23     const Stock * top = &stocks[0];  //新建一个指向stocks[0]的指针top,后面加上&stocks[0]的意思是对指针初始化
    24     for (int i = 1; i < STKS; i++)  //由于top_val设计的目的是将两个对象中的数据进行比较,top已经是指向第一个对象了,所以这里要从i=1开始
    25         top =  &(top->top_val(stocks[i])); 
    26         //由于top是一个指向对象stocks[0]的指针,类似于结构指针,要用间接运算符->访问成员
    27         //且top_val()函数返回的是一个对象,而等号左边是一个指针,所以要对该返回的对象取地址
    28         //即最后要使用地址运算符&对返回的对象取地址
    29     cout << "Total Worth最高的对象及该对象内的数据为:" << endl;
    30     top->show();  //指向对象的指针访问类中函数的方法:使用间接运算符->
    31     //由于返回的是const对象,那么show()方法也必须是一个const类型的!!刚刚因这个而出错了
    32 
    33     system("pause");
    34     return 0;
    35 }
    36 
    37 /*  user_main()程序说明  */
    38 /*
    39 01)指向对象的指针访问类中函数的方法:使用间接运算符->
    40    Stock stock1;  //创建一个类对象stock1
    41    Stock* pt = &stock1;  //创建一个指向stock1的指针pt
    42    pt->show();  //使用指针pt访问类中函数的方法
    43 */
    user_main.cpp

    /* user_main()程序说明 */
    01)指向对象的指针访问类中函数的方法:使用间接运算符->
        Stock stock1; //创建一个类对象stock1
        Stock* pt = &stock1; //创建一个指向stock1的指针pt
        pt->show(); //使用指针pt访问类中函数的方法

    执行结果:

    类作用域

    01)在类声明或成员函数定义中,可以使用未修饰的成员名称,就像sell()调用srt_tot()成员那样。
    02)构造函数名称在被调用时,才能被识别,因为它的名称与类名相同
    03)其他情况下,使用类成员函数的时候,必须根据上下文使用直接成员运算符(.)、间接成员运算符(->)或
         作用域解析运算符
    04)下面的代码演示了如何方位具有累作用域的标识符:

     1 #include <iostream>
     2 using namespace std;
     3 
     4 class IK
     5 {
     6 private:
     7     int fuss;  //fuss在类作用域中
     8 public:
     9     IK(int f = 9) { fuss = f; }  //创建一个构造函数,且有默认参数为9
    10     
    11     void ViewIK() const;  //声明一个类方法
    12 };
    13 
    14 void IK::ViewIK() const  //IK::使ViewIK在类作用域内
    15 {
    16     cout << fuss << endl;
    17 }
    18 
    19 int main()
    20 {
    21     IK* pik = new IK;  //创建指向类IK的指针pik,并为该指针分配存储空间
    22     IK ee = IK(8);  //创建类对象ee,并调用构造函数,8将覆盖默认值9
    23     ee.ViewIK();  //类对象将VIewIK带入到类作用域中来  显式覆盖掉默认值的8
    24     pik->ViewIK();  //显式默认值9
    25 
    26     delete pik;  //释放pik所指向的内存,但不会删除指针
    27 
    28     system("pause");
    29     return 0;
    30 }
    类作用域&指向类的指针的操作

    作用域为类的常量(在类中定义常量)

    01)声明类只是描述了对象的形式,并没有创建对象。因此在创建对象之前,将没有用于存储值的空间,因此下面
       的代码是错误的:
      class Bakery
      {
      private:
        const int months = 12; //不合法,因为此时还没有存储值的空间
        double costsp[months];
      }
    02)解决方法一:使用枚举
       在类声明中声明的枚举的作用域为整个类,因此可以使用枚举为整形常量提供作用域为整个类的符号名称。
       枚举的复习:
       enum spectrum {red,orange,yellow,green,blue,violet,indigo,ultraviolet}
       将spectrum称为枚举,将red,orange,yellow,green,blue,violet,indigo,ultraviolet称为枚举量,其中red=0,orange=1,yellow=2,green=3以此类推
       spectrum band; //使用新类型spectrum创建枚举变量band
       band=red; //有效
       band=2000; //无效
       band=red+green; //无效,因为没有为枚举定义算数运算
       band=spectrum(20); //有效,20不是枚举值,但20在取值范围之内
       所以Bakery类的改进方法如下:
      class Bakery
      {
      private:
        enum { months = 12 }; //由于此处并不打算创建枚举类型的变量,因此此处省略了枚举的名字
        double costsp[months];
      }
      注意:用这种方式声明枚举并不会创建枚举类数据成员,也就是说对象中不包含枚举。
      解决方法二:使用关键字static
      class Bakery
      {
      private:
        static const int months = 12; //合法
        double costsp[months];
      }

    似栈的C++实现方法

    01)栈的特征描述:
        可创建空栈
        可将数据项添加到栈顶(压如)push
        可从栈顶将数据项删除pop
       可查看栈是否填满,可查看栈是否为空
    02)以上也是将要写的一个代码实现的功能

     1 #ifndef STACK_H_
     2 #define STACK_H_
     3 
     4 typedef unsigned long Item;  //为unsigned long起别名为Item
     5 
     6 class Stack
     7 {
     8 private:
     9     enum {MAX = 5};  //创建一个枚举常量MAX
    10     Item items[MAX];  //创建一个数组,用这个数组来代替栈的概念
    11     int top;  //用top表示栈顶的位置(栈顶索引)
    12 public:
    13     Stack();  //声明默认构造函数
    14     bool isempty() const;  //声明一个不可更改对象内数据的、返回值为布尔值的函数
    15     bool isfull() const; 
    16     bool push(const Item & item);  //add item to stack(items)
    17     bool pop(Item & item);  //pop top into item
    18 };
    19 
    20 #endif
    stack.h
     1 //stack.cpp
     2 #include "stack.h"
     3 
     4 //默认构造函数的定义
     5 Stack::Stack()  //默认构造函数可以确保每次创建的栈都为空
     6 {
     7     top = 0;
     8 }
     9 
    10 bool Stack::isempty() const
    11 {
    12     return top == 0;  //如果top=0,则返回true
    13 }
    14 
    15 bool Stack::isfull() const
    16 {
    17     return top == MAX;  //如果top=MAX,则返回true
    18 }
    19 
    20 bool Stack::push(const Item & item)
    21 {
    22     if (top < MAX)
    23     {
    24         items[top++] = item;  //top++表示先使用top的值,然后再对top加1,类似于压栈操作
    25         return true;
    26     }
    27     else
    28         return false;
    29 }
    30 
    31 bool Stack::pop(Item & item)
    32 {
    33     if (top > 0)
    34     {
    35         item = items[--top];  //--top表示先对top的值先减1,再使用top的值
    36         return true;
    37     }
    38     else
    39         return false;
    40 }
    stack.cpp
     1 //user_mian.cpp
     2 #include <iostream>
     3 #include <cctype>  //for toupper() isalpha()
     4 #include "stack.h"
     5 
     6 int main()
     7 {
     8     using namespace std;
     9     Stack st;  //创建一个类对象st
    10     char ch;  //创建一个用来接收键盘输入的字符变量
    11     Item po;  //typedef unsigned long Item是在stack.h中的定义
    12 
    13     cout << "Please enter A to add a purchase orser," << endl;
    14     cout << "P to process a PO, or Q to quit" << endl;  //process a PO处理一个采购订单
    15 
    16     while (cin >> ch && toupper(ch) != 'Q') //如果输入的是字符,那么cin>>ch返回true;toupper(ch)是将ch转换为大写字母
    17     {
    18         while (cin.get() != '
    ')
    19             continue;  //输入一个字符之后,如果没有按回车健,那么继续去第二个while中去检测是否由有回车键按下
    20         if (!isalpha(ch))  //如果ch是字符字母,那么isalpha()返回非零值;否则返回0
    21         {
    22             cout << 'a';  //振铃,会发出响声
    23             cout << "Please enter a char to ch:";
    24             continue; //返回到第一个while中去执行
    25         }
    26         switch (ch)
    27         {
    28             case 'A':    //改句没有任何语句,且没有break,那么会自动转到case a中去执行。表示输入A或a都是可以的
    29             case 'a':cout << "Enter a PO number to add: ";
    30                 cin >> po;
    31                 if (st.isfull())
    32                     cout << "stack is already full" << endl;
    33                 else
    34                     st.push(po);
    35                 break;  //必须是要有这个break的,要不然会继续去执行case P
    36             case 'P':
    37             case 'p':
    38                 if (st.isempty())
    39                     cout << "stack is already empty" << endl;
    40                 else
    41                 {
    42                     st.pop(po);//由于在pop()定义中形参为引用,所以修改形参的值就相当于修改po的值,所以会动态的显示出栈信息
    43                     cout << "PO #" << po << " poped" << endl;
    44                 }
    45                 break;
    46         }
    47         cout << endl;
    48         cout << "Please enter A to add a purchase orser," << endl;
    49         cout << "P to process a PO, or Q to quit" << endl;  //process a PO处理一个采购订单
    50 
    51     }
    52     cout << "Bye" << endl;
    53 
    54     system("pause");
    55     return 0;
    56 }
    57 /* 总结 */
    58 /*
    59 01)在stack.h使用了typedef unsigned long Item为unsigned long起别名为Item
    60 02)在stack.h中定义的类声明了枚举常量MAX,方法为:enum {MAX = 5};实际上在类中声明常量是不允许的,
    61    但是可以声明枚举量,进而可以在类中使用该常量
    62 03)continue是省略continue以下的所有的语句,回到距离它自己最近的一个循环中去
    63 04)可以在switch语句中的case下继续去写一些语句,比如使用if语句,最后千万不要忘记加上break,否则
    64    会继续去执行下一个case语句的
    65 05)cctype头文件下的toupper(ch)函数是将ch转换成大写字母;isalpha(ch)是判断ch是否为字母,如果ch为字母,
    66    那么返回非零值,否则返回零值。
    67 06)a表示振铃,戴上耳机的话会听得到那个声音
    68 07)该函数模拟了栈的操作
    69 */
    user_main.cpp

    /* 总结 */
    /*
    01)在stack.h使用了typedef unsigned long Item为unsigned long起别名为Item
    02)在stack.h中定义的类声明了枚举常量MAX,方法为:enum {MAX = 5};实际上在类中声明常量是不允许的,
        但是可以声明枚举量,进而可以在类中使用该常量
    03)continue是省略continue以下的所有的语句,回到距离它自己最近的一个循环中去
    04)可以在switch语句中的case下继续去写一些语句,比如使用if语句,最后千万不要忘记加上break,否则
        会继续去执行下一个case语句的
    05)cctype头文件下的toupper(ch)函数是将ch转换成大写字母;isalpha(ch)是判断ch是否为字母,如果ch为字母,
        那么返回非零值,否则返回零值。
    06)a表示振铃,戴上耳机的话会听得到那个声音
    07)该函数模拟了栈的操作
    */

    执行结果:

    2019.04.10 haijing in HZ

  • 相关阅读:
    word文档的图片怎么保存到xhEditor上
    word文档的图片怎么保存到CuteEditor上
    word文档的图片怎么保存到TinyMCE上
    word文档的图片怎么保存到eWebEditor上
    word文档的图片怎么保存到wangEditor上
    ASP.NET如何上传大文件
    JavaScript如何上传大文件
    JS如何上传大文件
    Java如何上传大文件
    JSP如何上传大文件
  • 原文地址:https://www.cnblogs.com/YiYA-blog/p/10657012.html
Copyright © 2020-2023  润新知