




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第四章编译时多态性封装性是面向对象程序设计的基础,可以说没有封装性就没有面向对象的程序设计。封装性从根本上解 决了数据的安全性,为实现不同数据的操作同一性奠 定了基础,这种操作的同一性反映了客观世界中规范不同对象行为的需要,它了面向对象程序设计多态性的一部分。不过这种多态性必须预先确定操作所施加的数据类型,因此不是完备意义上的多态性,而 只是基于对象的多态性。这种不同数据的操作同一性 必须在程序编译时静态确定,故称为编译时多态性, 或称为静态多态性。实现这种静态多态性的途径是: 函数重载和运算符重载。4.1 函数重载函数重载为实现不同数据的操作同一性提供了根本 编程机制。函数重载的基本规则:
2、函数名必须相同:体现了操作同一性。 函数的参数必须不同:体现了同一操作的实现差异和所施加的数据差异。在面向对象的程序中函数重载表现在两个方面: 类外全局函数重载 类成员函数重载4.1.1 类外全局函数重载在面向对象程序设计中,全局函数常常用来作为类 的友元函数,因此全局函数重载可以用来实现不同类对象的同一类外操作。关于全局函数重载的方法已经在第二章中讲述,本章不再赘述。为了进一步理解函数重载是如何在编译时确定同一操作施加于不同类型的数据,我们模拟分析 C+ 编译器如何采用“名子压延”的方法实现重载函数的调用。所谓“名子压延”是指编译器编译重载函数调用时,先将原重载函数名和参数类型结合起来,以创
3、建新重 载函数名。然后用新重载函数名替代原重载函数名。例如,一个程序中有两个重载函数,原型为:int myAns(float x, int j); int myAns(int I, char c);编译时,编译器首先压延修改这两个重载函数名。修改后的新函数名或许会变成如下形式:int myAnsFLTINT(float x, int j); int myAnsINTCHAR(int i, char c);这样,就把原来无法区来的相同的函数名变成可以区分的不同的函数名。例如:调用 myAns(float x, int j) 时,则调用 myAnsFLTINT(float x, int j); 而
4、调用 myAns(int I, char c) 时,则调用 myAnsINTCHAR(int i, char c)。假如调用这两个函数的语句为:exam1 = myAns(15.3, 15); exam2 = myAns(45, 'a');用新函数名替代原函数名后,使得调用这两个函数的语句会发生如下相应的改变:exam1 = myAnsFLTINT(15.3, 15);exam2 = myAnsINTCHAR(45, 'a');4.1.2 类成员函数重载类成员函数重载分为两类:1 构造函数重载为类对象的创建和类对象的数据成员赋初值提供不 同的方法。重载构造函数的
5、参数必须不同。2 公有成员函数重载提供响应相同消息的不同接口方法。这类重载有两 种情况: 同一类中的重载成员函数的参数必须有差别。 派生类中分属于基类和派生类的重载成员函数的参数可以相同,但基类的重载成员函数被覆盖。 例如:class point int x, y;public:point(int a, int b) x = a; y = b; float area() return 0.0; ;class circle:public point int radius;public:circle(int x, int y, int rad):point(x, y) radius = rad;
6、float area()/ 重载成员函数return float(3.1416)*float(radius)*float(radius); ;main()point p(20, 20);circle c(8, 8, 30);cout << p.area() << endl; cout << c.area() << endl;cout << c.point:area() << endl;return 0;其中 area 是参数相同的重载成员函数。编译过程中区别这两个不同版本的 area 的情况有两种: 使用对象名只能分别调用
7、基类和派生类的参数相同的重载成员函数。例如:p.area() 将调用 point:area() 方法;c.area() 将调用 circle:area 方法。在对象名后添加 “基类名:”可以使用派生类对象名可以调用基类的参数相同的重载成员函数。例如:c.point:area() 将调用 point:area() 方法。4.2 运算符重载运算符可以视为是一种特殊的函数,它们可以用一 种简洁的,接近自然语言的表达式方式被调用。C+ 拥有一系列的预定义运算符,这些运算符能对所有的预定义类型的数据进行操作。也就是说,可以 使用预定义类型的数据作参数调用这些运算符函数。 例如,加运算符 "+&
8、quot;:int x, y; float e, f; y = x + y;f = e + f;这种对于不同类型的数据使用相同的表达式调用相同的运算符正是通过重载机制实现的。但这些运算符却 不能自动施加在自定义类型的对象。例如:自定义的 复数类 complex 的定义如下:class complexdouble real, imag; public:complex(double r = 0, double i = 0) real = r; imag = i; ;main()complexcom1(1.1, 2.2), com2(3.3, 4.4), total;total = com1 + c
9、om2; return 0;/ “+”运算符无法对复数进行操作很显然,因为运算符 “+”产生上述错误的操的作数据类型中不包括 complex 类型,所以编译器就不知解决的办法就是扩展 “+” 运算符操作的数据类“+”型,即为复数类complex 定义的运算符操作。C+ 的运算符重载方法为设计不同类对象的同一行为提供了非常有效和方便的。运算符重载是通过对运算符函数的重载实现的,运算符函数重载的原型和定义的一般形式如下:函数类型 operator (参数表列);/ 是运算符名的通配符号函数类型 operator (参数表列) 重载操作代码 例如,对复数类 complex 的 “+” 运算符重载:c
10、lass complex double real, imag;public:complex(double r = 0, double i = 0) real = r; imag = i; complex operator + (complex);complex complex:operator +(complex &c)complex sum;sum.real = real + c.real; sum.imag = imag + c.imag; return sum;4.2.1 重载运算符的规则 C+ 不定义新的运算符,只能对系统预定义运算符进行重载。 C+ 的预定义运算符中重载的包括
11、:算术运算符+、-、*、/、%关系运算符=、!=、<、>、<=、>=逻辑运算符|、&&、!单目运算符+、-、*、&自增自减运算符+、-位操作运算符|、&、<<、>>赋值运算符=、+=、-=、*=、/=、%=、&=、|=、=、<<=、>>=内存管理运算符new、delete、new、delete其他运算符()、->、 ->*、,、不重载预定义运算符的包括: 重载不能改变预定义运算符函数的参数(操作数)个数。双目运算符重载后仍为双目运算符,单目运 算符重载后仍为单目运算符。
12、重载不能改变预定义运算符的原有优先级。 重载不能改变预定义运算符的原有结合律。 重载运算符函数的参数不有缺省值。否则编译器会认为是改变了运算符函数的参数个数。成员运算符.成员指针运算符.*名域运算符:内存长度运算符sizeof条件运算符?: 重载的运算符必须与用户自定义类型的对象一起使用,因此重载运算符函数的参数中至少应该有一个是自定义类对象或类对象的。换句话说,重载运算符函数的参数不能全部是预定义类型对象,防止用户修改预定义运算符的性质。例如:int operator +(int a, int b) return a b; 显然,这是绝对不对于双目运算符,的。两个参数都是自定义类对象,例如两
13、个复数的加运算;也一个参数是自定义类对象,另一个参数是预定义类型对象,例如一个复数与一个实数的加运算。complex operator +(double d, complex& c) return complex(d + c.real, c.imag); 系统会为每个自定义类缺省重载了赋值运算符“=” 和取地址运算符“&”,其他运算符都需要根据需要进 行重载定义。其中: 赋值运算符“=”用于同类对象之间的数据成员赋值操作。一般情况下,缺省重载的赋值运算符“=”可以满足要求,但遇到类的数据成员中包含了动态 数据指针,则使用缺省重载的赋值运算符就可能 虽然可以任意定义重载运算符的操
14、作功能,但应该使重载运算符的功能类似于被重载运算符作用于预定义类型数据时的操作功能。 运算符重载函数可以是运算所施加类对象的成员函数,也可以是该类的友元函数,如果不需要类的私有数据成员,还可以是既非类成员函数也非友元函数的普通函数。在 Java 中虽然也不乏运算符重载的例子,例如:String str = "hello" + "there"该表达式相当于在 C+ 中的表达式:string str = "hello" + "there"上述表达式都是因为对 Java 的 String 类型和 C+ 的string 类
15、型的进行了“+” 运算符重载的结果。但要注意的是在 Java 中不用户进行运算符重载。4.3 使用成员函数重载运算符1 使用成员函数重载运算符的语法形式 在类定义体中要重载的运算符成员函数 type 运算符函数类型; operator 运算符函数名关键字; 要重载的运算符名; 参数表 被重载运算符所需要的操作数。参数个数 = 运算符所需操作数个数 - 1,缺省的左操作数必须是运算符所属类对象。例如:class complex double real, imag; public:complex operator + (complex&); 定义运算符成员函数例如:complex comp
16、lex:operator + (complex& com) return complex(real + com.real, imag + com.imag); 重载运算符的使用运算符的左操作数必须是该运算符成员函数所属 类的对象。 双目运算符例如:complex com1, com2, com3; com3 = com1 + com2;或com3 = com1.operator +(com2); 单目运算符例如:complex com;+com; com+; 或com.operator +();com.operator +(0);2 用成员函数重载运算符的使用实例例4-1 定义了一个表
17、达三位置的简单类 three_d,在此类中含有三位置的坐标。通过运算符重载来实现对此类对象的 +、 和 = 运算。注意:定义中 + 和 运算符函数的返回值不应是被加对象和被减对象,而 = 运算符函数的返回值必须是被赋值的对象。这都是运算符的操作含义所决定的。4.4 使1 使元函数重载运算符元函数重载运算符的语法形式 在类定义体中重载运算符友元函数 friend 友元函数关键字 type 运算符函数类型; operator 运算符函数名关键字; 要重载的运算符名; 参数表 被重载运算符所需要的操作数。参数个数 = 运算符所需操作数个数。 例如:class point int x, y; publ
18、ic:friend point operator +(point&, point&); 定义重载运算符友元函数例如:point operator +(point& p1, point& p2) return point(p1.x + p2.x, p1.y + p2.y); 重载运算符的使用 双目运算符例如:point pt1, pt2, pt3;pt3 = pt1 + pt2;或pt3 = pt1.operator +(pt2); 单目运算符例如:point pt;+pt; pt+ 或operator +(pt);operator +(pt, 0);2 使元函数
19、重载运算符的应用实例例4-2 使元函数重载运算符的方法实现复数的四则运算,复数运算规则如下:(a + bi) + (c + di) = (a + c) + (b + d)i;(a + bi) - (c + di) = (a - c) + (b - d)i;(a + bi) * (c + di) = (ac - bd) + (bc + ad)i;(a + bi) / (c + di) = (ac + bd) + (bc - ad)i)/(c2+ d2);定义一个复数类 complex,并和定义相应的友元函数,用于重载运算符 +、-、*、/。例4-3 使元函数重载运算符的方法实现集合运算。集合可以
20、用数组或链表表示,在该数组或链表中不包含重复元素。定义整型数集合 set,元素个数用整型变量 card 表示。集合的操作包括向集合中追加元素、显示集合的全部元素以及使元函数重载运算符的方法实现的集合的主要运算,这些运算包含: 判定某一个元素属于集合的运算符 &; 判定两个集合相等的运算符 =; 判定两个集合的不等于运算符 !=; 两个集合的交运算符 *; 两个集合的并运算符 +; 判定某集合是另一集合的子集运算符 <=; 判定某集合是另一集合的纯子集运算符 <。实例 4-3程序4.5 成员函数与友元函数重载运算符的比较1参数数目: 双目运算符 重载运算符的成员函数只有一个参
21、数,用于表示表达式的右操作数。 重载运算符的友元函数有两个参数,分别用于表达式的左、右操作数 单目运算符 重载运算符的成员函数无参数。 重载运算符的友元函数带有一个参数,用于表示表达式的唯一操作数。2无论是使用成员函数还是友元函数重载的运算符,调用它们的表达式形式是一致的,而调用它们的函数形式是有差别的。表达式形式友元函数调用形式成员函数调用形式a boperator (a, b)a. operator (b) aoperator (a)a. operator ()a operator (a, 0)a. operator (0)3大部分运算符重载函数既可以是成员函数,又可以是友元函数。究竟选择
22、哪一种好呢?要视实际情况 和程序员的习惯。但是应注意以下情况: 对于双目运算符,如果使用成员函数重载,则在有些情况下会产生操作数类型错误。例如: class complex double real, imag;public:complex operator +(double x);complex complex:operator +(double x) return complex(real + x, imag); 对 complex 的对象 com 进行如下的 + 运算:com = com + 100.0;由于对象 com 是 + 运算符的左操作数,所以它调用了 complex 类重载的 +
23、 运算符,把实数 100.0 加到 com 的实部数据成员 real 上。如果按照 + 运算符操作数的交换律,将上面的表达式写成:com = 100.0 + com;则会引起操作数类型错误,无法完成表达式所要 求的操作。这是因为 complex 类 + 运算符的左操作数是 complex 类对象,而表达式的左操作数是实数,无法调用 complex 类 + 运算符函数。如果使用两个友元函数来替换上述 complex 类的 +运算符重载:friend operator +(complex com, double x); friend operator +(double x, complex com
24、);complex operator +(complex com, double x) return complex(com.real + x, imag); complex operator +(double x, complex com) return complex(x + com.real, imag); 则可避免上述问题,因为友元运算符函数的两个 操作数都是显式地传递给运算符函数的,能满足 加运算操作数的交换律。所以一般建议使元函数重载双目运算符,特别是期望双目运算符的左操作数的类型能够隐式转换的情况,则重载双目运算符必须使元函数,而不能使用成员函数。 若一个运算符需要修改其操作所施
25、加对象(特别是该对象为该运算符的第一操作数)的状态,则选择使用成员函数重载运算符不破坏类对象的封装性,更符合面向对象程序设计的原则。4.6 自增 + 和自减 - 运算符的重载从运算操作的正确性考虑,使用成员函数还是使用友元函数重载自增运算符 + 和自减运算符 - 的效果是一致的。但从面向对象设计原则出发,类对象的自增和自减运算实际上是对被封装的类数据成员依次进行的。因此,建议使用成员函数实现自增运算符 + 和自减运算符 - 的重载。使用 + 和 - 的表达式形式分为前缀和后缀两种形式,这两种形式对操作数的最终修改虽然相同,但运 算符函数的返回值不同,前缀形式返回修改后的操作数,而后缀形式返回修
26、改前的操作数。例如:int i = 0, j = 0;i+; +j;cout << i <<","<< j << endl;/ 显示1,1cout << i+ <<","<< +j << endl;/ 显示1,2在 C+ 2.1 及以后的版本中,编译器可以通过判别在自增或自减运算符函数的参数列表中是否增加了一个附加的整型 int 形式参数来区分所重载的自增或自减运算符可以用于前缀表达式还是用于后缀表达式。1自增运算符 “+” 使用成员函数重载 原型前缀形式:类
27、名后缀形式:类名例如:class point int x, y; public:operator +();operator +(int);point operator +();point operator +(int); 定义前缀形式:类名后缀形式:类名例如:类名:operator +() 类名:operator +(int) point point:operatot +() +x,+y;return *this;point point:operatot +(int) point temp(x, y);+x,+y;return temp; 调用前缀表达式形式:+类对象名; 后缀表达式形式:类对
28、象名+; point pt;例如:+pt;pt+;/ 前缀表达式/ 后缀表达式前缀函数形式:对象名.operator +();后缀函数形式:对象名.operator +(0); point pt;例如:pt.operator +();pt.operator +(0);/ 前缀函数形式/ 后缀函数形式2自减运算符 “-” 使用成员函数重载 原型前缀形式:类名后缀形式:类名例如:class point int x, y; public:operator -();operator -(int);point operator -();point operator -(int); 定义前缀形式:类名后缀
29、形式:类名例如:类名:operator -() 类名:operator -(int) point point:operatot -() -x,-y;return *this;point point:operatot -(int) point temp(x, y);x-, y-;return temp; 调用前缀表达式形式:-类对象名; 后缀表达式形式:类对象名-; point pt;例如:-pt;pt-;/ 前缀表达式/ 后缀表达式前缀函数形式:对象名.operator -();后缀函数形式:对象名.operator -(0); point pt;例如:pt.operator -();/ 前缀
30、函数形式pt.operator -(0); / 后缀函数形式例4-4 在 point 类中对运算符 + 和 - 进行重载,其操作是对 point 类的数据成员 x 和 y 分别进行的。注意,如果在程序中没有重载能用于后缀表达式形式的 + 和 -,则在后缀表达式形式调用 + 和- 时,会出现编译警告,而运行时会以前缀表达式的结果替代后缀表达式的操作结果。4.7 调用运算符 () 和下标运算符 的重载1调用运算符“()”顾名思义,调用运算符可以用函数调用表达式的形 式使运算符操作所施加的对象完成任何需要的功能 操作,例如,为操作对象的各个数据成员赋值。调 用该运算符的表达式的一般形式为:类对象名(
31、调用参数列表);可以认为调用运算符是一个双目运算符。左操作数: 必须是该运算符操作所施加的类对象。右操作数: 为操作类对象所需要传递的一组参数, 这组参数可以由任意个数、任何类型的对象组成,当然也可以无参数。显然,应该使用成员函数重载调用运算符。 原型返回类型 operator ()(调用参数列表);其中返回类型可以是任何合法类型。例如:class point int x, y;public:void operator ()(int, int); 定义返回类型 类名:operator ()(调用参数列表)例如:void point:operator ()(int x, int y)this-&
32、gt;x = x; this->y = y; 调用类对象名(调用参数列表); point pt;pt(50, 80);例如:是一个使用成员函数重载调用运算符 “()”例4-5单的简实例,有助于对调用运算符定义形式、使用方法和用途的理解。注意,调用运算符函数的右操作数,调用参数列表中的参数也有缺省参数值。2下标运算符“”与调用运算符相似,下标运算符可以用下标表达式的形式对运算符操作所施加对象的相关数据成员进任何需要的和操作,例如,读写矩阵类对象封装的内部矩阵(数组)的某个指定元素。调用该运算符的表达式的一般形式为: 类对象名下标列表;可以认为下标运算符也是一个双目运算符。左操作数: 必须是
33、该运算符操作所施加的类对象。右操作数: 为类对象所需要传递的一组下标,这组下标可以由任意个数的整型对象组成,不无下标。显然,应该使用成员函数重载下标运算符。 原型返回类型 operator (<下标列表>);其中返回类型可以是除 void 外的任何合法类型。例如:class Matrix double mt10, 10;public:double& operator (int, int); 定义返回类型 类名:operator (下标列表)例如:double& Matrix:operator (int x, int y)if(x > -1 &&
34、 x < 10) && (x > -1 && x < 10) return mtx, y; 调用类对象名调用参数列表;Matrix matrix; matrix5, 8 = 38.5;例如:例4-6 是一个使用成员函数重载下标运算符的简单实例,有助于对下标运算符定义形式、使用方法和用途的理解。 通过重载的下标运算符函数可以方便地私有数据成员 divisionTotals 中的每一个元素。 重载下标运算符 “” 时,返回一个 int得的,使通过重载的 “”的元素既能够读,也能够写。因此,下标表达式可以出现在赋值语句的左边。例4-7 是一个用来处理
35、矩阵运算操作的实例。假定有一个实数矩阵,需要对它进行加法、减法和乘法运算(通过重载运算符+、-、*来实现),为此需要通过重载函数调用运算符(),来返回矩阵元素,以便 在实现矩阵的加、减、乘运算中使用。注意:矩阵的析构函数matrix:matrix() if(elems) /矩阵是否不为空delete elems;elems = 0;4.8 动态内存管理运算符的重载用于动态内存管理的运算符包括 new、delete (用于单个对象的内存分配和对象数组的内存分配和)和 new、delete (用于)。系统预定义的这两对运算符可以适用于所有类型对象的动态内存管理,但不能确保对所有类型对象的动态内存管
36、理都高效,尤 其是对那些占用内存空间小、结构简单的类型对象, 使用那些为了适应复杂情况管理的时、空开销是没有 必要的,因此大大降低运行效率。解决这一问题的办 法就是重载动态内存管理运算符,定义适应类型对象 动态创建和撤消的内存管理操作。重载动态内存管理运算符的方式有两种: 全局方式:重载的动态内存管理运算符完全替代了原有的动态内存管理运算符 new、delete、new、delete 。这种方式一般很少使用,除非确实需要修改原来的通用管理算法或添加所有类型对象创建和撤消都需要的附加操作。 局部方式:重载的运算符函数只对某个特定类对象的创建和撤消有效,这是重载动态内存管理运算符的常用方式。显然,
37、将动态内存管理运算符重载函数定义为成员函数更符合面向对象程序设计原则。重载 new 和 delete 运算符的一般方法: 定义可以存放 n 个被管理类型对象的静态数组,作为动态分配的内备。 定义空闲链,将被回收的对象内存链结起来。 定义指示变量,用于指示作为内空间是否已经被初次顺序分配完。备的静态数组 重载动态内存分配运算符 new,用于顺序从静态数组中或从空闲链中为动态创建的对象分配空间。 重载动态内存回收运算符 delete,用于动态创建的对象,并将该被闲链中。对象的内存空间链结到空例4-8 定义适用于 point 类对象动态创建和撤消的内存管理运算符 new 和 delete 重载成员函
38、数,并测试使用重载的 new 和 delete 动态创建和撤消 point类对象的情况。注意,此例中重载的 new 和 delete 运算符每次只能为单个 point 类对象分配和内存 ,而如果分配和point 类对象数组则需要重载 new 和 delete 运算符。1问题分析分配一个具有足够空间静态数组(数组元素的结构应满足存放 point 类对象属性和动态分配和的需要。对该数组的分配和操作示意如下:usedfree_listusedfree_listx1 x2x3y1 y2y3x1y1x2y2x3y3x4y4x4y4从数组中顺序分配随机删除已分配元素其中:·静态数组的元素由存放位
39、置属性 x 和 y 的单元,以及用于存放被元素地址的指针单元组成。·静态数据成员used 用于指示从静态数组中已经被初次顺序分配的元素个数。·静态数据成员freelist 用于指向被链表。回收的元素元素的动态分配和操作分为两种情况: 静态数组中的元素未被初次顺序分配完,即 used的值小于静态数组的元素总数时(元素第一次被 分配): 分配:以 used 的值为下标,为用户动态分配一个元素,并修改 used 的值。:将新的元素与 freelist 指向的存放已元素的空闲链连接,并使 freelist 指向修改后的空闲链。 数组中的元素已被初次顺序分配完,即 used 的值大于
40、静态数组的元素总数时(数组中元素的再分配): 分配:如果被元素的空闲链不为空,则从空闲链中动态分配一个元素,并修改空闲链。:将新的元素与 freelist 指向的存放已元素的空闲链连接,并使 freelist 指向修改后的空闲链。将运算符 new 和 delete 重载函数和定义为 point类的成员函数,point 类的类图被描述如下:2详细设计 类设计 point 类类定义class point int x, y;static int used; static block *freelist;public:point(int vx, int vy);void* operator new (
41、size_t size); void operator delete ();void print();其中,block 为用于动态定义 point 对象时,分配其属性空间的数据结构:struct block int x, y; block *next;算法描述重载运算符 new 的算法 N-S 图:重载运算符 delete 的算法 N-S 图: 类的应用在主函数 main 中动态创建 point 对象、显示对象信息;然后动态动态创建 point 对象,用以测试重载的动态内存管理运算符 new 和 delete 的功能。程序编码34.9 赋值运算符重载类对象之间的赋值操作是在一一对应的数据成员之
42、 间进行的。所以赋值运算符的重载函数应作为类的成 员函数。赋值运算符成员函数的原型、定义和调用表 达式如下: 原型类名operator =(const 类名&);例如:class point int x, y;public:point operator = (const point&); 定义类名 类名:operator = (const 类名&对象名)例如:point point:operator = (const point& pt)x = pt.x; y = pt.y;return *this; 调用对象名1 = 对象名2;point pt1, pt2;p
43、t2 = pt1;系统会为每一个类缺省定义一个隐含的赋值运算符例如:成员函数。通常情况下,缺省赋值运算符是可以胜任类对象之间的赋值操作,但在某些特殊情况下,如类中有指针类型的数据成员时,使用缺省赋值运算符就可能产生错误。例如:#include <iostream.h> #include <string.h> class stringchar* ptr;public:string(char* s)ptr = new charstrlen(s) + 1; strcpy(ptr, s);string() delete ptr; void print() cout <&l
44、t; ptr << endl; ;void main()string p1("Chen");string p2(“Zhang"); p2 = p1;cout << "p2:"p2.print();cout << "p1:"p1.print();在执行 p1.print(); 时将发生错误。是执行赋值语句 p2 = p1,使 p2.ptr 和 p1.ptr 都指向了 “Chen” 占用的内存空间,而 p2.ptr存原来所指向的 “Zhang”占用的内空间被泄漏。当 p2 的生命周期结束时,系
45、统自动调用析构函数将这一内存空间撤消。这时 p1 的指针成员ptr 所指向的内存空间已经成为不的空间了。下图描述了p这1一动错态误空间产1 生的p2过程动:态空间2执行 p2 = p1 之前执行 p2 = p1 之后P2 生命周期结束后解决这一问题的方法是显式地定义一个重载赋值运算符的成员函数,使 p1 和 p2 有各自的string& operator = (const string&);string& string:operator = (const string& s)空间。if (this = &s) return *this;if (ptr)
46、delete ptr;/ 类对象自身赋值/原有动态内存指针ptr = new charstrlen(s.ptr) + 1;/ 为赋值操作分配新内存strcpy(ptr, s.ptr);return *this;/ 赋值字符串值/ 返回被赋值的类对象两点说明: 重载类的赋值运算符应该使用成员函数,而不应使元函数,如果上述赋值运算符重载使用了友元函数:friend string& operator = (string& p2, string& p1);这时,表达式p1 = "chen"将被解释为operator = (p1, "chen&quo
47、t;)这显然是没有什麽问题的,但对于表达式"chen" = p1将被解释为:/ 错误的赋值语句operator = ("chen", p1)C+ 编译器首先将 “chen”即string转换成一个隐含的对象,然后该隐含对象。因此并不认为这个表达式是错误的,从而将导致赋值语法的。 类的赋值运算符可以被重载,但重载的赋值运算符成员函数在其派生类中是不能被继承的。建议:在属性的操作上,重载赋值运算符和拷贝构造函数具有一致性。因此,在需要用户自定义重载赋值运算符和拷贝构造函数的情况下,一般先定义一个实现属性操作的私有成员函数,然后在重载赋值运算符和拷贝构造函数定
48、义中调用此私有成员函数。这样既保证了重载赋值运算符和拷贝构造函数的4.10 重载输入运算符 ">>"输入运算符 “>>”讲是输入流类(将在第八章中详细述)的成员函数。该运算符也是一个双目运算符,它的左操作数必须是输入流类对象的,表示被输入的信息必须来自标准的输入流设备;而右操作数是接收输入信息的指定类对象的。因此,为自定义类重载的输入运算符函数只能是类的友元函数。1原型friend 输入流类&operator >> (输入流类&,类名&);例如:class pointint x, y;public:friend i
49、stream& operator >> (istream&, point&);2定义输入流& operator >>(输入流&流对象名, 类名& 对象名)return 流对象名;例如:istream& operator >> (istream& in, point& pt);cout << “顺序输入点的位置坐标 x 和 y:”;in >> pt.x >> pt.y; return in;3调用cin >> 类对象名;例如:point pt
50、;cin >> pt;注意: 输入运算符重载函数的第一个参数的类型必须是输入流类 istream 对象的,形参名(流对象名)可以使用任何合法的标识符。 输入运算符重载函数的第二个参数的类型必须是接收输入信息的指定类对象的,例如 point&, 而不能使用指定类名,例如 point。 输入运算符重载函数的返回类型必须是输入流类istream 对象的的输入流类对象的,并且在函数体中由 return 返回名必须与第一个参数的形参名(流对象名)一致。4.11 重载输出运算符 “<<"输出运算符 “<<”讲是输出流类(将在第八章中详细述)的成员函数。
51、该运算符也是一个双目运算符,它的左操作数必须是输出流类对象的,表示信息必须被输出到标准的输出流设备;而右操作数是输出信息的指定类对象。因此,为自定义类重载的输入运算符函数也只能是类的友元函数。1原型friend 输出流类&operator << (输出流类&,类名);例如:class pointint x, y;public:friend istream& operator >> (istream&, point&); friend ostream& operator << (ostream&, poi
52、nt);2定义输出流& operator <<(输出流&流对象名, 类名 对象名)return 流对象名;例如:ostream& operator << (ostream& out, point pt);out << “点的位置坐标 x 和 y:”;out << pt.x << “,” <<return out;pt.y << endl;3调用cout << 类对象名;例如:point pt;cin >> pt;cout << pt;注意: 输
53、出运算符重载函数的第一个参数的类型必须是输出流类 ostream 对象的,形参名(流对象名)可以使用任何合法的标识符。 输出运算符重载函数的第二个参数的类型必须是输出信息的指定类对象或对象的或 point。,例如 point&, 输出运算符重载函数的返回类型必须是输出流类ostream 对象的的输出流类对象的,并且在函数体中由 return 返回名必须与第一个参数的形参名(流对象名)一致。例8-3 中对输入运算符 >> 和输出运算符 << 进行了重载,使它们能对按照指定形式(例如由实部和虚部组成的代数和形式,20 + 35i )表示的复数进行标准输入输出操作。例8-4 中对输入运算符 >> 和输出运算符 << 进行了重载,使它们能对按照指定形式(例如由分母组成的分数形式,3/8) 表示的有理数进行标准输入输出操作。4.12 类型转换所谓类型转换是指编译器将一种数据类型值转换
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 文化产业发展专项资金2025年申请项目文化产业与金融创新报告
- 2025-2026学年江苏省南京市鼓楼区高三生物第一学期期末考试试题
- 新员工绩效管理协议
- 朗文英语教材教学课件
- 2025-2026学年江苏省南京市玄武区高三生物第一学期期末质量检测模拟试题
- 独坐敬亭山教学实录课件
- 2025年安全生产法律法规测试教材含答案
- 人教版必修三第二章《为什么停止开发“北大荒”》教学设计
- 明星短视频项目后续跟踪合同
- 网络直播技能培训委托协议
- 加油站加油员劳动合同8篇
- 2025年全国企业员工全面质量管理知识竞赛题库(试题及答案)
- 2025年电信人工智能学习考试题库(含答案)
- 机器人焊接技术与应用考核试卷
- 名著《红岩》三年中考真题及典型模拟题训练(解析版)
- 《会计职业道德》第2版 课件 第一章 道德和职业道德概述
- 开学第一课 教学设计-2024-2025学年七年级上学期道德与法治部编版
- 危险性较大的分部分项工程安全监理实施细则
- 《企业的可持续发展》课件
- 咨询服务承揽合同范本
- 施工期间交通导行方案
评论
0/150
提交评论