c++面向对象第十章运算符重载(1)
C++基础系列——运算符重载
C++基础系列——运算符重载1. 运算符重载简介所谓重载,就是赋予新的含义。
函数重载(Function Overloading)可以让⼀个函数名有多种功能,在不同情况下进⾏不同的操作。
同样运算符重载(Operator Overloading)可以让同⼀个运算符可以有不同的功能。
可以对 int、float、string 等不同类型数据进⾏操作<< 既是位移运算符,⼜可以配合 cout 向控制台输出数据也可以⾃定义运算符重载:class Complex{public:Complex();Complex(double real, double imag);Complex operator+(const Complex &a) const;void display() const;private:double m_real;double m_imag;};// ...// 实现运算符重载Complex Complex::operator+(const Complex &A) const{Complex B;B.m_real = this->m_real + A.m_real;B.m_imag = this -> m_imag + A.m_imag;return B;// return Complex(this->m_real + A.m_real, this->m_imag + A.m_imag);}int main(){Complex c1(4.3, 5.8);Complex c2(2.7, 3.7);Complex c3;c3 = c1 + c2; // 运算符重载c3.display();return 0;}运算结果7 + 9.5i运算符重载其实就是定义⼀个函数,在函数体内实现想要的功能,当⽤到该运算符时,编译器会⾃动调⽤这个函数,它本质上是函数重载。
运算符重载(面向对象程序设计C++)
运算符重载运算符重载的本质是函数重载,它也是C++多态的一种体现,为用户提供了一个直观的接口,调用运算符操作自定义数据类型其实就是调用运算符函数。
运算符重载增强了C++的可扩充性,使得C++代码更加直观、易读,且便于对对象进行各种运算操作。
重载的运算符是具有特殊名字的函数:它们的名字由关键字operator 和其后要重载的运算符共同组成。
语法格式 返回类型 operator 运算符(参数列表){函数体; }运算符重载规则:只能重载C++中已有的运算符,不能创建新的运算符。
重载之后的运算符不能改变其优先级和结合性,也不能改变其操作数的个数及语法结构。
避免没有目的地使用重载运算符。
类属关系运算符“.”、成员指针运算符“*”、作用域运算符“::”、sizeof 运算符和三目运算符“?:”不可以重载。
输入输出运算符的重载 IO 标准库使用“>>”和“<<”运算符执行输入输出操作,对于这两个运算符来说,IO 库定义了其基本数据类型的操作,但若要直接对类对象进行输入输出,则需要在类中重载这两个运算符。
输入运算符语法格式istream& operator>>(istream&, 类对象引用);输出运算符语法格式ostream& operator<<(ostream&,const 类对象引用); 关系运算符的设计准则关系运算符都要成对的重载。
“==”运算符应该具有传递性当成对出现运算符重载时,可以把一个运算符的工作委托给另一个运算符。
赋值运算符的重载对于赋值运算符来说,如果不重载,那么类会自动为我们提供一个赋值运算符。
这个默认的赋值运算符跟默认拷贝构造函数一样,就是把一个对象的数据成员的值复制给另一个对象对应的数据成员。
下标运算符的重载返回类型 operator[] (参数列表);重载“[]”运算符可以实现两个目的:(1)“对象[下标]”的形式类似于“数组[下标]”,更加符合习惯。
重载的概念(精)
重载
} vector vector::operator -( vector v1) {
vector v;
v.x = - v1.x; v.y = - v1.y;
return v;
} void main() { vector v1(4.5,-7.8),v2(-1.5,1.2);
v1.print();
重载
Increase & operator ++(); Increase operator ++(int); void display() {cout <<"the value is"<<value<<endl;} //前缀方式
重载
protected:
int value; }; Increase & Increase::operator ++() {
重载
例1 求绝对值函数abs()的重载。 int abs(int x) {
return(x>=0?x:-x);
} float abs(float x) { return(x>=0?x:-x); } long abs(long x)
{
重载
return(x>=0?x:-x);
} double abs(double x) { return(x>=0?x:-x);
v2.print();
重载
}
运行结果为: 4.5 -7.8 -1.5 1.2 3 -3 -6.6 6.6
重载运算符在调用时仍然是靠参数区分的。上例中 重载的运算符“-”,若出现在两个数据之间,只要这两 个数据为相同的基本数据类型、vector类型或其它重载 过的类型,都能正确调用相应的运算代码;若它单独出 现在某个基本数据类型或vector类型等重载过的数据前 面,则自动执行求“相反数”的运算。
C_运算符重载_各类详细介绍
▪ 说明
运算符重载函数 operator@()可以返回任何类型,甚至可 以是 void类型,但通常返回类型与它所操作的类的类型 相同,这样可使重载运算符用在复杂的表达式中。例如, 在例7-2中,可以将几个复数连续进行加、减、乘、除的 运算。
用友元函数重载单目运算符时,需要一个显式的操作数, 例7-3中,用友元函数重载单目运算符“-”
#include<iostream.h> class nclass{ int a,b; public:
nclass(int x=0,int y=0) { a=x;b=y;} friend nclass operator -(nclass obj); void show(); };
▪ complex operator+(complex com1,complex com2) { return complex(com1.real+com2.real,com1.imag+com2.imag;}
▪ 这种方法是直接将一个无名临时对象创建到主调函数中,那么 运行效率高于前一种。
▪ 单目运算符重载
nclass operator-(nclass obj) { obj.a=-obj.a;
obj.b=-obj.b; return obj;} void nclass::show() { cout<<"a="<<a<<" b"<<b;} ▪ main() ▪{ ▪ nclass ob1(10,20),ob2; ▪ ob1.show(); ▪ ob2=-ob1; ▪ ob2.show(); ▪ return 0; ▪}
运算符重载
{
rpart=rp;
ipart=ip;
}
Complex add( const Complex & com )
{
Complex temp ;
temp .rpart= com .rpart+rpart;
temp .ipart= com .ip+ipart;
return temp ;
}
2020/7/2
பைடு நூலகம்};
★单目运算符重载 :
Complex Complex:: operator {
Complex temp;
( 单) 目无参数 双目一个参数
temp.rpart=-rpart;
temp.ipart)=-ipart;
return temp;
}
Complex c=-a+b;
(a.operator-( )).operator+(b)
};
point point::operator+ (point p1)
{ point p; p.x=x+p1.x; p.y=y+p1.y; return p; }
void main()
{ point p1(10,10),p2(20,20); p1=p1+p2 ;
p1.print(); ++p1; p1.print();
}
//(╳)
运算符成员函数——调用成员函数的对象隐式成为表达式的第一个运算数 外部运算符函数——其第一个参数直接对应表达式的第一个运算数;
x1=x2+x3;
operator+(x2, x3);
2020/7/2
6 6
简述运算符重载的规则。
简述运算符重载的规则。
篇一:运算符重载是C/C++语言中一种强大的功能,允许程序员自定义函数的行为,以处理不同类型的数据。
运算符重载允许程序员在函数中重载算术、逻辑和位运算符,从而能够处理数组、结构体和指针等不同类型的数据。
以下是运算符重载的规则:1. 算术运算符重载算术运算符包括加号、减号、乘号和除号。
每个算术运算符都有一组默认的行为,可以通过运算符重载来自定义它们的行为。
例如,重载加号运算符可以使函数接受一个整数参数,并返回一个新的整数。
下面是一个简单的例子,演示了如何重载加号运算符:```c++struct MyStruct {int value;};MyStruct operator+(const MyStruct& other, int value) {return MyStruct(value + other.value);}int main() {MyStruct mystruct1 = { 10 };MyStruct mystruct2 = { 20 };int result = mystruct1 + mystruct2;std::cout << "result = " << result << std::endl;return 0;}```在上面的例子中,我们定义了一个名为`MyStruct`的结构体类型,其中包含一个整数类型。
然后,我们定义了一个重载加号运算符的函数,该函数接受一个整数类型的参数,并返回一个新的`MyStruct`对象。
在`main`函数中,我们定义了两个`MyStruct`对象`mystruct1`和`mystruct2`,并将它们相加,结果存储在`result`变量中。
2. 逻辑运算符重载逻辑运算符包括条件运算符和逻辑非运算符。
每个逻辑运算符都有一组默认的行为,可以通过运算符重载来自定义它们的行为。
第十章 运算符重载
7.赋值运算符
只要是用户定义的类或结构,都应能进行赋值运算,如: Struct S{int a,b; }; S a,b; a=b; //c语言允许如此赋值 对任何类,像拷贝构造函数一样,c++也默认提供赋值运算符,但要区别拷 贝构造函数和赋值运算符: void fn(MyClass& mc) { MyClass newMC=mc; //这是拷贝构造函数 newMC=mc; //这是赋值运算符 } 在拷贝构造函数中,我们碰到深拷贝与浅拷贝的问题,赋值运算符也同样, 什么时候浅拷贝不合适,就应提供赋值运算符。 通常赋值运算符有两部分,第一部分与析构函数类似,在其中取消对象已经 占用的资源。第二部分与拷贝构造函数类似,在其中分配新的资源。
6.转换运算符
转换运算符的声明形式为: operator 类型名( ); 它没有返回类型,因为类型名就代表了它的返回类型。 转换运算符将对象转换成类型名规定的类型。转换时的形式就象强不行的,因为强制转换只 能对基本数据类型进行操作,对类类型的操作是没有定义的。 要防止同一类型提供多个转换路径,它会导致编译出错,例如: class A { void main() { public: B sb; A(B& b); //用B构造A A a=A(sb); //是构造还是转换? //… } }; class B { public: operator A(); //B转换成A //… };
C++规定,运算符中,参数说明都是内部类型时,不能重载。例如不允许声 明: int * operator + (int, int*); 即不允许进行下述运算: int a=5; int *pa=&a; pa=a*pa; //error C++基本数据类型之间的关系是确定的,如果允许定义其上的新操作,那么, 基本数据类型的内在关系将混乱。 C++还规定了“. :: .* .-> ?:”这5个运算符不能重载,也不能创造新运算 符。例如,不允许声明: int operator @(int,int); 或者: int operator :: (int,int);
C 期末考试第十章运算符的重载复习提纲完整ppt
定义作为友元函数的重载函数 {
赋值 pt.x++; _____运算_______________
return pt; } int main() { int m; while(cin>>m) { Point p1(m,m),p2(m+1,m+1),p3; (++p1).print(); p3 = p2++; p3.print(); p2.print(); } return 0; }
Complex(){real=0;imag=0}
//定义构造函数
Complex(double r,double i){real=r; imag=I;} //构造重载函数
Complex operator + (Complex &c2)
//声明重载运算符的“+”函数
void display();
//定义输出函数
*4、::重C载sizo不emo能f p改?:l变ex运算C符o对m象p的le个x数::operator +(Complex &c2) //定义重载运算符“+”的函数
class {Complex
* :: sizeof ?: {
Complex c;
赋值
c.real=real+c2.real;
pt. void
二、重载的方法:
运算符重载的方法是定义一个重载运算符的函数,在需要执行被重载的运算符 时,系统就自动调用该函数,以实现相应的运算。也就是说,运算符重载是通过 定义函数实现的。运算符重载实质上是函数的重载。
C++运算符重载(简单易懂)
C++运算符重载(简单易懂)运算符重载,就是对已有的运算符重新进⾏定义,赋予其另⼀种功能,以适应不同的数据类型。
你可以重定义或重载⼤部分 C++ 内置的运算符。
例如 + 、 - 、 * 、 / 、++、--、>>、<<等,这样,你就能使⽤⾃定义类型的运算符。
运算符重载的基本格式重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。
与其他函数⼀样,重载运算符有⼀个返回类型和⼀个参数列表。
Point operator+(const Point &);运算符重载有两种⽅式:⼀种是类内重载(运算符重载函数作为类的成员函数),另⼀种是类外重载(运算符重载函数作为类的友元函数)类内重载#include <iostream>using namespace std;class Point{public:Point(){};Point (int x, int y): x(x),y(y) {};Point operator+(const Point &a){ //类内重载,运算符重载函数作为类的成员函数Point ret;ret.x = this->x + a.x;ret.y = this->y + a.y;return ret;}int x,y;};int main() {Point a(2,4),b(5,3);Point c = a + b;cout<< "x :" << c.x << endl;cout<<"y :" << c.y << endl;}当上⾯的代码被编译和执⾏时,它会产⽣下列结果:x : 7y: 7运算符重载是类内重载时,运算符重载函数作为类的成员函数,以上述代码为例 a + b 相当于 a 对象调⽤+⽅法并且传⼊参数时 b 对象类外重载#include <iostream>using namespace std;class Point{public:Point(){};Point (int x, int y): x(x),y(y) {};friend Point operator+(const Point &, const Point &);int x,y;};Point operator+(const Point &a,const Point &b){//类外重载,运算符重载函数作为类的友元函数Point ret;ret.x = a.x + b.x;ret.y = a.y + b.y;return ret;}int main() {Point a(2,4),b(5,3);cout<< "x :" << c.x << endl;cout<<"y :" << c.y << endl;}当上⾯的代码被编译和执⾏时,它会产⽣和上⾯⼀样的结果各种运算符重载实例下⾯将进⾏各种运算符重载实例的代码演⽰,演⽰⼏种基本的运算符重载。
《C运算符重载》PPT课件
h
4
二元运算符重载(2)
函数的返回值 返回引用 返回非常量值 返回常量值 返回值优化
重载+和+=运算符对(- -=, * *=, 等)
h
5
重载赋值p; operator=(const T&);
默认的赋值运算
限制赋值运算
重载赋值运算符
原因:成员有指针,并动态分配内存时。 检查自我赋值:
• 效率
• a=a; 的情况
h
6
一元运算符
全局函数 成员函数 增量/减量运算符(++/--)
前缀:T& operator++ (); 后缀:T operator++(int);
迭代器
h
7
重载运算符的方针
运算符 所有的一元运算符 = () [] -> ->* += -= *= /= ^=&=|=%=>>=<<= 成员 所有其他二元运算符
成员 非成员
建议使用 必须是成员
h
8
运算符重载
h
1
重载运算符
意义(例)
重载条件 自定义的类型 不可重载的运算符 :: . ->* 不可使用新的运算符
运算符重载 一元/二元运算符: 全局函数/成员函数
h
2
二元运算符重载(1)
全局函数格式: 成员函数的格式: 重载后的表达式形式(成员函数)
左结合:a+b+c; (a.operator+(b)).operator+(c)
C++学习之运算符重载的总结
C++学习之运算符重载的总结C++学习之运算符重载的总结运算符重载是对已有的运算符赋予多重含义,使同⼀个运算符作⽤域不同类型的数据导致不同⾏为的发⽣,C++为运算符重载提供了⼀种⽅法,即运算符重载函数。
其函数名字规定为operator后紧跟重载运算符。
⽐如:operator+(),operator*()等。
(1)运算符重载函数作为类的友元函数的形式: class 类名 { friend 返回类型 operator运算符(形参表); } 类外定义格式: 返回类型 operator运算符(参数表) { 函数体 }友元函数重载双⽬运算符(有两个操作数,通常在运算符的左右两则),参数表中的个数为两个。
若是重载单⽬运算符(只有⼀个操作数),则参数表中只有⼀参数。
同⼀个运算符可以定义多个运算符重载函数来进⾏不同的操作。
(2)运算符重载函数作为类的成员函数的形式: class 类名 { 返回类型 operator 运算符(形参表); } 类外定义格式: 返回类型类名:: operator 运算符(形参表) { 函数体; }对于成员函数重载运算符⽽⾔,双⽬运算符的参数表中仅有⼀个参数,⽽单⽬则⽆参数。
同样的是重载,为什么和友元函数在参数的个数上会有所区别的。
原因在于友元函数,没有this指针。
对于双⽬运算符⽽⾔,运算符重载函数的形参中仅为⼀个参数,它作为运算符的右操作数(如com2对象),⽽当前对象作为左操作数(如:上述中的com1对象),它是通过this指针隐含传递给成员运算符重载函数的。
例如下⾯这样的定义:Complex operator+(Complex com1);//成员函数重载双⽬运算符+20Complex Complex::operator+(Complexcom1)21{22return Complex(real+com1.real,imag+com1.imag);23}对于单⽬运算符⽽⾔,当前对象作为运算符的操作数。
C程序设计运算符重载资料
•
{
目的对象与源对象不是同一个对象
•
……
复制被被赋值对象
•
}
•
*;
返回目的对象
•}
*重载赋值运算符“=”
• 如用户没有为一个类重载赋值运算符,编译程序将生成一个默认赋值运算符函数。赋值运算 把源对象的数据成员逐个地复制到目的对象的相应数据成员
0;
返回值0, 返回操作系统
}
程序运行时屏幕输出如下: 6 -6 请按任意键继续. . .
用类的友元函数重载双目运算符
• 将双目运算符重载为类的友元函数时,友元函数形参表中包含有两个参数,这两个参数分别 作为运算符的左、右操作数。
例4.6 双目运算符重载为类的友元函数示例。
<>
编译预处理命令
;
使用命名空间
例4.8 双目运算符重载为普通函数示例。 声明整型类
{
:
数据成员
;
数据值
:
公有函数
( n = 0): (n){ }
构造函数
( n) { = n; }
设置数据值
() { ; } 返回数据值
};
( , ) 重载运算符"+"
{ (() + ()); }
()
主函数()
{
i(6), j(9), k;
定义整型对象
例4.5 单目运算符重载为类的友元函数示例。
<>
编译预处理命令
;
使用命名空间
声明整型类
{
: 数据成员
在 6.0下会出现的编译时错语,是 6.0的一个,在
<> ;;
编译预处理:
:
改为:
运算符重载详解
运算符重载详解1.运算符重载定义:C++中预定义的运算符的操作对象只能是基本数据类型。
但实际上,对于许多⽤户⾃定义类型(例如类),也需要类似的运算操作。
这时就必须在C++中重新定义这些运算符,赋予已有运算符新的功能,使它能够⽤于特定类型执⾏特定的操作。
运算符重载的实质是函数重载,它提供了C++的可扩展性,也是C++最吸引⼈的特性之⼀。
运算符重载是通过创建运算符函数实现的,运算符函数定义了重载的运算符将要进⾏的操作。
运算符函数的定义与其他函数的定义类似,惟⼀的区别是运算符函数的函数名是由关键字operator和其后要重载的运算符符号构成的。
运算符函数定义的⼀般格式如下:<返回类型说明符> operator <运算符符号>(<参数表>){<函数体>} 2.运算符重载时要遵循以下规则:(1) 除了类属关系运算符"."、成员指针运算符".*"、作⽤域运算符"::"、sizeof运算符和三⽬运算符"?:"以外,C++中的所有运算符都可以重载。
(2) 重载运算符限制在C++语⾔中已有的运算符范围内的允许重载的运算符之中,不能创建新的运算符。
(3) 运算符重载实质上是函数重载,因此编译程序对运算符重载的选择,遵循函数重载的选择原则。
(4) 重载之后的运算符不能改变运算符的优先级和结合性,也不能改变运算符操作数的个数及语法结构。
(5) 运算符重载不能改变该运算符⽤于内部类型对象的含义。
它只能和⽤户⾃定义类型的对象⼀起使⽤,或者⽤于⽤户⾃定义类型的对象和内部类型的对象混合使⽤时。
(6) 运算符重载是针对新类型数据的实际需要对原有运算符进⾏的适当的改造,重载的功能应当与原有功能相类似,避免没有⽬的地使⽤重载运算符。
(7)重载运算符的函数不能有默认的参数,否则就改变了运算符的参数个数,与前⾯第3点相⽭盾了;(8)重载的运算符只能是⽤户⾃定义类型,否则就不是重载⽽是改变了现有的C++标准数据类型的运算符的规则了,会引会天下⼤乱的;(9)⽤户⾃定义类的运算符⼀般都必须重载后⽅可使⽤,但两个例外,运算符“=”和“&”不必⽤户重载;(10)运算符重载可以通过成员函数的形式,也可是通过友元函数,⾮成员⾮友元的普通函数。
C++程序设计基础教程 第2版 第10章 运算符重载
Time(int m,int s):minute(m),sec(s) {} Time& operator++(); //前置++ Time& operator--(); //前置-Time operator++(int); //后置++ Time operator--(int); //后置-void display() {
9
10.2 运算符重载规则
(3) 重载不能改变运算符运算对象的个数 (4) 重载不能改变运算符的优先级别 (5) 重载不能改变运算符的结合性 (6) 重载运算符的函数不能有默认参数 (7) 须和用户自定义类型的对象一起使用,其参数至少有一个 是类对象(或其引用)。
10
10.2 运算符重载规则
(8) 用于类对象的运算符一般必须重载,但“=”和“&”例外。 (9) 应使重载后的功能类似于该运算符作用于标准类型数据时 实现的功能。 (10) 运算符重载形式:类的成员函数、类的友元函数。
cout<<minute<<":"<<sec<<endl; } private:
int minute; int sec; };
Time& Time::operator++() { sec+=1; if(sec>=60) { sec-=60; ++minute; } return *this;
}
Time Time::operator++(int) { Time result=*this; sec+=1; if(sec>=60) { sec-=60; ++minute; } return result;
C++之运算符重载(1)
C++之运算符重载(1)在前一节中曾提到过,C++中运行时的多态性主要是通过虚函数来实现的,而编译时的多态性是由函数重载和运算符重载来实现的。
这一系列我将主要讲解C++中有关运算符重载方面的内容。
在每一个系列讲解之前,都会有它的一些基础学问需要我们去理解。
而运算符重载的基础就是运算符重载函数。
所以今日主要讲的是运算符重载函数。
1.运算符重载是对已有的运算符给予多重含义,使同一个运算符作用域不同类型的数据导致不同行为的发生。
比如 1 int i;2 int i1=10,i2=10;3 i=i1+i2;4 std::cout \"i1+i2=\" i std::endl;5 6 double d;7 double d1=20,d2=20;8 d=d1+d2;9 std::cout \"d1+d2=\" d std::endl; 在这个程序里\"+\"既完成两个整形数的加法运算,又完成了双精度型的加法运算。
为什么同一个运算符\"+\"可以用于完成不同类型的数据的加法运算?这是由于C++针对预定义基本数据类型已经对\"+\"运算符做了适当的重载。
在编译程序编译不同类型数据的加法表达式时,会自动调用相应类型的加法运算符重载函数。
但是C++中所提供的预定义的基本数据类型究竟是有限的,在解决一些实际的问题时,往往需要用户自定义数据类型。
比如高中数学里所提到的复数: 1 class Compl //复数类 2 { 3 public: 4 double real;//实数 5 double imag;//虚数 6 Complex(double real=0,double imag=0) 7 { 8 this-real=real; 9 this- imag=imag;10 }11 } 如果我们建立两个复数,并用\"+\"运算符让它们挺直相加: 1 Complexcom1(10,10),com2(20,20),m;2 sum=com1+com2; 那么会提醒没有与这些操作数匹配的 \"+\" 运算符的错误。
五、运算符重载_面向对象程序设计
面向对象程序设计
2-16
友元运算符函数
单目运算符
对于要改变操作数的运算符(++、--),参数 必须是引用或指针 不改变操作数的运算符(-),可以不用
双目运算符
有两个操作数,因此参数也应该设两个
以友元重载运算符,进行复数类运算fcomplex.cpp
面向对象程序设计
2-16
成员运算符函数
类内声明形式
面向对象程序设计
2-16
友元运算符函数
类内声明
friend 返回类型 operator 运算符(参数表)
类外定义形式
返回类型 operator 运算符(参数表) {……} 类外函数,不属于任何类对象,没有this指针 重载双目运算符,参数表需两个参数 重载单目运算符,参数表需一个参数 不能用友元重载的运算符:= ()[ ] ->
Type指返回类型,@为++或--运算符 参数int只是为了区分前置或后置,通常被传递为0
面向对象程序设计
2-16
特殊运算符重载
++和--
class CPoint { int x,y; public: CPoint(int a=0,int b=0){x=a; y=b;} CPoint operator ++(); CPoint operator ++(int); friend CPoint operator --(CPoint &); friend CPoint operator --(CPoint &, int); void print() {cout<<"x,y:"<<x<<" "<<y<<endl;} };
(C 面向对象程序设计 谭浩强)第10章 运算符重载
第10章运算符重载10.1 什么是运算符重载10.2 运算符重载的方法10.3 重载运算符的规则10.4 运算符重载函数作为类成员函数和友元函数10.5 重载双目运算符10.6 重载单目运算符10.7 重载流插入运算符和流提取运算符10.8 不同类型数据间的转换第10章运算符重载所谓重载,就是重新赋予新的含义。函数重载就是对一个已有的函数赋予新的含义,使之实现新功能。运算符也可以重载。实际上,我们已经在不知不觉之中使用了运算符重载。现在要讨论的问题是: 用户能否根据自己的需要对C++已提供的运算符进行重载,赋予它们新的含义,使之一名多用。譬如,能否用“+”号进行两个复数的相加。在C++中不能在程序中直接用运算符“+”对复数进行相加运算。用户必须自己设法实现复数相加。例如用户可以通过定义一个专门的函数来实现复数相加。见例10.1。10.1 什么是运算符重载第10章运算符重载例10.1 通过函数来实现复数相加。using namespace std;class Complex //定义Complex类{public:Complex( {real=0;imag=0;}//定义构造函数Complex(double r,double i{real=r;imag=i;} //构造函数重载Complex complex_add(Complex &c2; //声明复数相加函数void display( ; //声明输出函数private:double real; //实部double imag; //虚部};Complex Complex∷complex_add(Complex &c2 {Complex c;c.real=real+c2.real;第10章运算符重载c.imag=imag+c2.imag;return c;}void Complex∷display( //定义输出函数{cout<<″(″< ″ , ″ < ″ i ″ << endl ;}{Complex c1(3,4,c2(5,-10,c3;//定义3个复数对象c3=plex_add(c2; //调用复数相加函数cout<<″c1=″; c1.display( ;//输出c1的值cout<<″c2=″; c2.display( ;//输出c2的值cout<<″c1+c2=″; c3.display( ;//输出c3的值return 0;}运行结果如下:c1=(3+4ic2=(5-10ic1+c2=(8,-6i第10章运算符重载结果无疑是正确的,但调用方式不直观、太烦琐,使人感到很不方便。能否也和整数的加法运算一样,直接用加号“+”来实现复数运算呢?如c3=c1+c2;编译系统就会自动完成c1和c2两个复数相加的运算。如果能做到,就为对象的运算提供了很大的方便。这就需要对运算符“+”进行重载。第10章运算符重载运算符重载的方法是定义一个重载运算符的函数,在需要执行被重载的运算符时,系统就自动调用该函数,以实现相应的运算。也就是说,运算符重载是通过定义函数实现的。运算符重载实质上是函数的重载。重载运算符的函数一般格式如下:函数类型 operator 运算符名称 (形参表列{ 对运算符的重载处理 }例如,想将“+”用于Complex类(复数的加法运算,函数的原型可以是这样的:Complex operator+ (Complex& c1,Complex& c2;10.2 运算符重载的方法第10章运算符重载在定义了重载运算符的函数后,可以说: 函数operator+重载了运算符+。为了说明在运算符重载后,执行表达式就是调用函数的过程,可以把两个整数相加也想像为调用下面的函数:int operator + (int a,int b{return (a+b;}如果有表达式5+8,就调用此函数,将5和8作为调用函数时的实参,函数的返回值为13。这就是用函数的方法理解运算符。可以在例10.1程序的基础上重载运算符“+”,使之用于复数相加。第10章运算符重载例10.2 改写例10.1,重载运算符“+”,使之能用于两个复数相加。#includeusing namespace std;class Complex{public:Complex( {real=0;imag=0;}Complex(double r,double i{real=r;imag=i;}Complex operator+(Complex &c2;//声明重载运算符的函数void display( ;private:double real;double imag;};Complex Complex∷operator+(Complex &c2 //定义重载运算符的函数{ Complex c;c.real=real+c2.real;c.imag=imag+c2.imag;第10章运算符重载return c;}void Complex∷display({ cout<<″(″< ″ , ″ < ″ i ″ << endl ;}int main({ Complex c1(3,4,c2(5,-10,c3;c3=c1+c2; //运算符+用于复数运算cout<<″c1=″;c1.display( ;cout<<″c2=″;c2.display( ;cout<<″c1+c2=″;c3.display( ;return 0;}运行结果与例10.1相同:c1=(3+4ic2=(5-10ic1+c2=(8,-6i第10章运算符重载请比较例10.1和例10.2,只有两处不同:(1 在例10.2中以operator+函数取代了例10.1中的complex_add函数,而且只是函数名不同,函数体和函数返回值的类型都是相同的。(2 在main函数中,以“c3=c1+c2;”取代了例10.1中的“c3=plex_add(c2;”。在将运算符+重载为类的成员函数后,C++编译系统将程序中的表达式c1+c2解释为c1.operator+(c2 //其中c1和c2是Complex类的对象即以c2为实参调用c1的运算符重载函数operator+(Complex &c2,进行求值,得到两个复数之和。第10章运算符重载虽然重载运算符所实现的功能完全可以用函数实现,但是使用运算符重载能使用户程序易于编写、阅读和维护。在实际工作中,类的声明和类的使用往往是分离的。假如在声明Complex类时,对运算符+,-,*,/都进行了重载,那么使用这个类的用户在编程时可以完全不考虑函数是怎么实现的,放心大胆地直接使用+,-,*,/进行复数的运算即可,十分方便。对上面的运算符重载函数operator+还可以改写得更简练一些:Complex Complex∷operator + (Complex &c2{return Complex(real+c2.real, imag+c2.imag;}需要说明的是: 运算符被重载后,其原有的功能仍然保留,没有丧失或改变。第10章运算符重载通过运算符重载,扩大了C++已有运算符的作用范围,使之能用于类对象。运算符重载对C++有重要的意义,把运算符重载和类结合起来,可以在C++程序中定义出很有实用意义而使用方便的新的数据类型。运算符重载使C++具有更强大的功能、更好的可扩充性和适应性,这是C++最吸引人的特点之一。第10章运算符重载(1 C++不允许用户自己定义新的运算符,只能对已有的C++运算符进行重载。(2 C++允许重载的运算符C++中绝大部分的运算符允许重载。具体规定见书中表10.1。不能重载的运算符只有5个:. (成员访问运算符.* (成员指针访问运算符∷ (域运算符sizeof(长度运算符?: (条件运算符10.3 重载运算符的规则第10章运算符重载前两个运算符不能重载是为了保证访问成员的功能不能被改变,域运算符和sizeof运算符的运算对象是类型而不是变量或一般表达式,不具重载的特征。(3 重载不能改变运算符运算对象(即操作数的个数。(4 重载不能改变运算符的优先级别。(5 重载不能改变运算符的结合性。(6 重载运算符的函数不能有默认的参数,否则就改变了运算符参数的个数,与前面第(3点矛盾。(7 重载的运算符必须和用户定义的自定义类型的对象一起使用,其参数至少应有一个是类对象(或类对象的引用。也就是说,参数不能全部是C++的标准类型,以防止用户修改用于标准类型数据的运算符的性质。第10章运算符重载(8 用于类对象的运算符一般必须重载,但有两个例外,运算符“=”和“&”不必用户重载。① 赋值运算符(=可以用于每一个类对象,可以利用它在同类对象之间相互赋值。② 地址运算符&也不必重载,它能返回类对象在内存中的起始地址。(9 应当使重载运算符的功能类似于该运算符作用于标准类型数据时所实现的功能。(10 运算符重载函数可以是类的成员函数(如例10.2,也可以是类的友元函数,还可以是既非类的成员函数也不是友元函数的普通函数。第10章运算符重载在本章例10.2程序中对运算符“+”进行了重载,使之能用于两个复数的相加。在该例中运算符重载函数operator+作为Complex类中的成员函数。“+”是双目运算符,为什么在例10.2程序中的重载函数中只有一个参数呢?实际上,运算符重载函数有两个参数,由于重载函数是Complex类中的成员函数,有一个参数是隐含的,运算符函数是用this指针隐式地访问类对象的成员。10.4 运算符重载函数作为类成员函数和友元函数第10章运算符重载可以看到,重载函数operator+访问了两个对象中的成员,一个是this指针指向的对象中的成员,一个是形参对象中的成员。如this->real+c2.real,this->real就是c1.real。在10.2节中已说明,在将运算符函数重载为成员函数后,如果出现含该运算符的表达式,如c1+c2,编译系统把它解释为c1.operator+(c2即通过对象c1调用运算符重载函数,并以表达式中第二个参数(运算符右侧的类对象c2作为函数实参。运算符重载函数的返回值是Complex 类型,返回值是复数c1和c2之和(Complex(c1.real +c2.real,c1.imag+c2.imag。第10章运算符重载运算符重载函数除了可以作为类的成员函数外,还可以是非成员函数。可以将例10.2改写为例10.3。例10.3 将运算符“+”重载为适用于复数加法,重载函数不作为成员函数,而放在类外,作为Complex类的友元函数。#includeusing namespace std;class Complex{public:Complex( {real=0;imag=0;}Complex(double r,double i{real=r;imag=i;}friend Complex operator + (Complex &c1,Complex &c2;//重载函数作为友元函数void display( ;private:double real;double imag;第10章运算符重载};Complex operator + (Complex &c1,Complex &c2 //定义作为友元函数的重载函数{return Complex(c1.real+c2.real, c1.imag+c2.imag;}void Complex∷display({cout<<″(″< ″ , ″ < ″ i ″ << endl ;}int main({Complex c1(3,4,c2(5,-10,c3;c3=c1+c2;cout<<″c1=″; c1.display( ;cout<<″c2=″; c2.display( ;cout<<″c1+c2 =″; c3.display( ;}第10章运算符重载与例10.2相比较,只作了一处改动,将运算符函数不作为成员函数,而把它放在类外,在Complex类中声明它为友元函数。同时将运算符函数改为有两个参数。在将运算符“+”重载为非成员函数后,C++编译系统将程序中的表达式c1+c2解释为operator+(c1,c2即执行c1+c2相当于调用以下函数:Complex operator + (Complex &c1,Complex &c2{return Complex(c1.real+c2.real, c1.imag+c2.imag;}求出两个复数之和。运行结果同例10.2。为什么把运算符函数作为友元函数呢?因为运算符函数要访问Complex 类对象中的成员。如果运算符函数不是Complex类的友元函数,而是一个普通的函数,它是没有权利访问Complex类的私有成员的。第10章运算符重载在10.2节中曾提到过: 运算符重载函数可以是类的成员函数,也可以是类的友元函数,还可以是既非类的成员函数也不是友元函数的普通函数。现在分别讨论这3种情况。首先,只有在极少的情况下才使用既不是类的成员函数也不是友元函数的普通函数,原因是上面提到的,普通函数不能直接访问类的私有成员。在剩下的两种方式中,什么时候应该用成员函数方式,什么时候应该用友元函数方式?二者有何区别呢?如果将运算符重载函数作为成员函数,它可以通过this指针自由地访问本类的数据成员,因此可以少写一个函数的参数。但必须要求运算表达式第一个参数(即运算符左侧的操作数是一个类对象,第10章运算符重载而且与运算符函数的类型相同。因为必须通过类的对象去调用该类的成员函数,而且只有运算符重载函数返回值与该对象同类型,运算结果才有意义。在例10.2中,表达式c1+c2中第一个参数c1是Complex类对象,运算符函数返回值的类型也是Complex,这是正确的。如果c1不是Complex类,它就无法通过隐式this指针访问Complex类的成员了。如果函数返回值不是Complex类复数,显然这种运算是没有实际意义的。如想将一个复数和一个整数相加,如c1+i,可以将运算符重载函数作为成员函数,如下面的形式:Complex Complex∷operator+(int &i //运算符重载函数作为Complex 类的成员函数{return Complex(real+i,imag;}第10章运算符重载注意在表达式中重载的运算符“+”左侧应为Complex类的对象,如c3=c2+i;不能写成c3=i+c2; //运算符“+”的左侧不是类对象,编译出错如果出于某种考虑,要求在使用重载运算符时运算符左侧的操作数是整型量(如表达式i+c2,运算符左侧的操作数i是整数,这时是无法利用前面定义的重载运算符的,因为无法调用i.operator+函数。可想而知,如果运算符左侧的操作数属于C++标准类型(如int或是一个其他类的对象,则运算符重载函数不能作为成员函数,只能作为非成员函数。如果函数需要访问类的私有成员,则必须声明为友元函数。可以在Complex类中声明:第10章运算符重载friend Complex operator+(int &i,Complex &c; //第一个参数可以不是类对象在类外定义友元函数:Complex operator+(int &i, Complex &c //运算符重载函数不是成员函数{return Complex(i+c.real,c.imag;}将双目运算符重载为友元函数时,在函数的形参表列中必须有两个参数,不能省略,形参的顺序任意,不要求第一个参数必须为类对象。但在使用运算符的表达式中,要求运算符左侧的操作数与函数第一个参数对应,运算符右侧的操作数与函数的第二个参数对应。如c3=i+c2; //正确,类型匹配c3=c2+i; //错误,类型不匹配第10章运算符重载请注意,数学上的交换律在此不适用。如果希望适用交换律,则应再重载一次运算符“+”。如Complex operator+(Complex &c, int &i //此时第一个参数为类对象{return Complex(i+c.real,c.imag;}这样,使用表达式i+c2和c2+i都合法,编译系统会根据表达式的形式选择调用与之匹配的运算符重载函数。可以将以上两个运算符重载函数都作为友元函数,也可以将一个运算符重载函数(运算符左侧为对象名的作为成员函数,另一个(运算符左侧不是对象名的作为友元函数。但不可能将两个都作为成员函数,原因是显然的。C++规定,有的运算符(如赋值运算符、下标运算符、函数调用运算符必须定义为类的成员函数,有的运算符则不能定义为类的成员函数(如流插入“<<”和流提取运算符“>>”、类型转换运算符。第10章运算符重载由于友元的使用会破坏类的封装,因此从原则上说,要尽量将运算符函数作为成员函数。但考虑到各方面的因素,一般将单目运算符重载为成员函数,将双目运算符重载为友元函数。在学习了本章第10.7节例10.9的讨论后,读者对此会有更深入的认识。说明: 有的C++编译系统(如Visual C++ 6.0没有完全实现C++标准,它所提供不带后缀.h的头文件不支持把成员函数重载为友元函数。上面例10.3程序在GCC中能正常运行,而在Visual C++ 6.0中会编译出错。但是Visual C++所提供的老形式的带后缀.h的头文件可以支持此项功能,因此可以将程序头两行修改如下,即可顺利运行:#include以后如遇到类似情况,亦可照此办理。第10章运算符重载双目运算符(或称二元运算符是C++中最常用的运算符。双目运算符有两个操作数,通常在运算符的左右两侧,如3+5,a=b,i<10等。在重载双目运算符时,不言而喻在函数中应该有两个参数。下面再举一个例子说明重载双目运算符的应用。10.5 重载双目运算符第10章运算符重载例10.4 定义一个字符串类String,用来存放不定长的字符串,重载运算符“==”,“<”和“>”,用于两个字符串的等于、小于和大于的比较运算。为了使读者便于理解程序,同时也使读者了解建立程序的步骤,下面分几步来介绍编程过程。(1 先建立一个String类:#includeusing namespace std;class String{public:String( {p=NULL;} //默认构造函数String(char *str; //构造函数void display( ;private:char *p;//字符型指针,用于指向字符串};第10章运算符重载String∷String(char *str //定义构造函数{p=str;} //使p指向实参字符串void String∷display( //输出p所指向的字符串{cout<int main({String string1(″Hello″,string2(″Book″;string1.display( ;cout<string2.display( ;return 0;}运行结果为HelloBook第10章运算符重载(2 有了这个基础后,再增加其他必要的内容。现在增加对运算符重载的部分。为便于编写和调试,先重载一个运算符“>”。程序如下:#include#includeusing namespace std;class String{public:String( {p=NULL;}String(char *str;friend bool operator>(String &string1,String &string2;//声明运算符函数为友元函数void display( ;private:char *p;//字符型指针,用于指向字符串};String∷String(char *str{p=str;}第10章运算符重载void String∷display( //输出p所指向的字符串{cout<bool operator>(String &string1,String &string2//定义运算符重载函数{if(strcmp(string1.p,string2.p>0return true;else return false;}int main({String string1(″Hello″,string2(″Book″;cout<<(string1>string2<<endl;}程序运行结果为1。这只是一个并不很完善的程序,但是,已经完成了实质性的工作了,运算符重载成功了。其他两个运算符的重载如法炮制即可。第10章运算符重载(3 扩展到对3个运算符重载。在String类体中声明3个成员函数:friend bool operator> (String &string1, String &string2;friend bool operator< (String &string1, String &string2;friend bool operator==(String &string1, String& string2;在类外分别定义3个运算符重载函数:bool operator>(String &string1,String &string2 //对运算符“>”重载{if(strcmp(string1.p,string2.p>0return true;elsereturn false;}bool operator<(String &string1,String &string2 //对运算符“<”重载{if(strcmp(string1.p,string2.p<0return true;else第10章运算符重载return false;}bool operator==(String &string1,String &string2 //对运算符“==”重载{if(strcmp(string1.p,string2.p==0return true;elsereturn false;}再修改主函数:int main({String string1(″Hello″,string2(″Book″,string3(″Computer″; cout<<(string1>string2<<endl;//比较结果应该为truecout<<(string1 endl ; //比较结果应该为falsecout<<(string1==string2<<endl; //比较结果应该为falsereturn 0;}第10章运算符重载运行结果为1结果显然是对的。到此为止,主要任务基本完成。(4 再进一步修饰完善,使输出结果更直观。下面给出最后的程序。#includeusing namespace std;class String{public:String( {p=NULL;}String(char *str;friend bool operator>(String &string1,String &string2;friend bool operator<(String &string1,String &string2;friend bool operator==(String &string1,String &string2;第10章运算符重载void display( ;private:char *p;};String∷String(char *str{p=str;}void String∷display( //输出p所指向的字符串{cout<bool operator>(String &string1,String &string2 {if(strcmp(string1.p,string2.p>0return true;elsereturn false;}bool operator<(String &string1,String &string2第10章运算符重载{if(strcmp(string1.p,string2.p<0return true;elsereturn false;}bool operator==(String &string1,String &string2 {if(strcmp(string1.p,string2.p==0return true;elsereturn false;}void compare(String &string1,String &string2{if(operator>(string1,string2==1{string1.display( ;cout<<″>″;string2.display( ;}elseif(operator<(string1,string2==1第10章运算符重载{string1.display( ;cout<<″<″;string2.display( ;}elseif(operator==(string1,string2==1{string1.display( ;cout<<″=″;string2.display( ;}cout<}int main({Stringstring1(″Hello″,string2(″Book″,string3(″Computer″,string4(″H ello″;compare(string1,string2;compare(string2,string3;compare(string1,string4;return 0;}运行结果为Hello>BookBookHello==Hello第10章运算符重载增加了一个compare函数,用来对两个字符串进行比较,并输出相应的信息。这样可以减轻主函数的负担,使主函数简明易读。通过这个例子,不仅可以学习到有关双目运算符重载的知识,而且还可以学习怎样去编写C++程序。这种方法的指导思想是: 先搭框架,逐步扩充,由简到繁,最后完善。边编程,边调试,边扩充。千万不要企图在一开始时就解决所有的细节。类是可扩充的,可以一步一步地扩充它的功能。最好直接在计算机上写程序,每一步都要上机调试,调试通过了前面一步再做下一步,步步为营。这样编程和调试的效率是比较高的。读者可以试验一下。第10章运算符重载单目运算符只有一个操作数,如!a,-b,&c,*p,还有最常用的++i和--i等。重载单目运算符的方法与重载双目运算符的方法是类似的。但由于单目运算符只有一个操作数,因此运算符重载函数只有一个参数,如果运算符重载函数作为成员函数,则还可省略此参数。下面以自增运算符“++”为例,介绍单目运算符的重载。10.6 重载单目运算符第10章运算符重载例10.5 有一个Time类,包含数据成员minute(分和sec(秒,模拟秒表,每次走一秒,满60秒进一分钟,此时秒又从0开始算。要求输出分和秒的值。#includeusing namespace std;class Time{public:Time( {minute=0;sec=0;} //默认构造函数Time(int m,int s:minute(m,sec(s{ } //构造函数重载Time operator++( ; //声明运算符重载函数void display( {cout< ″ : ″ < //定义输出时间函数private:int minute;int sec;};Time Time∷operator++( //定义运算符重载函数{if(++sec>=60第10章运算符重载{sec-=60; //满60秒进1分钟++minute;}return *this; //返回当前对象值}int main({Time time1(34,0;for (int i=0;i<61;i++{++time1;time1.display( ;}return 0;}运行情况如下:34:134:2┆34:5935:035:1 (共输出61行第10章运算符重载可以看到: 在程序中对运算符“++”进行了重载,使它能用于Time类对象。“++”和“--”运算符有两种使用方式,前置自增运算符和后置自增运算符,它们的作用是不一样的,在重载时怎样区别这二者呢?针对“++”和“--”这一特点,C++约定: 在自增(自减运算符重载函数中,增加一个int型形参,就是后置自增(自减运算符函数。例10.6 在例10.5程序的基础上增加对后置自增运算符的重载。修改后的程序如下:#includeusing namespace std;class Time{public:Time( {minute=0;sec=0;}Time(int m,int s:minute(m,sec(s{}第10章运算符重载Time operator++( ;//声明前置自增运算符“++”重载函数Time operator++(int;//声明后置自增运算符“++”重载函数void display( {cout< ″ : ″ <private:int minute;int sec;};Time Time∷operator++( //定义前置自增运算符“++”重载函数{if(++sec>=60{sec-=60;++minute;}return *this;//返回自加后的当前对象}Time Time∷operator++(int//定义后置自增运算符“++”重载函数{Time temp(*this;sec++;第10章运算符重载if(sec>=60{sec-=60;++minute;}return temp; //返回的是自加前的对象}int main({Time time1(34,59,time2;cout<<″ time1 : ″;time1.display( ;++time1;cout<<″++time1: ″;time1.display( ;time2=time1++; //将自加前的对象的值赋给time2cout<<″time1++: ″;time1.display( ;cout<<″ time2 :″;time2.display( ; //输出time2对象的值}第10章运算符重载请注意前置自增运算符“++”和后置自增运算符“++”二者作用的区别。前者是先自加,返回的是修改后的对象本身。后者返回的是自加前的对象,然后对象自加。请仔细分析后置自增运算符重载函数。运行结果如下:time1 : 34:59(time1原值++time1: 35:0 (执行++time1后time1的值time1++: 35:1 (再执行time1++后time1的值time2 : 35:0 (time2保存的是执行time1++前time1的值可以看到: 重载后置自增运算符时,多了一个int型的参数,增加这个参数只是为了与前置自增运算符重载函数有所区别,此外没有任何作用。编译系统在遇到重载后置自增运算符时,会自动调用此函数。第10章运算符重载C++的流插入运算符“<<”和流提取运算符“>>”是C++在类库中提供的,所有C++编译系统都在类库中提供输入流类istream和输出流类ostream。cin和cout分别是istream类和ostream类的对象。在类库提供的头文件中已经对“<<”和“>>”进行了重载,使之作为流插入运算符和流提取运算符,能用来输出和输入C++标准类型的数据。因此,在本书前面几章中,凡是用“cout<<”和“cin>>”对标准类型数据进行输入输出的,都要用#include 把头文件包含到本程序文件中。10.7 重载流插入运算符和流提取运算符第10章运算符重载用户自己定义的类型的数据,是不能直接用“<<”和“>>”来输出和输入的。如果想用它们输出和输入自己声明的类型的数据,必须对它们重载。对“<<”和“>>”重载的函数形式如下:istream & operator >> (istream &,自定义类 &;ostream & operator << (ostream &,自定义类 &;即重载运算符“>>”的函数的第一个参数和函数的类型都必须是istream&类型,第二个参数是要进行输入操作的类。重载“<<”的函数的第一个参数和函数的类型都必须是ostream&类型,第二个参数是要进行输出操作的类。因此,只能将重载“>>”和“<<”的函数作为友元函数或普通的函数,而不能将它们定义为成员函数。第10章运算符重载在程序中,人们希望能用插入运算符“<<”来输出用户自己声明的类的对象的信息,这就需要重载流插入运算符“<<”。例10.7 在例10.2的基础上,用重载的“<<”输出复数。#includeusing namespace std;class Complex{public:Complex( {real=0;imag=0;}Complex(double r,double i{real=r;imag=i;}Complex operator + (Complex &c2; //运算符“+”重载为成员函数friend ostream& operator << (ostream&,Complex&; //运算符“<<”重载为友元函数private:10.7.1 重载流插入运算符“<<”第10章运算符重载double real;double imag;};Complex Complex∷operator + (Complex &c2//定义运算符“+”重载函数{return Complex(real+c2.real,imag+c2.imag;}ostream& operator << (ostream& output,Complex& c //定义运算符“<<”重载函数{output<<″(″< ″ + ″ < ″ i ″ << endl ;return output;}int main({Complex c1(2,4,c2(6,10,c3;c3=c1+c2;cout<return 0;}第10章运算符重载(在Visual C++ 6.0环境下运行时,需将第一行改为#include , 并删去第2行。运行结果为(8+14i可以看到在对运算符“<<”重载后,在程序中用“<<”不仅能输出标准类型数据,而且可以输出用户自己定义的类对象。用“cout< 3” 即能以复数形式输出复数对象 c3 的值。形式直观 , 可读性好 , 易于使用。下面对怎样实现运算符重载作一些说明。程序中重载了运算符“<<”,运算符重载函数中的形参output是ostream类对象的引用,形参名output是用户任意起的。分析main函数最后第二行:cout<第10章运算符重载运算符“<<”的左面是cout,前面已提到cout是ostream类对象。“<<”的右面是c3,它是Complex类对象。由于已将运算符“<<”的重载函数声明为Complex类的友元函数,编译系统把“cout< 3” 解释为operator<<(cout,c3即以cout和c3作为实参,调用下面的operator<<函数:ostream& operator<<(ostream& output,Complex& c{output<<″(″< ″ + ″ < ″ i ″ << endl ;return output;}调用函数时,形参output成为cout的引用,形参c成为c3的引用。因此调用函数的过程相当于执行:cout<<″(″< ″ + ″ < ″ i ″ << endl ; return cout ;第10章运算符重载请注意: 上一行中的“<<”是C++预定义的流插入符,因为它右侧的操作数是字符串常量和double类型数据。执行cout语句输出复数形式的信息。然后执行return语句。请思考: return output的作用是什么?回答是能连续向输出流插入信息。output是ostream类的对象,它是实参cout的引用,也就是cout通过传送地址给output,使它们二者共享同一段存储单元,或者说output是cout的别名。因此,return output就是return cout,将输出流cout的现状返回,即保留输出流的现状。请问返回到哪里?刚才是在执行cout<在已知cout< 的返回值是 cout 的当前值。如果有以下输出 :第10章运算符重载cout<先处理cout< 即(cout<而执行(cout< 得到的结果就是具有新内容的流对象 cout, 因此 ,(cout< 相当于 cout( 新值 < 。运算符“<<” 左侧是 ostream 类对象 cout, 右侧是 Complex 类对象 c2, 则再次调用运算符“<<” 重载函数 , 接。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
★ 归纳 ◆ 函数operator+重载了运算符+ ◆ 在执行复数相加的表达式c1+c2时,系统就会调用 operator+函数,把c1和c2作为实参,与形参进行 虚实结合
二、 运算符重载的方法
1、 典例分析
★
例10.2:改写例10.1,重载运算符“+”,
使之能用于复数的相加
#include <iostream>
可以将运算符重载函数作为成员函数,如下面
的形式 ◆ Complex Complex::operator + (int &i) { return Complex(real+i;image);}
★ 注意:在表达式中重载的运算符“+”左侧应为
Complex类的对象,如:
◆ c3=c2+i; ◆ 不能写成:c3=i+c2; // 运算符的左侧不是类对象,编译出错
cout<<“c1+c2=”;c3.display();
return 0;}
★ 问题:“+”是双目运算符,为什么该重载函数只 有 一个参数呢? ◆ 实际上,该运算符重载函数有两个参数
◆ 由于重载函数是Complex类中的成员函数,有一
个参数是隐含的,运算符函数用this指针隐式地
访问类对象的成员
★ 详细分析 ◆ 在将运算符函数重载为成员函数后,如果出现含 该运算符的表达式,如c1+c2,编译系统把它解释 为c1.operator+(c2) ◆ 即通过对象c1调用运算符重载函数,并以表达式 中的第二个参数作为函数实参
◆
将运算符“+”重载为适用于复数加法,
但重载函数不作为成员函数,而是放在
类外,作为Complex的友元函数
#include <iostream>
using namespace std;
class Complex
{ public: Complex(){ real=0,image=0 }; Complex(double r,double i) { real=r,image=i}; // 构造函数的重载
void display();
protected:
double real; // 实部
double image;
};
// 虚部
Complex Complex::operator +(Complex &c2) { Complex c; c.real=real+c2.real; c.image=image+c2.image;
4、 重载既不能改变运算符的优先级别, 也不能改变运算符的结合性 ★ ★ 优先级别:比如先乘除、后加减 结合性:如赋值运算符是右结合性
第四节 运算符重载函数作为 类成员函数和友元函数
一、 运算符重载函数作为类成员函数
★
典例分析:例10.2
二、 运算符重载函数可以是非成员 函数
★ 典例分析:例10.3
2、 C++允许重载的运算符
★
C++绝大部分的运算符允许重载,
不能重载运算符只有5个(P319)
第三节 运算符重载的规则
3、 重载不能改变运算符运算对象(即操作 对象)的个数 ★ ★ 概念:双目运算符和单目运算符 双目运算符在重载以后仍为双目运算符 (如关系运算符>和<)
第三节 运算符重载的规则
2、 问题:用户能否根据自己的需要对C++ 已提供的运算符进行重载? ★ ★ 比如:能否用“+”实现两个复数的相加 典例10.1(未使用运算符重载)
#include <iostream>
using namespace std;
class Complex
{ public: Complex(){ real=0,image=0 }; Complex(double r,double i) { real=r,image=i }; // 构造函数的重载 Complex complex_add(Complex &c2); void display();
//定义三个复数对象
c3=c1+c2; // 运算符+用于复数运算 c1.display(); c2.display();
cout<<“c1=”; cout<<“c2=”;
cout<<“c1+c2=”;c3.display();
return 0;}
★ 在将运算符函数重载为非成员函数后,C++编译
系统将程序中的表达式c1+c2解释为:
(Complex &c1,Complex &c2)
{ return Complex(c1.real+c2.real;
c1.image+c2.image);} void Complex::display() { ...... }
int main()
{ Complex c1(3,4),c2(5,-10),c3;
using namespace sБайду номын сангаасd;
class Complex
{ public: Complex(){ real=0,image=0 }; Complex(double r,double i) { real=r,image=I }; // 构造函数的重载 Complex operator +(Complex &c2); // 声明重载运算符的“+”函数
return 0;}
二、运算符的重载
3、 归纳
★ ★
结果无疑是正确的 但调用方式很不直观,太繁琐,使人
感到不方便
第二节 运算符重载的方法
一、 运算符重载的定义
1、 运算符重载是通过定义函数实现的 ★ 在需要执行被重载的运算符时,系统就
自动调用该函数(运算符重载的实质是 函数的重载)
一、 运算符重载的定义
1、 运算符也可以重载
★
实际上,我们已经在不知不觉中使用了
运算符的重载
★ 例如:大家都已习惯于用加法运算符“+”对整数、
单精度数和双精度数进行加法运算
如:5+8、5.8+3.7等等 ◆ 其实计算机对整数、单精度数和双精度数的加法 过程很不相同 ◆ 原因:C++已经对运算符“+”进行了重载
二、运算符的重载
operator+(c1,c2) ◆ 即执行c1+c2相当于调用以下函数: Complex operator + (Complex &c1,Complex &c2) { return Complex(c1.real+c2.real;
c1.image+c2.image);}
三、 归纳
1、 运算符重载函数可以是类的成员函数, 也可以是类的友元函数,还可以是既
friend Complex operator +
(Complex &c1,Complex &c2);
// 重载函数为友元函数
void display(); protected: double real; double image; }; // 实部 // 虚部
Complex Complex::operator +
return c;}
Void complex::display()
int main()
{ Complex c1(3,4),c2(5,-10),c3;
//定义三个复数对象
c3=c1+c2; // 运算符+用于复数运算 c1.display(); c2.display();
cout<<“c1=”; cout<<“c2=”;
2、 运算符重载的一般格式
★
函数类型 operator 运算符名称
(形参表列)
{ 对运算符的重载处理}
★ 典例:想将“+”用于Complex类(复数)的加法
运算,函数的原型可以是这样的:
◆ Complex operator +(Complex c1, Complex c2); ◆ 注意:函数名是由关键字operator和运算符组成
二、 运算符重载的方法
2、 归纳
★
通过运算符重载,扩大了C++已有运算
符的作用范围,使之能用于类对象
★
使C++具有更强大的功能,更好的可扩 充性和适应性
第三节 运算符重载的规则
1、 C++不允许用户自己定义新的运算符, 只能够对已有的C++运算符进行重载 ★ 例如:不能将**定义为幂运算符
第三节 运算符重载的规则
int main()
{ Complex c1(3,4),c2(5,-10),c3;
//定义三个复数对象
c3=plex_add(c2); //调用复数相加函数 cout<<“c1=”; cout<<“c2=”; c1.display(); c2.display();
cout<<“c1+c2=”;c3.display();
非类的成员函数也不是类的友元函数
的普通函数 ★ 后者问题在于:不能访问类的私有成员
三、 归纳
2、 如果将运算符重载函数作为成员函数, 它可以通过 this指针自由地访问本类的
数据成员,因此,可以少写一个函数的
参数 ★ 但要求运算表达式的第一个参数是一个 类对象,而且与运算符函数的类型相同
★ 例如:欲将一个复数和一个整数相加,如c1+i,