• C++ Primer Plus 笔记第十二章


    类和动态内存分配

    本章内容包括:

       对类成员使用动态内存分配

       隐式和显示复制构造函数

       隐式和显示重载赋值运算符

       在构造函数中使用 new 所必须完成的工作

       使用静态类成员

       将定位 new 运算符用于对象

       使用指向对象的指针

       实现队列抽象数据类型(ADT)

    12.1 动态内存和类

       C++ 让程序在运行时决定内存分配,而不是在编译时决定;

       C++ 使用 new 和 delete 运算符来动态控制内存;

       遗憾的是,使用 new 和 delete 运算符将导致许多新的编程问题:

        析构函数将是必不可少的;

        有时要重载赋值运算符

    12.1.1 复习示例和静态类成员

       使用 char 指针(而不是 char 数组)来表示姓名:

        类声明没有为字符串本身分配存储空间,而是在构造函数中使用 new 来为字符串分配空间;

        避免了在类声明中预先定义字符串长度

       静态类成员有一个特点: 无论创建了多少对象,程序都只创建一个静态变量副本;

       注意: 静态数据成员在类声明中声明,在包含类方法文件中初始化;

           初始化时使用作用域运算符来指出静态成员所属的类;

           如果静态成员是整型或者枚举型 const,则可以在类声明中初始化

       警告: 在构造函数中使用 new 来分配内存时,必须在相应的析构函数中使用 delete 来释放内存;

              使用 new [ ] 来分配内存,应该使用 delete [ ] 来释放内存

    12.1.2 特殊成员函数

       C++ 自动提供的成员函数:

        默认构造函数,如果没有定义构造函数;

        默认析构函数,如果没有定义;

        复制构造函数,如果没有定义;

        赋值运算符,如果没有定义;

        地址运算符,如果没有定义

        默认构造函数不完成任何工作,但使得能够声明数组和未初始化的对象;

        默认赋值构造函数和默认赋值运算符使用成员赋值;

        默认析构函数不完成任何工作;

        隐式地址运算符返回调用对象的地址(即this指针 )

       StringBad 类中的问题由 隐式复制构造函数 和 隐式赋值运算符 引起的;

       1. 默认构造函数

        如果没有提供任何构造函数,C++ 将创建默认的构造函数;

        如果定义了构造函数,C++ 将不会定义默认构造函数,如果希望创建对象时不显示的对其进行初始化,则要显示的定义默认构造函数;

        只能有一个默认构造函数,编译器不能处理二义性

       2. 复制构造函数

        复制构造函数用于将一个对象复制到新创建的对象中;

        复制构造函数用于初始化过程中,而不是赋值过程中;

        类的复制构造函数原型:

          Class_name ( const Class_name & );

       3. 何时调用复制构造函数

        新建一个对象并将其初始化为同类现有对象时,复制构造函数都将被调用:

          StringBad ditto ( motto );       // calls StringBad ( const StringBad & )

          StringBad metoo = motto;          // calls StringBad ( const StringBad & )

          StringBad also = StringBad( motto );  // calls StringBad ( const StringBad & )

          StringBad * pStringBad = new StringBad ( motto );   // calls StringBad ( const StringBad & )

        每当程序生成了对象副本时,编译器都将使用复制构造函数:

          函数按值传递对象;

          函数返回对象;

          编译器生成临时对象

       4. 默认的复制构造函数的功能

        默认的复制构造函数逐个复制非静态成员(成员复制也称为浅复制);

        如果成员本身就是类对象,则将使用这个类的复制构造函数来复制成员对象;

        静态函数不受影响,因为它们不属于整个类

    12.1.3 回到StringBad: 复制构造函数的哪里出现了问题

       问题一: 程序使用默认的复制构造函数创建对象,而没有进行计数更新

       问题二: 隐式复制构造函数是按值进行复制的,没有生成新的内存空间

       1. 定义一个显示复制构造函数以解决问题

        解决类设计中这种问题方法是进行深度复制(deep copy):

          复制构造函数应当复制字符串并将副本的地址赋给 str 成员,调用析构函数将释放不同的字符串;

        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 ";

        }

        必须定义复制构造函数的原因,一些类成员是使用 new 初始化的,指向数据的指针而不是数据本身;

       警告: 如果类中包含了使用 new 初始化的指针成员,应当定义一个复制构造函数,以指向的数据而不是指针,被称为深度复制

           复制的另一种形式(成员复制或浅复制)只是复制指针值,浅复制不会深入“挖掘”以复制指针引用的结构

     12.1.4 StringBad 的其他问题: 赋值运算符

       C++ 允许类对象赋值, 这是通过自动为类重载赋值运算符实现的,原型如下:

        Class_name & Class_name :: operator = ( const Class_name & );

         接受并返回一个指向类对象的引用

       1. 赋值运算符的功能以及何时使用它

        将已有的对象赋给另一个对象时,将使用重载的赋值运算符

        初始化对象时,并不一定会使用赋值运算符,使用复制构造函数

        StringBad metoo = knot; 实现时可能分两步来处理这条语句:

          使用复制构造函数创建一个临时对象;

          通过赋值将临时对象的值复制到新对象中

       解决赋值的问题

        StringBad & StringBad :: operator = ( const StringBad & st )

        {

          if( this == &st )

            return *this;

          delete [ ] str;

          len = st.len;

          str = new char [len + 1];

          std :: strcpy( str, st.str );

          return *this; 

        }

    12.2 改进后的新 String 类

       添加一些说明 String 类工作原理的方法:

        int length() const {return len;}

        friend bool operator < ( const String &st1, const String &st2 );

        friend bool operator > ( const String &st1, const String &st2 );

        friend bool operator == ( const String &st1, const String &st2 );

        firend operator >> (istream & is, String & st);

        char & operator [ ] (int i);

        const char & operator [ ] (int i) const;

        static int HowMany();

       C++ 11 引入新关键字 nullptr,用于表示空指针

    12.2.2 比较成员函数

       比较操作符重载应用接受两个参数的标准 strcmp() 函数:

        如果第一个参数位于第二个参数之前,返回一个负值;

        如果第一个参数位于第二个参数之后,返回一个正值;

        如果两个参数相等,返回0;

    12.2.3 使用括号表示法访问字符

       C++ 中,两个中括号组成一个运算符——中括号运算符,可以使用方法 operator [ ] ( );

       中括号运算符,一个操作数位于第一个中括号的前面,另一个操作数了位于两个中括号之间; // city[0]

       operator 是一个 String 对象,对于 operator [4]:

        C++查找名称和特征标于此相同的方法: String :: operator [ ] ( int i );

        将 opera[4] 替换为 opera.operator[ ] (4);

        opera 对象调用该方法,数组下标成为该函数的参数                                

    12.2.4 静态类成员函数

       可以将成员函数声明为静态的:

        不能通过对象调用静态成员函数,如果静态成员函数实在公有部分声明的,则可以使用类名和作用域解析运算符调用;

        静态成员函数不与特定的对象相关联,因此只能使用静态数据成员

    12.2.5 进一步重载赋值运算符

       提高将常规字符串复制到 String 对象的效率,最简单的方法是重载赋值运算符,使之能够使用常规字符串

    程序清单 12.4  string1.h

     1 #ifndef STRING1_H_
     2 #define STRING1_H_
     3 #include<iostream>
     4 using std::ostream;
     5 using std::istream;
     6 
     7 class String
     8 {
     9 private:
    10     char *str;
    11     int len;
    12     static int num_strings;
    13     static const int CINLIM = 80;   // cin input limit
    14 public:
    15     // 构造函数与其他方法
    16     String(const char * s);  // 构造函数
    17     String();                 // 默认构造函数 
    18     String(const String &);    // 复制构造函数
    19     ~String();                 // 析构函数
    20     int length() const { return len; }
    21 
    22     // 重载操作符方法
    23     String & operator = (const String &);
    24     String & operator = (const char *);
    25     char & operator[](int i);
    26     const char & operator[](int i) const;
    27 
    28     // 重载操作符的友元
    29     friend bool operator < (const String & st1, const String & st2);
    30     friend bool operator > (const String & st1, const String & st2);
    31     friend bool operator == (const String & st1, const String & st2);
    32     friend ostream & operator << (ostream & os, const String & st);
    33     friend istream & operator >> (istream & is, String & st);
    34 
    35     // 静态成员函数
    36     static int HowMany();
    37 };
    38 
    39 #endif 
    View Code

    程序清单 12.5 string1.cpp

      1 #pragma warning(disable:4996)
      2 #include<cstring>
      3 #include"string1.h"
      4 using std::cin;
      5 using std::cout;
      6 
      7 // 初始化静态类成员
      8 int String::num_strings = 0;
      9 
     10 // 静态方法
     11 int String::HowMany()
     12 {
     13     return num_strings;
     14 }
     15 
     16 // 类方法****************************************************
     17 
     18 // 构造函数传入C标准字符串参数
     19 String::String(const char * s)
     20 {
     21     len = std::strlen(s);
     22     str = new char[len + 1];
     23     std::strcpy(str, s);
     24     num_strings++;
     25 }
     26 
     27 // 默认构造函数
     28 String::String()
     29 {
     30     len = 4;
     31     str = new char[1];
     32     str[0] = '';
     33     num_strings++;
     34 }
     35 
     36 // 复制构造函数
     37 String::String(const String & st)
     38 {
     39     num_strings++;
     40     len = st.len;
     41     str = new char[len + 1];
     42     std::strcpy(str, st.str);
     43 }
     44 
     45 // 析构函数
     46 String::~String()
     47 {
     48     --num_strings;
     49     delete[] str;
     50 }
     51 
     52 
     53 
     54 // 重载操作符方法*****************************************
     55 
     56 // 从 String 类到 String 类赋值
     57 String & String::operator = (const String & st)
     58 {
     59     if (this == &st)
     60         return *this;
     61     delete[] str;
     62     len = st.len;
     63     str = new char[len + 1];
     64     std::strcpy(str, st.str);
     65     return *this;
     66 }
     67 
     68 // 从 C字符串 到 String 类赋值
     69 String & String::operator = (const char * s)
     70 {
     71     delete[] str;
     72     len = std::strlen(s);
     73     str = new char[len + 1];
     74     std::strcpy(str, s);
     75     return *this;
     76 }
     77 
     78 // 从非 const String 类读写字符
     79 char & String::operator[] (int i)
     80 {
     81     return str[i];
     82 }
     83 
     84 // 从 const String 类读取字符
     85 const char & String::operator[] (int i) const
     86 {
     87     return str[i];
     88 }
     89 
     90 // 重载操作符友元
     91 bool operator < (const String &st1, const String &st2)
     92 {
     93     return (std::strcmp(st1.str, st2.str) < 0);
     94 }
     95  
     96 bool operator > (const String &st1, const String &st2)
     97 {
     98     return st2 < st1;
     99 }
    100 
    101 bool operator == (const String &st1, const String &st2)
    102 {
    103     return (std::strcmp(st1.str, st2.str) == 0);
    104 }
    105 
    106 // 字符串输出
    107 ostream & operator << (ostream & os, const String & st)
    108 {
    109     os << st.str;
    110     return os;
    111 }
    112 // 字符串快速并且直接输入
    113 istream & operator >> (istream & is, String & st)
    114 {
    115     char temp[String::CINLIM];
    116     is.get(temp, String::CINLIM);
    117     if (is)
    118         st = temp;
    119     while (is && is.get() != '
    ')
    120         continue;
    121     return is;
    122 }
    View Code

    程序清单 12.6 sayings1.cpp

     1 #include<iostream>
     2 #include"string1.h"
     3 const int ArSize = 10;
     4 const int MaxLen = 81;
     5 
     6 int main()
     7 {
     8     using std::cout;
     9     using std::cin;
    10     using std::endl;
    11     String name;
    12     cout << "Hi, What`s your name?
    >>";
    13     cin >> name;
    14 
    15     cout << name << ", please enter up to " << ArSize << " short sayings <empty line to quit>:
    ";
    16     String sayings[ArSize];     // 对象数组
    17     char temp[MaxLen];
    18 
    19     int i;
    20     for (i = 0; i < ArSize; i++)
    21     {
    22         cout << i + 1 << ": ";
    23         cin.get(temp, MaxLen);
    24         while (cin && cin.get() != '
    ')  
    25             continue;
    26         if (!cin || temp[0] == '')     // empty line?  后半部分用于旧版本的检测空行
    27             break;
    28         else
    29             sayings[i] = temp;
    30     }
    31     int total = i;    // total # of lines read
    32 
    33     if (total > 0)
    34     {
    35         cout << "Here are your sayings:
    ";
    36         for (i = 0; i < total; i++)
    37             cout << sayings[i][0] << ": " << sayings[i] << endl;
    38 
    39         int shorest = 0;
    40         int first = 0;
    41         for (i = 1; i < total; i++)
    42         {
    43             if (sayings[i].length() < sayings[shorest].length())
    44                 shorest = i;
    45             if (sayings[i] < sayings[first])
    46                 first = i;
    47         }
    48         cout << "Shortest saying:
    " << sayings[shorest] << endl;
    49         cout << "First alphabetically:
    " << sayings[first] << endl;
    50         cout << "This program used " << String::HowMany() << " String objects. Bye.
    ";
    51     }
    52     else
    53         cout << "No input! Bye.
    ";
    54 
    55     return 0;
    56 
    57 }
    View Code

    12.3 在构造函数中使用 new 时应注意的事项

       在构造函数中使用 new 来初始化指针成员,则应在析构函数使用 delete;

       new 和 delete 必须互相兼容。new 对应于 delete,new [ ] 对应于 delete [ ];

       只有一个析构函数,如果有多个构造函数,必须以相同的方式使用 new,所有的构造函数都必须与它兼容;

       delete (无论带不带 [ ])可以用于空指针(NULL、0、nullptr);

       定义一个复制构造函数,通过深度复制将对象初始化为另一个对象:

        String :: String ( const String & st )

        {

          num_string++;                    // 更新静态成员 

          len = st.len;         

          str = new char [ len + 1 ];   // 分配新地址空间

          std::strcpy(str,st.str);       // 把字符串复制到新的地址

        }

       定义一个赋值运算符,通过深度复制将一个对象复制给另一个对象:

        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;         // 返回一个指向调用对象的引用

        }

    12.3.2 包含类成员的类的逐成员复制

       对于包含类成员的类,类的类成员使用动态内存分配: 

        默认的逐成员复制和赋值行为有一定的智能;

        逐成员复制将使用成员类型定义的复制构造函数和赋值运算符

        如果类因其他成员需要定义复制构造函数和赋值运算符,这些函数必须显示地调用 String 和 string 的复制构造函数和赋值运算符

    12.4 有关返回对象的说明

       当成员函数或独立的函数返回对象时,有几种返回方式可供选择:

        指向对象的引用;

        指向对象的 const 引用;

        const 引用;

    12.4.1 返回指向 const 对象的引用

       使用 const 引用的常见原因是旨在提高效率:

        返回对象将调用复制构造函数,而返回引用不会;

        引用指向的对象应该在调用函数执行时存在;

        参数被声明为 const 引用,返回类型必须为 const;

    12.4.2 返回指向非 const 对象的引用

       operator = () 的返回值用于连续赋值:

        Sring s1 ( "Good stuff" );

        String s2, s3;

        s3 = s2 = s1;

        返回类型不是 const 因为方法 operator = () 返回一个指向 s2 的引用,可以对其进行修改。返回引用可以不调用复制构造函数

       operator << ( ) 的返回值用于串接输出:

        String s1( "Good stuff" );

        cout << s1 << "is coming!";

        operator << ( cout, s1 ) 的返回值成为一个用于显示字符串 " is coming! "的对象;

        返回类型必须为 ostream &,而不能是 ostream,因为 ostream 没有公有的复制构造函数

    12.4.3 返回对象

       如果返回的对象是调用函数中的局部变量,不能按引用返回它,被调用函数执行完毕后,局部对象将调用其析构函数,引用指向的对象不再存在;

       返回对象而不是引用,将调用复制构造函数

    12.4.4 返回方式总结

       如果方法或函数要返回局部对象,则应返回对象,而不是指向对象的引用,将使用复制构造函数来生成返回的对象;

       如果方法或函数返回一个没有公有复制构造函数的类的对象,它必须返回这种对象的引用;

       有些方法和函数(如重载的赋值运算符)可以返回对象,也可以返回指向对象的引用,首选引用,因为效率高

    12.5 使用指向对象的指针

       C++ 经常使用指向对象的指针;

       程序清单12.7 sayings2.cpp

     1 #include<iostream>
     2 #include<cstdlib>
     3 #include<ctime>
     4 #include"string1.h"
     5 
     6 const int ArSize = 10;
     7 const int MaxLen = 81;
     8 
     9 int main()
    10 {
    11     using namespace std;
    12     String name;
    13     cout << "Hi, what`s your name?
    ";
    14     cin >> name;
    15 
    16     cout << name << ", please enter up to " << ArSize << " short saying <empty line to quit>
    ";
    17     String sayings[ArSize];
    18     char temp[MaxLen];
    19     int i;
    20     for (i = 0; i < ArSize; i++)
    21     {
    22         cout << i + 1 << ": ";
    23         cin.get(temp, MaxLen);
    24         while (cin && cin.get() != '
    ')
    25             continue;
    26         if (!cin || temp[0] == '')
    27             break;
    28         else
    29             sayings[i] = temp;
    30     }
    31 
    32     int total = i;
    33     if (total > 0)
    34     {
    35         cout << "Here are your sayings:
    ";
    36         for (i = 0; i < total; i++)
    37             cout << sayings[i] << "
    ";
    38         String * shortest = &sayings[0];
    39         String * first = &sayings[0];
    40         for (i = 0; i < total; i++)
    41         {
    42             if (shortest->length() > sayings[i].length())
    43                 shortest = &sayings[i];
    44             if (*first > sayings[i])
    45                 first = &sayings[i];
    46         }
    47         cout << "Shortest saying:
    " << *shortest << endl;
    48         cout << "First saying:
    " << *first << endl;
    49         srand(time(0));
    50         int choice = rand() % total;  // pick index at random
    51         String * favorite = new String(sayings[choice]);
    52         cout << "My favorite sayings 
    " << *favorite << endl;
    53         delete favorite;
    54     }
    55     else
    56         cout << "Not much to say, eh?
    ";
    57     cout << "Bye.
    ";
    58 
    59     return 0;
    60 }
    View Code

       使用 new 初始化对象,如果 Class_name 是类, value 的类型为 Type_name:

        Class_name * pclass = new Class_name(value);

    12.5.1 再谈 new 和 delete

    12.5.2 指针和对象小结

       使用对象指针注意:

        使用常规表示法来声明指向对象的指针:

          String * glamour;

        可以将指针初始化为已有的对象:

          String * first = &sayings[0];

        可以使用 new 来初始化指针,这将创建一个新对象(使用构造函数):

          String * favorite = new String (sayings[choice]);

          String * gleep = new String            // 使用默认构造函数

          String * glop = new String ( "my my my " )         // 使用构造函数 String ( const char * ) 

          String * favorite = new String ( sayings[ choice ] )   // 使用复制构造函数 String ( const String & )

        可以使用 -> 运算符通过指针访问类方法:

          shortest -> length();

        可以对对象指针应用解除引用运算符 (*)来获得对象:

          *first

    12.5.3 再谈定位 new 运算符

    12.6 复习各种技术

    12.6.1 重载 << 运算符

       要重新定义 << 运算符,以便将它和 cout 一起用来显示对象的内容,定义下面友元运算符函数:

        ostream & operator << ( ostream & os, const c_name & obj )

        {

          os <<  .  .  . ;    // display object contents

          return os;

        }

        其中 c_name 为类名, 如果该类能够返回所需内容的公有方法,则可以在运算符中使用这些方法,这样便不用将它们设置为友元函数了

    12.6.2 转换函数

       要将单个值转换为类类型,需要定义类构造函数:

        c_name ( type_name value );   // c_name为类名, type_name是要转换的类型名称

       要将类转换为其他类型,需要创建类成员函数:

        operator type_name ( );       // type_name 为要转换的类型

       使用转换函数时要小心,可以在声明构造函数时使用关键字 explicit,防止被用于隐式转换

    12.6.3 其构造函数使用 new 的类

       如果使用 new 运算符来分配类成员指向的内存,在设计时应采取一些措施:

        对于指向的内存是由 new 分配的所有类成员,都应在析构函数中对其使用 delete,该运算符将释放分配的内存;

        如果析构函数通过对指针类成员使用 delete 来释放内存,则每个构造函数都应当使用 new 来初始化指针,或设置为空指针;

        构造函数使用 new[ ] 或 new,不能混用;

        new [ ] 对应于 delete [ ] , new 对应于 delete;

        应定义一个分配内存(而不是使用指针指向已有内存)的复制构造函数。这样程序能将类对象初始化为另一个类对象;

        应定义一个重载赋值运算符的类成员函数,这样程序能将类对象赋值给另一个类对象

    12.7 队列模型     

        队列特征:

        队列存储有序的项目序列;

        队列所能容纳的项目数有一定的限制;

        应当能够创建空队列;

        应当能够检查队列是否为空;

        应当能够检查队列是否为满;

        应当能够在队尾添加项目;

        应当能够在队首删除项目;

        应当能够确定队列中的项目数目

       1. Queue类的接口

       2. Queue类的实现

        确定队列的数据结构——链表:

          struct Node

          {

            Item item;

            struct Node * next;

          }

        嵌套结构和类:

          在类声明中声明的结构、类或枚举被称为是被嵌套在类中,其作用域为整个类;

       3. 类方法

        类构造函数应提供类成员的值,对于非静态 const 数据成员,必须在执行到构造函数体之前,创建对象时进行初始化;

        C++ 提供: 成员初始化列表:

          成员初始化列表有逗号分隔的初始化列表组成(前面带冒号);

          通常,初始值可以是常量或构造函数的参数列表中的参数,不限于初始化常量;

          只有构造函数可以使用这种初始化列表语法;

          引用于 const 数据类似,只能在被创建时进行初始化

           对于简单数据成员,使用成员初始化列表和在函数体中使用赋值没有什么区别

        注意:

          这种格式只能用于构造函数;

          必须使用这种格式来初始化非静态 const 数据成员;

          必须使用这种格式来初始化引用数据成员;

          数据成员被初始化的顺序与它们出现在类声明中的顺序相同,与初始化器中的顺序无关

        警告: 不能将成员初始化列表语法用于构造函数之外的其他类方法

        成员初始化列表使用的括号的方式也可用于常规初始化:

          int games = 162;

          double talk = 2.71828;

         替换为:

          int games(162);

          double talk(2.71828);

          这使得初始化内置类型就像初始化类对象一样

        C++ 11允许类内初始化:

          class Classy

          {

            int mem1 = 10;

            const int mem2 = 20;

            .  .  .

          }

       禁止复制构造函数和赋值运算符的方法:

        1. 将所需的方法定义为伪私有方法:

          class Queue

          {

          private:

            Queue ( const Queue & q ): qsize(0)  { }

            Queue & operator = ( const Queue & q )  { return *this; }

          }    

          避免了本来将自动生成的默认方法定义;

          方法是私有的,所以不能被广泛使用

        2. C++ 11提供了另一种禁用方法的方式——使用关键字 delete

    12.7.2 Customer类

       程序清单12.10  queue.h

     1 #ifndef QUEUE_H_
     2 #define QUEUE_H_
     3 
     4 class Customer
     5 {
     6 private:
     7     long arrive;
     8     int processtime;
     9 public:
    10     Customer() { arrive = processtime = 0; }
    11     void set(long when);
    12     long when() const { return arrive; }
    13     int ptime() const { return processtime; }
    14 };
    15 
    16 typedef Customer Item;
    17 
    18 class Queue
    19 {
    20 private:
    21     struct Node { Item item; struct Node * next; };
    22     enum {Q_SIZE = 10};
    23     Node * front;
    24     Node * rear;
    25     int items;
    26     const int qsize;
    27     Queue(const Queue & q) : qsize(0) {}
    28     Queue & operator =(const Queue & q) { return *this; }
    29 public:
    30     Queue(int qs = Q_SIZE);
    31     ~Queue();
    32     bool isempty() const;
    33     bool isfull() const;
    34     int queuecount() const;
    35     bool enqueue(const Item & item);
    36     bool dequeue(Item & item);
    37 };
    38 #endif
    View Code

       程序清单12.11  queue.cpp

     1 #include"queue.h"
     2 #include<cstdlib>
     3 
     4 // Queue methods
     5 Queue::Queue(int qs) : qsize(qs)
     6 {
     7     front = rear = NULL;
     8     items = 0;
     9 }
    10 
    11 Queue::~Queue()
    12 {
    13     Node * temp;
    14     while (front != NULL)
    15     {
    16         temp = front;
    17         front = front->next;
    18         delete temp;
    19     }
    20 }
    21 
    22 bool Queue::isempty() const
    23 {
    24     return items == 0;
    25 }
    26 
    27 bool Queue::isfull() const
    28 {
    29     return items == qsize;
    30 }
    31 
    32 int Queue::queuecount() const
    33 {
    34     return items;
    35 }
    36 
    37 // add item to queue
    38 bool Queue::enqueue(const Item & item)
    39 {
    40     if (isfull())
    41         return false;
    42     Node * add = new Node;
    43     add->item = item;
    44     add->next = nullptr;
    45     items++;
    46     if (front == NULL)
    47         front == add;
    48     else
    49         rear->next = add;
    50     rear = add;
    51     return true;
    52 }
    53 // Place front item into item variable and remove from queue
    54 bool Queue::dequeue(Item & item)
    55 {
    56     if (isempty())
    57         return false;
    58     item = front->item;
    59     items--;
    60     Node * temp = front;
    61     front = front->next;
    62     delete temp;
    63     if (items == 0)
    64         rear = nullptr;
    65     return true;
    66 }
    67 
    68 // time set to a random value in the range 1 - 3
    69 void Customer::set(long when)
    70 {
    71     processtime = std::rand() % 3 + 1;
    72     arrive = when;
    73 }
    View Code

     12.8 总结

       在类构造函数中,可以使用 new 为数据分配内存,然后将内存地址赋给类成员,这样便可以处理长度不同的字符串;

       在类构造函数中使用 new 如果对象包含成员指针,指向的内存由 new 分配:

        释放保存对象的内存并不会自动释放成员指针指向的内存;

        应在类析构函数中使用 delete 来释放分配的内存

       如果对象包含指向 new 分配的内存的指针成员,则将对象初始化为另一个对象,或将一个对象赋值给另一个对象会出现问题:

        浅复制, 没有创建指针指向内容的副本

         解决方法:定义一个特殊的复制构造函数来重新定义初始化,并重载赋值运算符:

          新的定义都将创建指向数据的副本,并使新对象指向这些副本,旧对象与新对象都将引用独立的相同的数据

       对象的存储持续性为自动或外部时,在它不再存在时将自动调用其析构函数;

       如果使用 new 运算符为对象分配内存,并将地址赋给一个指针,则当使用 delete 时自动为对象调用析构函数;

       如果使用定位 new 运算符为类对象分配内存,则必须显示的为该对象调用析构函数:

        方法是使用指向该对象的指针调用析构函数的方法

       C++ 允许在类中包含结构、类和枚举定义,这些嵌套类型的作用域为整个类;

       C++ 为类构造函数提供了一种可用来初始化数据成员的特殊语法:成员初始化列表

        如果数据是非静态 const 成员或引用,则必须采用这种格式

        C++11 允许类内初始化,与成员初始化列表等价,使用成员初始化列表的构造函数将覆盖相应的类内初始化

     

  • 相关阅读:
    js给redio设置哪一个被选中
    问候struts2升级的新版本2.5
    图片上传与显示时候的路径问题
    Json,String,Map之间的转换
    springboot之web
    关于struts2中出现的漏洞,如何测试解决
    Table-valued functions and Scalar-valued functions in SQL Server
    Using system view: sys.sysprocesses to check SqlServer's block and deadlock
    利用sys.sysprocesses检查SqlServer的阻塞和死锁
    SQLSERVER加密解密函数(非对称密钥 证书加密 对称密钥)
  • 原文地址:https://www.cnblogs.com/kidycharon/p/9857325.html
Copyright © 2020-2023  润新知