C++的一项强大的功能就是支持运算符的重载。我们可以通过自己的习惯和要求去重载定义运算符,以便于完成一些复杂的操作。
比如,可以将‘+’重载为两个复数相加。但是需要遵守一定的规则;
说明一下运算符重载的规范:
比如说‘+’运算符必须使用两个操作数,a+b,即a,b各为一个操作数。
那么将加法重载为只需要一个操作数这种情况是不允许的,比如a+,b+,这样
是不行的。
3.不能修改运算符的优先级
4.不能创建新运算符
5.不能重载下面的运算符
sizeof:sizeof运算符
. :成员运算符
.* : 成员指针运算符
:: :作用域解析运算符
?: :条件运算符
typeid: 一个RTTI运算符
还有一系列强制转换运算符
用一个实例来说明理解简单的运算符重载
#include
using namespace std;
class Time
{
private:
int hours;
int minutes;
public:
Time(); //构造函数,
Time(int h,int m=0); //构造函数
void AddMin(int m);
void AddHr(int h);
void Reset(int h = 0, int m = 0);
Time operator+(const Time & t)const;
Time operator-(const Time& t)const;
Time operator*(double n)const;
void show()const;
};
Time::Time() //无参数的构造函数
{
hours = minutes = 0;
}
Time::Time(int h, int m) //有两个参数的构造函数
{
hours = h, minutes = m;
}
void Time::AddMin(int m)
{
minutes += m;
hours += minutes / 60;
minutes %= 60;
}
void Time::AddHr(int h)
{
hours += h;
}
void Time::Reset(int h, int m)
{
hours = h;
minutes = m;
}
Time Time::operator+(const Time& t)const //加法运算符的重载
{
Time sum;
sum.minutes = minutes + t.minutes;
sum.hours = hours + t.hours + sum.minutes / 60;
sum.minutes %= 60;
return sum;
}
Time Time::operator-(const Time& t)const
{
Time diff;
int tot1, tot2;
tot1 = t.minutes + 60 * t.hours;
tot2 = minutes + 60 * hours;
diff.minutes = (tot2 - tot1) % 60;
diff.hours = (tot2 - tot1) / 60;
return diff;
}
Time Time::operator*(double mult)const
{
Time result;
long totalminutes = hours * mult * 60 + minutes * mult;
result.hours = totalminutes / 60;
result.minutes = totalminutes % 60;
return result;
}
void Time::show()const
{
std::cout << hours << " hours ," << minutes << " minutes";
}
int main()
{
Time a(3, 20);
Time b(5, 90);
Time sum = a + b; //相当于sum=a.operator+(b),相当于把‘+’等价转换成‘.operator+()'
cout << "a=";
a.show(); //调用成员函数
cout << "\nb=";
b.show();
cout << "\na+b=sum=";
sum.show();
cout << endl << "sum*3=";
sum = sum*3; //sum=sum.operator*(3),相当于把'*'等价转换成'.operator*();
sum.show();
cout << endl << "sum-a=";
sum = sum - a; //sum =sum.operator-(a),相当于把'-'等价转换成'.operator-()';
sum.show();
cout << endl << endl << "上面很明显都是使用重载,那么不使用重载的版本也可以做到一样的事情,重载无非就是把函数名转换成符号名更为方便.\n";
a.AddHr(5);
a.AddMin(70);
a.show();
cout << endl;
return 0;
}
设置友元函数是方便非成员函数访问成员函数私有数据的一种方式。
首先知道,类私有数据是不能直接访问的,那么如果普通非成员函数想要访问类私有数据(priveta),该怎么搞?我知道的有2个方法;
那么第二种方法就比较局限,那么设置友元是更好的方法。
友元有三种:
通过让函数成为类的友元,可以赋予函数与类的成员函数相同的访问权限。也就是说,类成员函数能访问类的私有数据,而普通函数不能,但是如果该函数是类的友元,那么这个普通函数就也可以访问类的私有数据了。
这里暂且只说友元函数;
以运算符重载为例,上面可以发现,当重载运算符*的时候,左右两边的操作数分别是Time类对象和一个double内置类型。第一个操作数是Time类对象,可能这时候会疑惑为什么operator * (double x)里面只有一个参数,这是因为,重载函数是类成员函数,参数列表里面可以忽略类对象参数,而在该函数实现中,直接使用this指针,this指针指向调用函数的对象。就好像我进自己家还需要和门卫汇报一下能不能进去吗?
这不是重点说重点,重点是,友元函数可以让普通函数拥有和类的成员函数相同的访问权限。这有什么用呢?举个例子:
假设A,B都是类对象,重载加法运算符,那么可以这样:A=B+3. 这就要严格保证左边的操作数是调用对象,
A=B+3 等价于 A=B.operator+(3)
这没有什么问题,但是如果是这样呢: A=3+B,
这就不行了,因为没有A=3.operator+(B) ,3是一个常量,不是一个对象,自然不能调用对象。
那么为了解决这个问题,就必须使用非成员函数,这样就可以自定义操作数的顺序,但是这又引发了一个问题,非成员函数不能访问类的私有数据。友元函数因此而来。
、
创建友元函数
首先,在需要访问数据的类里面声明原型:
friend Time operator+(double m,const Time &t);
这个原型意味着两点:
第二步是定义函数,但是注意,虽然该函数原型在类中声明,但是他不是类成员函数,所以定义的时候不要画蛇添足,错误加上了Time::限定符
定义如下:
Time operator+(double m,Time &t)
{
Time result;
int totalmin=m+t.minutes;
t.hours+=totalmin/60;
t.minutes%=60;
}
这样一来,这样就可以实现:B=3+A 了。
总结,类的友元函数是非成员函数,其访问权限与成员函数相同.
提一个小技巧,实际上可以将这个友元函数转换成非友元函数,只要这样就可以
Time operator+(double m,Time &t)
{
return t*m;
}
这样完美避开了主要的问题:非成员函数不能访问类的是有数据
那么上面的笔记说明,如果要为类重载运算符,且要将非类的项作为其第一个操作数,则可以用友元函数来反转操作数的顺序.
最初,<<运算符是C,C++的位运算符,将值中的位左移。
ostream类对该运算符进行了重载,将其转换为一个输出工具。cout是一个ostream对象,位于名称空间std里面。它可以识别所有的C++基本类型。 因为ostream类声明中都包含了相应的重载的operator()定义。
1.<<的第一种重载版本
如果使用Time成员函数来重载<<,Time对象将是第一个操作数,就像使用成员函数重载*运算符那样,这意味着必须这样使用<<;
trip<
这样会很难看,所以需要使用友元函数。
void operator<<(ostream &os,const Time &t)
{ os<
这样就可以这样写:cout< 由于没有访问ostream的私有成员,所以不必是ostream类的友元。 cerr也是一个ostream对象,它将输出发送到标准错误流----默认为显示器,但在UNIX,Linux,Windows命令行环境中,可将标准错误流重定向到文件。 2.<<的第二种重载版本 注意,返回类型使ostream&,意味着该函数返回ostream对象的引用。因为函数开始执行时,程序传递了一个对象引用给它,这样做的最终结果是:函数的返回值就是传递给他的对象。 注意
调用cout<
使用void类型的重载运算符不支持多次操作,比如:cout<<…<ostream &ostream<<(ostream &os,const Time &t)
{
os<<t.hours<<" hours "<<t.minutes<<" minutes ";
return os;
}
cout<
operator<<(cout,trip);
那么可以这样:
cout<<" …“<
1.只有在类声明中的原型中才能使用friend关键字,除法函数定义也是原型,否则不能在函数定义中使用该关键字。
2.非成员版本的重载函数运算符函数所需的形参数目与运算符使用的操作数数目相同,而成员版本所需的参数数目少一个,因为其中的一个操作数是被隐式地传递地调用对象。