第11章 运算符重载
280 likes | 511 Vues
第11章 运算符重载. 11.1 重载运算符的概念. 一个运算符代表一种操作,常用的运算通常可借助一个运算符来实现简单表示,如 a+b 。 C++ 允许对运算符进行重载,实质是对表示某种运算的特殊函数进行重载。. 11.1.1 重载运算符的函数特征. 常见运算对于 C++ 系统的 内置类型 是有效的,但对于 用户定义的数据类型 ,多数运算不再有效。. class Point { int x, y; public: Point(int xv=0, int yv=0) { x = xv; y = yv; }
第11章 运算符重载
E N D
Presentation Transcript
11.1 重载运算符的概念 • 一个运算符代表一种操作,常用的运算通常可借助一个运算符来实现简单表示,如a+b。 • C++允许对运算符进行重载,实质是对表示某种运算的特殊函数进行重载。
11.1.1 重载运算符的函数特征 • 常见运算对于C++系统的内置类型是有效的,但对于用户定义的数据类型,多数运算不再有效。 class Point { int x, y; public: Point(int xv=0, int yv=0) { x = xv; y = yv; } int getx() { return x; } int gety() { return y; } }; Point add(Point& av, Point& bv) { return Point(av.getx()+bv.getx(), av.gety()+bv.gety()); } void main( ) { Point a(1,1), b(2,3), c; //c = a + b; c = add(a, b); }
11.1.1 重载运算符的函数特征(续) • 增加上述函数实现了对加法运算的重载。 • 使用方式: • c = a + b; • c = operator+(a, b); • 运算符重载可以使程序更容易理解,但并不是必需的。 Point operator + ( Point& av, Point& bv ) { return Point(av.getx()+bv.getx(), av.gety()+bv.gety()); }
Point operator+(Point& av, int bx) { return Point(av.getx()+bx, av.gety()); } 另一个重载的加法函数 11.1.1 重载运算符的函数特征(续) • 如果@代表一个运算符,则“operator@”是代表@运算的“函数名”。 • 运算符重载通常是与类定义联系在一起的。
11.1.2 类重载运算符的两种方法 • 1. 成员和非成员重载 • 将运算符重载函数定义为友员(普通函数,非成员)和成员(类的方法)。 class Point { int x, y; public: Point(int xv=0, int yv=0):x(xv), y(yv){ } Point operator+(Point& bv); //成员重载 friend Point operator+(Point& av, Point& bv);//友员重载 }; Point Point::operator+(Point& bv) //成员实现 { return Point(x+bv.x, y+bv.y); } Point operator+(Point& av, Point& bv) //友员实现 { return Point(av.x+bv.x, av.y+bv.y); }
11.1.2 为类重载运算符的两种方法(续) • 2. 采用成员和非成员重载运算符时的函数原型差异 • 重要差别:函数形参的个数不同。 • 对于一个n目的运算符,使用友员重载时仍需n个形式参数,而使用成员重载时只有n-1个参数. • 原因: • 每个类的非静态方法都有一个缺省的this指针作为第一个参数。 • 类的友元函数则没有this指针。
11.1.2 为类重载运算符的两种方法(续) • 3. 不同重载方法对运算符使用形式的影响 • 对于运算符@来说,采用类的成员函数重载时可以“a@b” 和a.operator@( b )两种形式使用运算符,如: • Point a(1,1), b(2,3); • Point c = a + b; • Point c = a.operator + ( b ); • 采用友员重载时可以采用运算和函数调用两种语法形式,如: • Point d = a + b; • Point e = operator+(a, b);
11.1.2 为类重载运算符的两种方法(续) 1、双目运算符@: 成员函数只带一个参数;友元函数带两个参数 aa@bb aa@bb aa.operator@(bb) operator@(aa, bb) 2、单目前缀运算符@: 成员函数不带参数;友元函数带一个参数 @aa @aa aa.operator@( ) operator@(aa )
11.1.2 为类重载运算符的两种方法(续) • 4. 使用成员和非成员重载的限制 • 必须使用成员而不能使用非成员重载: • =、-> 、( )、[ ] • 只能使用友员重载而不能使用类成员重载: • <<、>> • 如果运算符属于赋值类运算,操作中需要修改当前对象(this),一般使用成员重载: • +=、-=、*=、/=、++(前置)、--(前置) • 如果运算符的第一个操作数不是当前定义的类类型数据则必须使用友员重载。
11.1.2 为类重载运算符的两种方法(续) • 原因:若采用成员时第一个参数必然是this。 friend Point operator+(int a, const Point& b) { return Point(a+b.x, b.y); }
11.1.3 重载运算符的限制 ⑴只能重载C++已有的运算符,不能臆造。 ⑵重载时应尽量保持运算符原来的意义,不应“挪为他用”。 ⑶不能改变运算符的本来特性,包括操作数个数、优先级别和结合次序。 ⑷不能改变运算符对基本类型数据的操作方式,不能定义只有内置类型数据为参数的运算符重载。 (5)以下5个C++运算符不能被重载: .(成员访问运算符)、.*(成员指针访问运算符)、 ::(域解析符)、?:(条件运算符)、sizeof
11.2 重载运算符函数的设计 • 1. 重载运算符的返回值 • 运算符函数的返回值类型一般都与当前类的类型有关。 • 运算符函数返回对象的值还是对象引用 • 原则:如果运算符形成的表达式(函数调用表达式)需要作为左值则返回引用,否则返回值。
11.2 重载运算符函数的设计(续) • ⑴+、-、*、/、%、后置的++和-- • 因为加法、减法、乘法、除法和后置的加1和减1表达式不能做左值,重载时应返回对象的值,故应采用类似如下的函数原型: • Point operator+(...); • Point operator++(...); • ⑵=、[ ]、前置的++和-- • 由于赋值表达式、下标引用变量和前置的加1、减1表达式都可做左值,故应返回对象的引用,如: • Point& operator=(...); • char& operator[](...);
11.2 重载运算符函数的设计(续) • 2. 运算符函数的引用参数 • 当运算符需要操作类的对象时,应确定该使用指针还是引用做函数的参数。一般不直接使用类对象做参数,其中的一个重要原因是效率。 • 此外,如果运算符函数中不修改被引用对象的值,通常要对参数进行常量限制,如: • Point operator+(const Point& a, const Point& b);
11.3 若干常见运算符的重载 • 一些其它重要的运算符重载。
11.3.1 重载增量运算符++ • 1. 前置的++ • 前置++遵循一般的单目运算符重载规范,但由于可以作为左值,故函数应该返回当前对象的引用。 class Point { int x, y; public: Point(int xv=0, int yv=0){ x = xv; y = yv; } Point& operator++( ); //成员重载 }; Point& Point::operator++( ) //成员实现 {++ x; ++ y; //当前对象的坐标加1 return *this; //返回当前对象引用 }
11.3.1 重载增量运算符++(续) Point a(1,2), b; b = ++a; 由于前置++运算返回的是已经加1之后的当前对象,故b的值为(2,3)。 如果使用友员重载,应以如下代码实现: //在类Point定义中的函数声明 friend Point& operator++(Point& a); Point& operator++(Point& a) //作为普通函数实现 { ++ a.x; ++ a.y; return a; }
11.3.1 重载增量运算符++(续) • 2. 后置的++ • 将后置++运算视为二元运算,且第二个运算数是int类型,即后置运算表达式a++被视为a+0。 • 后置++是普通加法运算,不能作为左值,故返回值只是对象值而不是引用。
11.3.1 重载增量运算符++(续) Point operator++(int); Point Point::operator++(int) { Point t(x, y); x++; y++; return t; } 重载后的后置加运算符可以按如下方式调用: Point a(1,2); a++; operator++(a, 0); a.operator++( 0 ); friend Point operator++(Point& a, int); Point operator++(Point& a, int) { Point t(a.x, a.y); a.x++; a.y++; return t; } 见exam_13后置++
11.3.2 重载赋值运算符= • C++会为每个类提供一个缺省的赋值运算符,因此,在对象之间可以直接赋值。 • 对于Point类,下面的代码可以正常工作: • Point a(1, 2), b; • b = a; • 赋值运算的实质是利用一个对象来重新改写一个已有的对象,为了避免两个对象占用同一份资源如内存区,不能直接进行数据按位复制,需要修改缺省的赋值行为。
11.3.2 重载赋值运算符=(续) • 赋值运算必须使用成员重载,且因为赋值运算可做左值,故应返回对象的引用。 • String& operator=(const String& copy); • 赋值运算本质上是析构函数和构造函数的结合体,含义是拆除原对象,再根据右值构造新对象。
11.3.3 重载下标运算符[] • C++中,在重载下标运算符时,认为它是一个双目运算符,例如:X[Y] X为左操作数 Y为右操作数 • 格式: • 返回类型 类名::operator[ ](形参) • { • //函数体 • } • 其中形参表示下标,并且只能有一个参数 • 下标运算符重载定义只能使用成员函数 • X[5] 被解释为 X.operator[ ]( 5 )
11.3.3 重载下标运算符[](续) • 若想使String对象像C字符串那样支持对字符的索引,这需要为String增加一个下标运算符函数。 //在String类定义中增加的声明 inline char& operator[](int index); //类定义外的实现 inline char& String::operator[](int index) { return str[index]; } 函数的返回值是引用,其目的是支持类似下面的语法: String s("a string."); s[2] = ’x’; 经过重载后的s[k]如同C中的字符数组元素一样,是一个普通的char型变量,可读可写。
11.3.4 重载类型转换运算符 • 重载类型转换运算符可以将一个对象表达式转换为其它类型的数据。 • 类型转换运算为单目运算,必须用成员重载。 • 将Point对象转换为double型,结果是点的坐标到原点的距离。 operator double(); //在Point类定义中增加的声明 Point::operator double() //在Point类定义之外的代码实现 { return sqrt(x*x + y*y); //需要cmath头文件支持 } 程序中可以很容易地使用重载后的“距离运算”: Point a(10, 20); double b = double(a);
11.3.4 重载类型转换运算符(续) • 应注意类型转换运算的特殊形式: • ⑴double是转换后的类型,可以是C++的内置类型或用户定义的类型。 • 将Point对象转换为一个C字符串, • operator char*(); //在Point类定义中的声明 • ⑵无参数,因为缺省的this指针给出了被转换的当前对象。 • ⑶函数没有返回类型,事实上,函数的返回类型已包含在名字中,此例为double。
*11.3.5 重载函数调用运算符与函数对象 • 此部分内容自学。