📄 08章 运算符重载.txt
字号:
183 // print integersl size and contents
184 cout << "Size of array integers1 is"
185 << integers1.getSize()
186 << "\nArray after initialization:\n"
187 << integersl << '\n';
188
189 // print integers2 size and contents
190 cout << "Size of array integers2 is "
191 << integers2.getSize()
192 << "\nArray after initialization:\n"
193 << integers2 << '\n';
194
195 // input and print integersl and integers2
196 cout << "Input 17 integers:\n";
197 cin >> integers1 >> integers2;
198 cout << "After input, the arrays contain:\n"
199 << "integersl:\n" << infegers1
200 << "integers2:\n" << integers2 << '\n';
201
202 // use overloaded inequality (!=) operator
203 cout << "Evaluating: integers1 != integers2\n";
204 if ( integers1 != integers2 )
205 cout << "They are not equal\n";
206
207 // create array integers3 using integers1 as an
208 // initlizer; print size and contente
209 Array integers3( integers1 );
210
211 cout << "\nSize of array integers3 is"
212 << integers3.getSize()
213 << "\nArray after initialization:\n"
214 << integers3 << '\n';
215
216 // use overloaded assignment (=) operator
217 cout << "Assigning integers2 to integers1:\n";
218 integers1 = integers2;
219 cout << "integersl:\n" << integers1
220 << "integers2:\n" << integers2 << '\n';
221
222 // use overloaded equality (==) operator
223 cout << "Evaluating: integers1 == integers2\n";
224 if ( integers1 == integers2 )
225 cout << "They are equal\n\n";
226
227 // use overloaded subscript operator to create rvalue
228 cout << "integers1[ 5 ] is "<< integers1[ 5 ] << '\n';
229
230 // use overloaded subscript operator to create lvalue
231 cout << "Assigning 1000 to integers1[ 5 ]\ n";
232 << integers1[ 5 ] = 1000;
233 cout << "integers1:\n" << integers1 << '\n';
234
235 // attempt to use out of range subscript
236
237 integers1[ 15 ] = 1000; // ERROR: out of range
238
239 return 0;
240 }
输出结果:
# of arrays instantiated = 0
# of arrays instantiated 2
Size of array integersl is 7
Array after initialization:
0 0 0 0
0 0 0 0
Size of array integers2 is 10
Array after initialization:
0 0 0 0
0 0 0 0
0 0
Input 17 integers:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
After input, the arrays contain:
integersl:
1 2 3 4
5 6 7
integers2:
8 9 10 11
12 13 14 15
16 17
Evaluating:integers1 != integers2
They are not equal
Size of array integers3 is 7
Array after initialization:
1 2 3 4
5 6 7
Assigning integers2 to inteqersl:
integersl:
8 9 10 11
12 13 14 15
16 17
integers2:
8 9 10 11
12 13 14 15
16 17
Evaluating: integersl == integers2
They are equal
integersl[ 5] is = 13
Assigning 1000 to integersl[ 5 ]
integersl:
8 9 10 11
12 1000 14 15
16 17
Attempt to assign 1000 to integersl[15]
Assertion failed: 0 <= subscript && subscript < size,
file Arrayl.cpp, line 87
abnormal program termination
图 8.4 用重载运算符演示Array类
类Array的static类变量arrayCount包含了程序执行过程中实例化的Arrayy对象的个数,该值由第176行的static成员函数getArrayCount返回。程序实例化子类Array的两个对象(第179行),对象integees1有7个元素.对象integers2有10个元素(默认的元素个数由类Array的构造函数指定)。
第181行再次调用getArrayCount,取得类变量arrayCount的值。第184到第187行用成员函数getSize确定arrayintegers1的长度,并使用Array重载流插入运算符输出integer1,以证实构造函数正确地初始化了数组的元素。接下来,第190到第193行的程序先输出数组integers2的长度,然后用重载的流插入运算符输出integer2数组。
完成上述工作后,程序提示用户输入17个整数,Array重载流读取运算符并使用下列语句(第197行):
cin >> integers1 >> integers2;
把这些值读入到两个数组中,前7个整数保存在integers1中,其余的则保存在intergers2中。为了证实输入操作的正确性,程序用流插入运算符输出了这两个数组(第198到第200行)。
接下来,程序通过测试条件(第204行):
integers1 != integers2
来验证重载的不相等运算符!=,输出结果表明这两个数组确实不相等。
第209行程序实例化第三个数组integers3并用数组integers1对其初始化,这将调用Array复制构造函数将integers1复制到integers3中。我们将在后面详细讨论复制构造函数。
程序在第211到第214行输出integers3的长度,并使用Array重载流插入运算符输出integers3,以证实构造函数正确地初始化数组。
接下来,第218行通过下列语句测试重载的赋值运算符(=):
integers1 = integers2;
然后打印出这两个数组来验证赋值的正确性。原来的integere1中只有7个整数,现在必须要使其能容纳integers2中的10个元素的副本。重载的赋值运算符可以改变原先的integers1的大小并复制integer2中的元素。
接下来,第224行用重载的运算符==;测试赋值后integers1和integers2是否相等。
接下来,第228行用重载的下标运算符引用integers1[5](integers1数组范围内的一个元素),这个带下标的数组名作为右值来打印integersl[5]的值,第232行将integers[5]放在赋值语句左边并赋给其一个新值1000,注意,operator[]返回引用并作为左值使用(在确定了5是在integers1的长度范围内)。
第237行程序试图将1000赋给integers[15](越界的元素)。Array重载了[]运算符捕获到该错误并中止程序。
有意思的是,数组下标运算符不仅仅可用于数组,还可以用于从其他各种容器类(如链表、字符串、字典等等)中选择元素。此外,下标不仅仅是整数,还可以是字符、字符串、浮点数甚至是用户自定义的对象。
上面介绍了程序是如何执行的。下面再分析—下类的首部和成员函数的定义。第29行到第3l行
int size;// size Of the array
int *ptr;// pointer to first element of array
static int arrayCount;// # of Arrays instantiated
是类的private数据成员,包括一个int类型指针ptr(指向Array对象中存储整型的动态分配数组)、一个表示数组元素个数的size成员以及一个表示已经实例化的数组对象数目的static成员arrayCount。
第9行和第10行:
friend ostream &operator<<(ostream &,const Array &);
friend istream &operator<<(istream &,Array &);
声明了重载的流插入、流读取运算符是类Array的友元。当编译器遇到表达式:
cout << arrayObject
通过生成operator<<(cout,arrayObject)来调用函数operator<(ostream &, constArray &)。当编译器遇到表达式:
cin >> arrayObject
通过生成operator>>(cin,arrayObjeet)来调用函数operatpr>>(istream &, Array &)。
因为Array对象总是在流插入运算符和流读取运算符的右边,所以这两个运算符函数不能是Array的成员函数。如果这些运算符函数是Array的成员函数,则可以用下列的语句(可能会出现意外情况)输入和输出Array:
arrayObject << cout;
arrayObject >> cin;
函数operator<<(在第151行定义)打印由size指定的存储在ptr中的数组元素的个数,而函数operator>>(在第142行定义)则把数据直接输入到ptr所指向的数组中。为了能够分别实现连续的输入输出,这两个运算符都返回了一个合适的引用。
代码行:
Array(int= 1O); // default Constructor
声明了类的默认构造函数,并且指定数组元素的默认大小为10。当编译器遇到如下声明:
Array integers1(7);
或与之等价的形式:
Array integers1 = 7;
编译器将调用默认构造函数(本例中默认构造函数实际上接收一个int参数,默认值为10)。默认构造函数(第47行定义)验证参数并赋值给Size数据成员,用new分配数组所需的空间,将new返回的指针赋给数据成员ptr,然后用assert测试new操作是否成功,并递增arrayCount的值,最后用for循环将数组的所有元素初始化为0。如果没有将Array初始化,也可以在以后读取相应的值,但这样做会降低程序的可执行性。Array和任何对象都应随时保持正确初始化和一致的状态。
第13行:
Array(const Array &); // copy Constructor
是一个复制构造函数(第60行定义),它通过建立现有Array对象的副本来初始化Array对象。必须要小心对待这种复制操作,避免两个Array对象指向同一块动态分配的存储区,默认的成员复制更容易发生这种问题。不论何时需要复制对象时都会调用复制构造函数,如在传值调用时、从被调用函数返回一个对象时、或把某个对象初始化为同类的另外一个对象的副本时。当声明创建类Array的一个对象并用另外一个对象对它初始化时,调用复制构造函数。例如下列声明:
Array integers3(integers1);
或者与之等价的声明:
Array integers3 = integersl;
常见程错误8.6
注意复制构造函数要按引用调用,而不是按值调用,否则复制构追函数调用会造成无穷递归(这是个致命逻辑错误),因为对于按值调用,建立传入复制构造函数的对象副本会造成复制构造函数的递归调用。
复制构造函数Array使用成员初始化值将数组的size值复制到新数组的数据成员size中,用new分配新数组所需的空间,把new返回的指针赋给数据成员Ptr,然后用assert测试new操作是否成功,并递增arrayCount的值,最后用for循环将数组的所有元素作为初始值复制到新数组中。
常见编程错误8. 7
如果构造函数简单地将源对象的指针复制到目标对象的指针,则这两个对象将指向同一块动态分配的内
存块,执行析构函数时将释放该内存决,从而导致另外一个对象的Ptr没有定义,这种情况可能令引起严重的
运行时错误。
软件工程视点8.4
通常要把构造函数、析构函数、重载的赋值运算符以及复制构这造函数一起提供给使用动态内存分配的类。
第14行:
~Array(); // destructor
声明了类的析构函数(第71行定义)。当撤消类Array的某个对象时,自动调用析构函数。析构函数用delete[]释放在构造函数中用new动态分配的内存块,然后递减arraycount的值。
第15行:
int getSize() const; // return size
声明了读取数组大小的函数。
第16行:
const Array &operator= ( const Array &); // assign arrays
声明了重载的赋值运算符函数。当编译器遇到表达式:
integers1 = integers2;
就会通过产生如下代码调用函数operator=:
integers1.operator=(integers2)
成员函数operator=(第82行定义)测试了这种赋值是否是自我赋值。如果是,则跳过赋值操作(即对象已经是其自身,无需再赋值)。如果不是,则成员函数确定两个数组长度是否相同,如果是,则左边Array对象的原始整数数组不重新分配。否则成员函数operator=用delete释放目标数组原先动态分配的空间,将源数组的数据成员size复制到目标数组的size,用new分配目标数组所需的空间并将new返回的指针赋给数组的Ptr成员,用assert测试new操作是否成功,最后再用for循环将源数组的每一个元素复制到目标数组中。不管这种操作是否是自我赋值,成员函数都返回当前对象
(即*this),这种处理方式允许诸如x=y=z这样的连续赋值。
常见编程错误8.8
类的对象包含指向动态分配的内存的指针,但如果没有为它提供重载的赋值运算符和复制构造函数则会
造成逻辑错误。
软件工程视点8. 5
把赋值运算符定义为类的private成员可以防止将一个类对象赋给另外一个类对象。
软件工程视点8.6
只要重载的赋值运算符和复制构造函数为private,就可以防止复制类对象。
第17行:
bool operator=(const Array &)const; // compare equal声明了重载的相等运算符。当编译器遇到malll函数中的如下表达式时:
integers1 == integeis2
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -