一. 概述:
1. 运算符重载的实质: 运算符重载的实质就是函数重载。在实现过程中,首先把指定的运算表达式转化为对运算符函数的调用,运算对象转化为运算符函数的实参,
然后根据实参的类型来确定需要调用的函数,这个过程是在编译过程中完成的。
2. 运算符重载的规则:
(1)C++中的运算符除了少数几个以外,全部可以重载,而且只能重载已有的这些运算符。
(2)重载之后运算符的优先级和结合性都不会改变。
(3)运算符重载是针对新类型数据的实际需要,对原有运算符进行适当的改造。
3. C++可以重载的运算符:
C++不可以重载的运算符:
*注意:本文中的例程都是在VC6.0下编译和运行的。
二. 实现:
运算符的重载形式有两种:重载为类的成员函数和重载为类的友元函数。
1. 运算符重载为类的成员函数的语法形式如下:
<函数类型> operator <运算符>(<形参表>)
{
<函数体>;
}
2. 运算符重载为类的友元函数的语法形式如下:
friend <函数类型> operator <运算符>(<形参表>)
{
<函数体>;
}
3. 举例:
分别用两种方式实现time类,用来保存时间(时、分、秒),通过重载操作符“+”实现两个时间的相加。
(1)用类的成员函数来实现:
功能:定义一个Time类用来保存时间(时、分、秒),通过重载操作符“+”实现两个时间的相加。
/*例1:定义一个Time类用来保存时间(时、分、秒),通过重载操作符“+”实现两个时间的相加。*/
/* IDE:VC6.0 */
#include <stdio.h>
class Time
{
public:
Time(){ hours=0;minutes=0;seconds=0;} //无参构造函数
Time(int h, int m,int s) //重载构造函数
{
hours=h; minutes=m; seconds=s;
}
Time operator +(Time&); //操作符重载为成员函数,返回结果为Time类
void gettime();
private:
int hours,minutes,seconds;
};
Time Time::operator +(Time& time)
{
int h,m,s;
s=time.seconds+seconds;
m=time.minutes+minutes+s/60;
h=time.hours+hours+m/60;
Time result(h,m%60,s%60);
return result;
}
void Time::gettime()
{
printf("%d:%d:%d/n",hours,minutes,seconds);
}
int main( )
{
Time t1(8,51,40),t2(4,15,30),t3;
t3=t1+t2;
t3.gettime();
return 0;
}
运行结果:
13:7:10
(2)用类的友元函数来实现:
/*例2:对例1进行修改,采用操作符重载为友元函数实现。 */
/* IDE:VC6.0 */
#include <stdio.h>
class Time
{
public:
Time(){ hours=0;minutes=0;seconds=0;} //无参构造函数
Time(int h, int m,int s) //重载构造函数
{
hours=h; minutes=m; seconds=s;
}
friend Time operator +(Time&,Time&); //重载运算符为友元函数形式。友元不是类成员,所以,这里
//要用两个参数,返回的结果是这两个参数的和。
void gettime( );
private:
int hours,minutes,seconds;
};
Time operator +(Time& time1,Time& time2)
{
int h,m,s;
s=time1.seconds+time2.seconds; //计算秒数
m=time1.minutes+time2.minutes+s/60; //计算分数
h=time1.hours+time2.hours+m/60; //计算小时数
Time result(h,m%60,s%60);
return result;
}
void Time::gettime( )
{
printf("%d:%d:%d/n",hours,minutes,seconds);
}
int main( )
{
Time t1(8,51,40),t2(4,15,30),t3;
t3=t1+t2; //调用友元函数
t3.gettime( );
return 0;
}
运行结果:
13:7:10
三. 一元运算符重载
类的单目运算符可重载为一个没有参数的非静态成员函数或者带有一个参数的非成员函数,参数必须是用户自定义类型的对象或者是对该对象的引用。
在C++中,单目运算符有++和--,它们是变量自动增1和自动减1的运算符。在类中可以对这两个单目运算符进行重载。
如同“++”运算符有前缀和后缀两种使用形式一样,“++”和“--”重载运算符也有前缀和后缀两种运算符重载形式,以“++”重载运算符为例,其语法格式如下:
<函数类型> operator ++(); //前缀运算
<函数类型> operator ++(int); //后缀运算
使用前缀运算符的语法格式如下:
++<对象>;
使用后缀运算符的语法格式如下:
<对象>++;
/*例3:友元函数重载示例,Increase的定义。*/
/* IDE:VC6.0 */
#include <stdio.h>
class Increase{
public:
Increase(int x):value(x){}
friend Increase & operator++(Increase & ); //前增量
friend Increase operator++(Increase &,int); //后增量
void display(){ printf("the value is %d /n", value); }
private:
int value;
};
Increase & operator++(Increase & a)
{
a.value++; //前增量
return a; //再返回原对象
}
Increase operator++(Increase& a, int)
{
Increase temp(a); //通过拷贝构造函数保存原有对象值
a.value++; //原有对象增量修改
return temp; //返回原有对象值
}
int main()
{
Increase n(20);
n.display();
(n++).display(); //显示临时对象值
n.display(); //显示原有对象
++n;
n.display();
++(++n);
n.display();
(n++)++; //第二次增量操作对临时对象进行
n.display();
return 0;
}
运行结果:
the value is 20
the value is 20
the value is 21
the value is 22
the value is 24
the value is 25
四. 二元运算符重载:
1. 对于双目运算符,一个运算数是对象本身的数据,由this指针给出,另一个运算数则需要通过运算符重载函数的参数表来传递。下面分别介绍这两种情况。
2. 对于双目运算符B,如果要重载B为类的成员函数,使之能够实现表达式“oprd1 B oprd2”,其中oprd1为A类的对象,则应当把B重载为A类的成员函数,该函数只有一个形参,形参的类型是oprd2所属的类型。
经过重载之后,表达式oprd1 B oprd2就相当于函数调用“oprd1.operator B(oprd2)”。
/*例4:设计一个点类Point,实现点对象之间的各种运算。*/
/* IDE:VC6.0 */
#include <stdio.h>
#include <iostream.h>
class Point
{
int x,y;
public:
Point() {x=y=0;}
Point(int i,int j) {x=i;y=j;}
Point(Point &);
~Point() {}
void offset(int,int); //提供对点的偏移
void offset(Point); //重载,偏移量用Point类对象表示
bool operator==(Point); //运算符重载,判断两个对象是否相同
bool operator!=(Point); //运算符重载,判断两个对象是否不相同
void operator+=(Point); //运算符重载,将两个点对象相加
void operator-=(Point); //运算符重载,将两个点对象相减
Point operator+(Point); //运算符重载,相加并将结果放在左操作数中
Point operator-(Point); //运算符重载,相减并将结果放在左操作数中
int getx() {return x;}
int gety() {return y;}
void disp()
{
cout<<"(" <<x <<"," <<y <<")" <<endl;
}
};
Point::Point(Point &p)
{
x=p.x;y=p.y;
}
void Point::offset(int i,int j)
{
x+=i;y+=j;
}
void Point::offset(Point p)
{
x+=p.getx();y+=p.gety();
}
bool Point::operator==(Point p)
{
if(x==p.getx() &&y==p.gety())
return 1;
else
return 0;
}
bool Point::operator!=(Point p)
{
if(x!=p.getx() ||y!=p.gety())
return 1;
else
return 0;
}
void Point::operator+=(Point p)
{
x+=p.getx();y+=p.gety();
}
void Point::operator-=(Point p)
{
x-=p.getx();y-=p.gety();
}
Point Point::operator+(Point p)
{
this->x+=p.x; this->y+=p.y;
return *this;
}
Point Point::operator-(Point p)
{
this->x-=p.x; this->y-=p.y;
return *this;
}
int main()
{
Point p1(2,3),p2(3,4),p3(p2);
printf("1:");
p3.disp();
p3.offset(10,10);
printf("2:");
p3.disp();
printf("3:/n", p2==p3);
printf("4:/n", p2!=p3);
p3+=p1;
printf("5:");
p3.disp();
p3-=p2;
printf("6:");
p3.disp();
p3=p1+p3; //先将p1+p3的结果放在p1中,然后赋给p3
printf("7:");
p3.disp();
p3=p1-p2;
printf("8:");
p3.disp();
return 0;
}
运行结果:
1:(3,4)
2:(13,14)
3:
4:
5:(15,17)
6:(12,13)
7:(14,16)
8:(11,12)
五. 特殊运算符重载
1. 赋值运算符重载
(1).运算符“+=”和“-=”的重载
对于标准数据类型,“+=”和“-=”的作用是将一个数据与另一个数据进行加法或减法运算后再将结果回送给赋值号左边的变量中。对它们重载后,使其实现其他相关的功能。
(2).运算符“=”的重载
赋值运算符“=”的原有含义是将赋值号右边表达式的结果拷贝给赋值号左边的变量,通过运算符“=”的重载将赋值号右边对象的私有数据依次拷贝到赋值号左边对象的私有数据中。
/*例5:“+="和“-="运算符重载。*/
/* IDE:VC6.0 */
#include <iostream.h>
class Vector
{
int x,y;
public:
Vector() { };
Vector(int x1,int y1) {x=x1;y=y1;}
friend Vector operator +=(Vector v1,Vector v2)
{
v1.x+=v2.x;
v1.y+=v2.y;
return v1;
}
Vector operator-=(Vector v)
{
Vector tmp;
tmp.x=x-v.x;
tmp.y=y-v.y;
return tmp;
}
void display()
{
cout<<"("<<x<<","<<y<<")"<<endl;
}
};
void main()
{
Vector v1(6,8),v2(3,6),v3,v4;
cout<<"v1=";
v1.display();
cout<<"v2=";
v2.display();
v3=v1+=v2;
cout<<"v3=";
v3.display();
v4=v1-=v2;
cout<<"v4=";
v4.display();
}
运行结果:
v1=(6,8)
v2=(3,6)
v3=(9,14)
v4=(3,2)
/*例6:"="运算符重载。*/
/* IDE:VC6.0 */
#include <iostream.h>
class Sample
{
int n;
public:
Sample() { }
Sample(int i) {n=i;}
Sample & operator=(Sample);
void disp() {cout<<"n="<<n<<endl;}
};
Sample &Sample::operator=(Sample s)
{
Sample::n=s.n;
return *this;
}
void main()
{
Sample s1(10),s2;
s2=s1;
s2.disp();
}
运行结果:
n=10
2. 下标运算符重载
下标运算符“[ ]”通常用于在数组中标识数组元素的位置,下标运算符重载可以实现数组数据的赋值和取值。下标运算符重载函数只能作为类的成员函数,不能作为类的友元函数。
下标运算符“[ ]”函数重载的一般形式为:
type class_name::operator[ ](arg_);
/*例7:下标运算符的重载。*/
/* IDE:VC6.0 */
#include <iostream.h>
class Demo {
int Vector[5];
public:
Demo() {};
int &operator[ ](int i) {return Vector[i]; }
};
void main()
{
Demo v;
for(int i=0;i<5;i++)
v[i]=i+1;
for(i=0;i<5;i++)
cout<<v[i]<<" ";
cout<<endl;
}
运行结果:
1 2 3 4 5
3. 比较运算符重载
比较运算符(>、<、= =等)重载必须返回真(非0)或假(0)。
/* 例8:对字符串重载运算符"+", ">"。*/
/* IDE:VC6.0 */
#include <stdio.h>
#include <string.h>
class String
{
char name[256];
public:
String(char* str)
{
strcpy(name,str);
}
String(){}
~String(){}
String operator+(const String&); //声明
int operator>(const String&); //声明
void display()
{
printf("The string is: %s/n",name);
}
};
static char* str;
String String::operator+(const String& a) //定义
{
strcpy(str,name);
strcat(str,a.name);
return String(str);
}
int String::operator>(const String& a) //定义
{
return strcmp(name,a.name);
}
int main()
{
str=new char[256];
String demo1("VC++ ");
String demo2("6.0 ");
demo1.display();
demo2.display();
String demo3=demo1+demo2;
demo3.display();
String demo4=demo3+"Programming.";
demo4.display();
printf("/n");
String demo5("VC++ 6.0 ");
printf("demo1 > demo2 ? answer: %d/n", demo1>demo2); // 1
printf("demo2 > demo1 ? answer: %d/n", demo2>demo1); // -1
printf("demo3 > demo5 ? answer: %d/n", demo3>demo5); // 1
delete str;
return 0;
}
运行结果:
The string is : VC++
The string is : 6.0
The string is : VC++ 6.0
The string is : VC++ 6.0 Programming.
demo1 > demo2 ? answer: 1
demo2 > demo1 ? answer: -1
demo3 > demo5 ? answer: 0
4. new与delete运算符重载
new和delete只能被重载为类的成员函数,不能重载为友元。而且,无论是否使用关键字static进行修饰,重载了的new和delete均为类的静态成员函数。
运算符new重载的一般形式为:
void *class_name::operator new(size_t,<arg_list>);
new重载应返回一个无值型的指针,且至少有一个类型为size_t的参数。若该重载带有多于一个的参数,则其第一个参数的类型必须为size_t。
运算符delete重载的一般形式为
void *class_name::operator delete(void *,<size_t>);
/*例9:重载new和delete运算符。*/
/* IDE:VC6.0 */
#include <iostream.h>
#include <stddef.h>
class memmanager{
public:
void *operator new(size_t size);
//分配一块大小为size的内存
void *operator new(size_t size,char tag);
//分配一块大小为size的内存,并且用字符tag赋值
void operator delete(void *p);
//释放指针p所指向的一块内存空间
};
void *memmanager::operator new(size_t size)
{
cout<<"new1 operator"<<endl;
char *s=new char[size]; //分配大小为size的内存空间
*s='a'; //用字符'a'赋值
return s; //返回指针
}
void *memmanager::operator new(size_t size,char tag)
{
cout<<"new2 operator"<<endl;
char *s=new char[size];
*s=tag; //用字符tag赋值
return s; //返回指针
}
void memmanager::operator delete(void *p)
{
cout<<"delete operator"<<endl;
char *s=(char *)p; //强制类型转换
delete[] s; //释放内存空间
}
void main()
{
memmanager *m=new memmanager();
delete m;
memmanager *n=new('B') memmanager();
delete n;
}
运行结果:
new1 operator
delete operator
new2 operator
delete operator
5. 逗号运算符重载
逗号运算符是双目运算符,和其他运算符一样,也可以通过重载逗号运算符来完成期望完成的工作。逗号运算符构成的表达式为“左运算数,右运算数”,该表达式返回右运算数的值。如果用类的成员函数来重载逗号运算符,则只带一个右运算数,而左运算数由指针this提供。
/*例10:逗号运算符重载。*/
/* IDE:VC6.0 */
#include <iostream.h>
#include<malloc.h>
class Point
{
int x,y;
public:
Point() {};
Point(int l,int w)
{
x=l;y=w;
}
void disp()
{
cout<<"Area:" <<x*y<<endl;
}
Point operator,(Point r) // 逗号运算符重载
{
Point temp;
temp.x=r.x;
temp.y=r.y;
return temp;
}
Point operator+(Point r)
{
Point temp;
temp.x=r.x+x;
temp.y=r.y+y;
return temp;
}
};
void main()
{
Point r1(1,2),r2(3,4),r3(5,6);
r1.disp();
r2.disp();
r3.disp();
r1=(r1,r2+r3,r3);
r1.disp();
}
运行结果:
Area:2
Area:12
Area:30
Area:30
6. 类型转换运算符重载
类型转换运算符重载函数的格式如下:
operator <类型名>()
{
<函数体>;
}
与以前的重载运算符函数不同的是,类型转换运算符重载函数没有返回类型,因为<类型名>就代表了它的返回类型,而且也没有任何参数。在调用过程中要带一个对象实参。
/*例11:使用转换函数。*/
/* IDE:VC6.0 */
#include <stdio.h>
class SmallInt {
public:
SmallInt( int ival ) : value( ival ) { }
// 转换操作符
// SmallInt ==> int
operator int() { return value; }
// 没有提供重载操作符
private:
int value;
};
void main()
{
SmallInt si( 3 );
printf("%f",si + 3.14159);
printf("/n");
}
/* IDE:VC6.0 */
#include <stdio.h>
class SmallInt {
public:
SmallInt( int ival ) : value( ival ) { }
// 转换操作符
// SmallInt ==> int
operator int() { return value; }
// 没有提供重载操作符
private:
int value;
};
void main()
{
SmallInt si( 3 );
printf("%f",si + 3.14159);
printf("/n");
}
/*
说明:
上的面程序被解析为如下两步:
1. 调用SmallInt 转换函数产生整型值3
2. 整型值3 被提升为3.0 并与double 文字常量3.14159 相加生成double 型6.14159
运行结果:
6.141590
7. ->运算符重载
“->”运算符是成员访问运算符,这种一元的运算符只能被重载为成员函数,所以也决定了它不能定义任何参数。一般成员访问运算符的典型用法是:
对象->成员
成员访问运算符“->”函数重载的一般形式为:
type class_name::operator->();
/*例12:重载->运算符。 */
/* IDE:VC6.0 */
include <iostream.h>
class pp
{
public:
int n;
float m;
pp *operator->()
{
return this;
}
};
void main()
{
pp t1;
t1->m=10;
cout<<"t1.k is:"<<t1.m<<endl;
cout<<"t1->k is"<<t1->m<<endl;
}
/*例12:重载->运算符。 */
/* IDE:VC6.0 */
include <iostream.h>
class pp
{
public:
int n;
float m;
pp *operator->()
{
return this;
}
};
void main()
{
pp t1;
t1->m=10;
cout<<"t1.k is:"<<t1.m<<endl;
cout<<"t1->k is"<<t1->m<<endl;
}
运行结果:
t1.k is 10
t1->k is 10
8. 函数调用运算符重载
函数调用运算符“()”只能说明成类的非静态成员函数,该函数具有以下的一般形式:
type class_name::operator()(<arg_list>);
与普通函数一样,重载了的函数调用运算符可以事先带有零个或多个参数,但不得带有缺省的参数。
/*例13:函数调用运算符重载示例。 */
/* IDE:VC6.0 */
#include <iostream.h>
class F
{
public:
double operator ()(double x, double y) const;
};
double F::operator ()(double x, double y) const
{
return (x+5)*y;
}
void main()
{
F f;
cout<<f(1.5, 2.2)<<endl;
}
运行结果:
14.3
9. I/O运算符重载
C++的I/O流库的一个重要特性就是能够支持新的数据类型的输出和输入。用户可以通过对插入符(<<)和提取符(>>)进行重载来支持新的数据类型。
下面通过一个例子讲述重载插入符和提取符的方法。
/*例14:I/O运算符重载。 */
/*例14:I/O运算符重载。 */
/* IDE:VC6.0 */
#include <iostream.h>
class Date
{
public:
Date(int y,int m,int d) {Year=y;Month=m;Day=d;}
friend ostream& operator<<(ostream &stream,Date &date);
friend istream& operator>>(istream &stream,Date &date);
private:
int Year,Month,Day;
};
ostream& operator<<(ostream &stream,Date &date)
{
stream<<date.Year<<"/"<<date.Month<<"/"<<date.Day<<endl;
return stream;
}
istream& operator>>(istream &stream,Date &date)
{
stream>>date.Year>>date.Month>>date.Day;
return stream;
}
void main()
{
Date Cdate(2004,1,1);
cout<<"Current date:"<<Cdate<<endl;
cout<<"Enter new date:";
cin>>Cdate;
cout<<"New date:"<<Cdate<<endl;
}
运行结果:
Current date:2004/1/1
Enter new date:2004 1 15 ↙
New date:2004/1/15
至此,运算符重载介绍完毕。
更多更精彩的例子及注意事项,请参考《C++ Primer》等书籍。