类和动态内存分配
作者:mmseoamin日期:2023-12-21

.

7. 解决赋值的问题

对于由于默认赋值操作符不合适而导致的问题,解决办法是提供赋值操作符(进行深度复制)定义。

其实现与复制构造函数相似,但也有一些差别。

● 由于目标对象可能引用了以前分配的数据,所以函数应使用delete[]来释放这些数据。

函数应当避免将对象赋给自身;否则,给对象重新赋值之前,释放内存操作可能删除对象的内容。

● 函数返回一个指向调用对象的引用。

通过返回一个对象,函数可以像常规赋值操作那样,连续进行赋值,即如果S0、S1和S2都是StringBad

对象,则可以编写这样的代码:

S0 = S1 = S2:

使用函数表示法时,上述代码为:

S0.operator= (S1.operator= (s2) ) :

因此,S1.operator=(S2)的返回值是函数SO.operator=()的参数。

因为返回值是一个指向StringBad对象的引用,因此参数类型是正确的。

下面的代码说明了如何为StringBad类编写赋值操作符:

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

if (this == &st)

return *this:

delete [] str:

len = st.len:

str = new char [len + 1]:

// object assigned to itself

// all done

// free old string

// get space for new string

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

return *this:

第12章 类和动态内存分配

// copy the string

// return reference to invoking object

代码首先检查自我复制,这是通过查看赋值操作符右边的地址(&s)是否与接收对象(this)的地址

相同来完成的。如果相同,程序将返回*this,然后结束。第10章介绍过,赋值操作符是只能由类成员函数

重载的操作符之一。

如果地址不同,函数将释放str指向的内存,这是因为稍后将把一个新字符串的地址赋给str。如果不

首先使用delete操作符,则上述字符串将保留在内存中。由于程序中不再包含指向该字符串的指针,因此

这些内存被浪费掉。

接卜来的操作与复制构造函数相似,即为新字符串分配足够的内存空间,然后将赋值操作符右边的对

象中的字符串复制到新的内存单元中。

上述操作完成后,程序返回*this并结束。

赋值操作并不创建新的对象,因此不需要调整静态数据成员 num_strings的值。

将前面介绍的复制构造函数和赋值操作符添加到StringBad类中后,所有的问题都解决了。例如,下

面是在完成上述修改后,程序输出的最后几行:

End of main()

"Celery Stalks at Midnight" object deleted, 4 left

"Spinach Leaves Bowl for Dollars" object deleted, 3 left

"Spinach Leaves Bowl for Dollars" object deleted, 2 left

"Lettuce Prey" object deleted. 1 left

"Celery Stalks at Midnight" object deleted, 0 left

现在,对象计数是正确的,字符串也没有被损坏。

12.1.3 改进后的新String类

有了更丰富的知识后,可以对StringBad类进行修订,将它重命名为String。首先,添加前面介绍过的

复制构造函数和赋值操作符,使类能够正确管理类对象使用的内存。其次,由于我们已经知道对象何时被

创建和释放,因此可以让类构造函数和析构函数保持沉默,不再在每次被调用时都显示消息。另外,也不

用再监视构造函数的工作情况,因此可以简化默认构造函数,使之创建一个空字符串,而不是“C++”。

接下来,可以在类中添加一些新功能。String类应该包含标准字符串函数库cstring的所有功能,才会

比较有用,但这里只添加足以说明其工作原理的功能(注意,String类只是一个用作说明的范例,而C++

标准string类的内容丰富得多)。具体地说,将添加以下方法:

int length()const { return len: }

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

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

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

friend operator>> (istream & is, String & st):

char & operator(] (int i):

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

static int HowMany ():

第…个新方法返回被存储的字符串的长度。接下来的3个友元函数能够对字符串进行比较。Operator>>()

函数提供了简单的输入功能:两个operator[](函数提供了以数组表示法访问字符串中各个字符的功能。静

态类方法 Howmany()将补充静态类数据成员num_string。下面来看一看具体情况。

1. 修订后的默认构造函数

需要注意的是新的默认构造函数,它与下面类似:

String: :String()

len = 0:

str = new char[1]:

str[0] = '\0':

-

// default string

390

C++Primer Plus(第五版)中文版

读者可能会问,为什么代码为:

str = new char[1]:

而不是:

str = new char:

上面两种方式分配的内存量相同,区别在于前者与类析构函数兼容,而后者不兼容。析构函数中包含

如下代码:

delete [] str:

delete[]与使用 new[]初始化的指针和空指针都兼容。因此对于下述代码:

str = new char[1]:

str{0] = '\0';

可修改为:

str - 0: // sets str to the null pointer

对于以其他方式初始化的指针,使用delete[]时,结果将是不确定的:

char words [15] = "bad idea";

char * pl= words:

char * p2 = new char;

char * p3:

delete [] pl: // undefined. so don't do it

delete [] p2: // undefined, so don't do it

delete [] p3: // undefined, so don't do it

// default string

2. 比较成员函数

在String类中,执行比较操作的方法有3个。如果按字母顺序(更准确地说,按照机器排序序列),第

一个字符串在第二个字符串之前,则Operator<()函数返回true。要实现字符串比较函数,最简单的方法是

使用标准的trcmp()函数,如果依照字母顺序,第一个参数位于第二个参数之前,则该函数返回一个负值;

如果两个字符串相同,则返回0;如果第一个参数位于第二个参数之后,则返回一个正值。因此,可以这

样使用 strcmp():

bool operator< (const String sst1, const String sst2)

if (std :: strcmp (stl.str, st2.str) > 0)

return true:

else

return false:

因为内置的>操作符返回的是一个布尔值,所以可以将代码进一步简化为:

bool operator< {const String sstl, const String sst2)

return (std :: stremp (stl.str. st2.str) < 0);

同样,可以按照下面的方式来编写另外两个比较函数:

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

return st2.str < stl.str:

bool operatorm= (const String &stl, const String &st2)

return (std :: stremp (stl.str. st2.str) == 0);

第一一个定义利用了<操作符来表示>操作符,对于内联函数,这是一种很好的选择。

将比较函数作为友元,有助于将String对象与常规的C字符串进行比较。例如,假设answer 是String

对象,则下面的代码:

if ("love" -- answer)

将被转换为:

-

第12章 类和动态内存分配

391

if loperator == ("love". answer) )

然后,编译器将使用某个构造函数将代码转换为:

if (operator == (String ("love") , answer) )

这与原型是相匹配的。