为什么要把这两个东西放在一起论述,是因为经常被同时结合运用。
先说运算符重载,运算符重载类比函数重载,但是比函数重载要复杂一些,也难理解一些,特别是运算符重载在不同参数个数上有不同的解释。
运算符重载分单目运算符和多目运算符的重载,有一些区别.
1.运算符的重载
举个二目运算符的例子:
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;
return c;
}
void complex::display()
{
cout<<"("<<real<<","<<imag<<"i)"<<endl;
}
调用
complex c1(2,3),c2(4,-5),c3;
c3=c1+c2;
c3.display();
输出结果是:
(6,-2i)
实际上调用过程是:
c1.operator+(c2)
运算符重载规则
详细的规则不详说,
说两点需要注意的:
1)有5个不能重载
.(成员访问运算符)
*(指针访问运算符)
::
sizeof
?:
2)重载运算符必须和用户定义的自定义类型的对象一起使用,其参数至少应有一个是类对象(或类对象的引用),也就是说,参数不能全部是c++的标准类型
如:
int operator +(int a,int b)
{
return (a-b);
}
这样是不对的,因为都是c++标准类型
应该这样
complex operator +(int a,complex &c)
{
return complex(a+c.real,c.imag);
}
在这里说明下之前说的,参数个数的问题,在这里是两个参数在调用时应该是这样:
c3=5+c2;
所以之前类中的对+的运算符重载也可以写成两个参数的形式,即:
complex operator +(complex &c1,complex &c2);
class time
{
public:
time(){min=0;sec=0;}
time(int m,int s):min(m),sec(s){}
time operator ++();
void display();
private:
int min;
int sec;
};
time time::operator()
{
if(++sec>=60)
{
sec-=60;
++min;
return *this;
}
}
void time::display()
{
cout<<min<<":"<<sec<<endl;
}
调用:
time t1(34,0)
while(true)
{
++time;
sleep(1);
}
运行情况如下:
34:1
34:2
.
.
.
34:59
35:0
35:1
.
.
.
上述的前置单目运算符,那自加后置运算符如何实现
例子如下:
time time::operator++(int)//参数中用int说明是后置运算符,这是c++约定的规则
{
time temp(* this);
sec++;
if(sec<=60)
{
sec-=60;
++min;
}
return temp;
}
在参数中填写int表示是后置运算符,因为++运算符结合律是从右向左的,所以后置++需要实现先取值,后自增的功能,所以需要一个临时对象保存当前对象的状态,并返回临时变量,即对象自增前的变量值。
最后是重载流运算符:
以插入流运算符举例“<<”,为什么要单独说呢,因为,流运算符需要实现对多个对象的输出
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:
double real;
double imag;
};
ostream &operator <<(ostream &output,complex &c)//运算符重载函数实现
{
output<<"("<<c.real<<","<<c.imag<<"i)"<<endl;
return output;
}
调用
complex c1(2,3),c2(4,-5),c3;
c3=c1+c2;
cout<<c3;
输出结果:
(6,-2i)
如果是
cout<<c3<<c2;
重载运算符是如何实现的呢?
这里我们注意到
ostream &operator<<(ostream &output,complex &c);
返回值是ostream 对象的引用,在函数实现中是返回的output这个引用,而output引用的就是cout这个输出流对象,这样在上述cout<<c3<<c2可以分开理解(cout<<c3)<<c2
这样在cout<<c3执行完之后相当于有执行一遍,cout<<c2,这样可以达到依次输出多个对象的目的。
2.友元函数
其实还有一个大的范围叫做友元,包括友元函数,友元类等等,在这里通过友元函数来说明友元的思想,和作用,这样就可以把一类的问题都能解决
在上述的插入流运算符重载中,用到了友元函数,友元就是朋友的意思,顾名思义就是说朋友能用的私人物品,区别一般的人,这样友元就能够访问类对象的私有成员了,这个就是友元的核心思想,但是这样容易破坏类的封装性。
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:
double real;
double imag;
};
ostream &operator <<(ostream &output,complex &c)//运算符重载函数实现
{
output<<"("<<c.real<<","<<c.imag<<"i)"<<endl;
return output;
}
再看下这个例子,运算符重载函数并不是类的成员函数,但是却可以访问类中的私有成员,因为在类中我们申明了该函数为友元函数
那如果友元函数为类的成员函数,该如何实现,该如何运用呢
举例如下:
class Date;
class Time
{
public:
Time(int,int,int);
void display(Date &);
private:
int hour;
int min;
int sec;
};
class Date
{
public:
Date(int,int,int);
friend void Time::display(Date &);
private:
int month;
int day;
int year;
};
Time::Time(int h,int m,int s):hour(h),min(m),sec(s){}
void Time::display(Date &d)
{
cout<<d.month<<"/"<<d.day<<"/"<<d.year<<endl;
cout<<hour<<":"<<min<<":"<<sec<<endl;
}
Date::Date(int m,int d,int y):hour(h),day(d),year(y){}
int main()
{
Time t1(10,13,56);
Date d1(12,25,2004);
t1.display(d1);
return 0;
}
用法看下代码就能够明白,就是实现在类中能用另外类的私有成员,用得比较少,在小的模块内部可能会运用到
主要是思想指导就好
class B;
class A
{
friend B;
}
这样B中的所有函数都是A类的友元函数。