在之前的友元中就曾经讲过,我们为了去访问修改私有成员中的数据时,只能通过公有的办法去进行访问操作,非常的局限。所以C++引用了友元函数,只要加上friend关键字,C++的这个类,会自动把这个函数的权限拉到类内,这样就可以访问私有成员了。
上一篇文章,讲了重载运算符的操作。但有些运算符不能够在类内进行重载,例如输入输出的符号。下面我会详细讲,为什么不能在类内进行重载。
这里我们先假设有一个类:
A{};//功能暂且不写,知道是个类就行了
首先,普通类内重载,会自带一个this指针,指向用此函数的对象。这被称作第一操作数。
如果在定义+号的时候,实际上是并不是简单的(类对象)+(某某)亦或是(某某)+(类对象);
它实际上全部的写法应该是:
A a; //这里定义了一个对象a
a.operator+(某某); //这是全部写法,也是正常情况应该写的情况,只是我们平时简写,编译器仍然认识罢了。
a+(某某) // 这是我们平时的写法
比如你想重载<<或者>>符号,这种情况,他的第一操作符就一定不是类本身,例如:
A a;
我们不可能写成:a<<或者是a>>;我们正常的写法应该是:cin >>a;cout << a;
这里第一操作符并不是a这个对象,而是cin/cout。这是ios的输入输出的参数。
这里有人就要问了,那(类对象)+(某某);我也可以写成(某某)+(类对象);这里实际上是交换律,因为,这里类对象写的地方,并不是唯一的,可以进行交换,这种情况可以不算。
话不多说,我们使用代码来进行讲解,下面先给大家展示时间类运算符重载,作为对比,我先写类内成员函数重载,再写类外友元重载,大家可以复制下来,看看。
#include <iostream>
#include <string>
using namespace std;
/*
* 1.类的成员 在类内的运算符重载函数的第一个操作数一定是类的对象
* 总结:
* 有的情况运算符必须写成非成员函数.这个函数如果需要获得类对象的私有数据,则有如下方法
* 1.可以利用交换律(前提是类已经实现该运算符的重载.1.5*t->t*1.5)
* 2.类把该函数声明为它的友元函数
* 3.类提供获取私有数据的公有方法
*
* 举例:如下
*/
class Time
{
private:
int hours;//小时
int minutes;//分钟
public:
Time(int h = 0, int m = 0) :hours(h), minutes(m)//构造函数
{}
Time operator +(const Time& t)const;//重载 +,注意返回值不是引用
Time operator -(const Time& t)const//重载 -
{
int tmp = (hours * 60 + minutes) - (t.hours * 60 + t.minutes);//分钟
return Time(tmp / 60, tmp % 60);
}
Time operator *(double n)const;//重载 * .含义;3:40 * 3 ->11小时0分
//3*3:40 不是
void show() const;
//提供获得时间的公有方法
/* int GetHours()const
{
return hours;
}
int GetMinutes()const
{
return minutes;
}*/
};
Time Time::operator+(const Time& t)const //2:30+2:45
{
return Time(hours + t.hours + (minutes + t.minutes) / 60, (minutes + t.minutes) % 60);
}
Time Time::operator *(double n)const // Time * 小数
{
double tmp = (hours * 60 + minutes) * n;
return Time{ (int)tmp / 60,(int)tmp % 60 };
}
void Time::show() const
{
cout << hours << "小时," << minutes << "分钟" << endl;
}
Time operator*(double n,const Time &t)//普通函数的形式重载*
{
//double tmp = n * (t.hours * 60 + t.minutes);//错误,这个函数不是Time成员,不能访问它的私有
// int tmp = (int)(n * (t.GetHours() * 60 + t.GetMinutes()));
// return Time(tmp/60,tmp%60);
return t * n;//前提:已经实现了 t*n
}
int main()
{
Time t1{ 2,35 };
Time t2 = { 2,40 };
Time t3 = t1 + t2;//t1.operator+(t2);
Time t4 = t2 - t1;//t2.operator-(t1);
Time t5 = t1 * 1.5;
Time t6 = 1.5 * t1;//没有实现 小数*时间
t3.show();
t4.show();
t5.show();
t6.show();
//t1 * 3;//t1.operator*(3);
//3*t1;//3.*(t1);//在3这个int类 没有实现对Time的*重载
return 0;
}
接下来,作为对比,我继续把重载输入输出的代码。
#include <iostream>
#include <string>
using namespace std;
class Time
{
private:
int hours;//小时
int minutes;//分钟
public:
Time(int h = 0, int m = 0);
void show() const;
//void operator <<(ostream& os)//os是输出流对象的引用
//{
// os << hours << "小时,," << minutes << "分钟" << endl;
//}
friend Time operator *(double n, const Time& t);//这个是Time的友元函数
friend ostream& operator <<(ostream& os, const Time& t);//这个是Time的友元函数
//实现 >> 运算符重载 istream
friend istream& operator >>(istream& is, Time& t);
};
Time::Time(int h, int m)
{
hours = h;
minutes = m;
}
void Time::show() const
{
cout << hours << "小时," << minutes << "分钟" << endl;
}
Time operator *(double n, const Time& t)
{
int tmp = (int)(n * (t.hours * 60 + t.minutes));
return Time(tmp/60,tmp%60);
}
ostream & operator <<(ostream& os, const Time& t)//os不加const,需要把数据写入到输出流
{
os << t.hours << "小时,," << t.minutes << "分钟" << endl;
return os;
}
istream& operator >>(istream& is, Time& t)
{
return is >> t.hours >> t.minutes;
}
int main()
{
Time t1 = { 2,35 };
Time t2 = { 2,40 };
Time t3 = 1.5*t1; //第一个操作数不是类对象,所以只能是非成员函数
//t3.show();
//t3 << cout; //可以作为类成员函数的,但不能理解
cout << t3;//这个不能调用t3的成员函数,但我们需要
cout << t1 << t2;
cin >> t1; //5 30
cout << t1;//5小时,30分钟
// cout << t3 ;//错误,没有实现如何 cout<< Time的对象
//cout 是 ostream类对象 ; cin是istream类对象
return 0;
}
每日金句:
一步一行,便无惧陷入泥沼!
---------------银枝