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

    }

    必须定义复制构造函数的原因,一些类成员是使用 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] = '\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() != '\n')
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?\n>>";
13     cin >> name;
14 
15     cout << name << ", please enter up to " << ArSize << " short sayings <empty line to quit>:\n";
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() != '\n')  
25             continue;
26         if (!cin || temp[0] == '\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:\n";
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:\n" << sayings[shorest] << endl;
49         cout << "First alphabetically:\n" << sayings[first] << endl;
50         cout << "This program used " << String::HowMany() << " String objects. Bye.\n";
51     }
52     else
53         cout << "No input! Bye.\n";
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?\n";
14     cin >> name;
15 
16     cout << name << ", please enter up to " << ArSize << " short saying <empty line to quit>\n";
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() != '\n')
25             continue;
26         if (!cin || temp[0] == '\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:\n";
36         for (i = 0; i < total; i++)
37             cout << sayings[i] << "\n";
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:\n" << *shortest << endl;
48         cout << "First saying:\n" << *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 \n" << *favorite << endl;
53         delete favorite;
54     }
55     else
56         cout << "Not much to say, eh?\n";
57     cout << "Bye.\n";
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 允许类内初始化,与成员初始化列表等价,使用成员初始化列表的构造函数将覆盖相应的类内初始化

 

 

 

 

转载于:https://www.cnblogs.com/kidycharon/p/9857325.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值