运算符重载和友元函数

运算符重载

所谓运算符重载其实就是运算符的多态,函数的多态就是同个函数名有不同的功能,运算符重载或者运算符多态就是同一个运算符有多个意思,在c语言中,出现过类似运算符重载的例子,比如

int x,y,z;
int* p;
x = 1;
y = 1;
z = x * y;
p = &x;

以上代码指出,*这个符号不仅可以用来计算数字之间相乘的结果,而且还可以解引用指针所指的空间中的值.

在c++中如果我定义一个类Time,这个类有两个对象time1,time2,我要计算time1,time2的和怎么计算?可以在成员函数增加一个sum(Time& )函数,通过调用time1.sum(time2)可以得出计算值,如果通过这样的time3 = time1 + time2的等式就可以直接计算出time1,time2的和岂不是很方便,这就是c++中的运算符重载.

c++提供了这样的机制,通过一个运算符函数operator [op] (操作数),例如operator+(Time& ),来完成对象之间的运算.编写下面的等式

time3 = time1 + time2

就可以自动转换为

time3 = time1.operator(time2)

通过以上这种方式就完成运算符的重载.

举例,我声名一个Time类以及成员函数的定义

class Time
	{
	private:
		int hours;
		int minutes;
	public:
		Time();
		Time(int h, 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;
	}
	
	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";
	}

其中Time(),Time(int h, int m = 0)时进行初始化的构造函数,Time operator+(const Time & t) const,Time operator-(const Time & t) const和Time operator*(double n) const分别是+,-和*的运算符函数,通过两个对象之间+,-,*运算,就可以自动转换为以上三个运算符函数,以下是测试函数

Time weeding(4, 35);
	Time waxing(2, 47);
	Time total;
	Time diff;
	Time adjusted;
	
	cout << "weeding time = ";
	weeding.Show();
	cout << endl;
	
	cout << "waxing time = ";
	waxing.Show();
	cout << endl;
	
	cout << "total work time = ";
	total = weeding + waxing;   // 使用 operator+()
	total.Show();
	cout << endl;
	
	diff = weeding - waxing;    // 使用 operator-()
	cout << "weeding time - waxing time = ";
	diff.Show();
	cout << endl;
	
	adjusted = total * 1.5;      // 使用 operator+()
	cout << "adjusted work time = ";
	adjusted.Show();
	cout << endl;
	
运行结果如下,这样就实现了两个Time对象weeding和waxing之间+,-,*

在这里插入图片描述

友元函数

再说友元函数之前,先来看一下Time operator*(double n) const这个运算符函数,通过以上例子我们可以知道一个Time类对象total可以和一个double型的数1.5相乘,然后转换为total.operator(1.5),但是,若果有人这这么输入1.5*total,这样就会发生错误.

原则上来说,c++想要完成的是对象和基本数据类型一样的操作,所以1.5 * total和total * 1.5含义是一样的,如何来纠正这种错误,不能在定义一个运算符函数,因为double类型不是对象,不能调用运算符函数,这种思路错误,实际上运算符重载要通过对象调用运算符函数才能完成对象之间的运算,这个时候就出现了友元函数.

它将

adjusted = 1.5 * total;

自动匹配为

Time operator*(double m,const Time& t);

友元函数是一类特殊非成员函数,因为友元函数可以直接访问私有成员,所以特殊.

友元函数的声名和意义如下,友元函数的声名和运算符函数的声名类似,也是放在类声名中,添加了一个friend的关键字.

friend Time operator*(double m, const Time & t)

只要有这样的声名就意味着,参考C++ primer plus(第六版) p391

1.虽然友元函数是在类中声明的,但是它不是成员函数,因此不能通过运算符自动转换
2.虽然operator*()函数不是成员函数,单但是它与成员函数的访问权限相同

定义如下

friend Time operator*(double m, const Time & t)
{
	Time result;
    long totalminutes = hours * mult * 60 + minutes * mult;
    result.hours = totalminutes / 60;
    result.minutes = totalminutes % 60;
    return result;
}

有了以上的声名和定义,下面的语句

adjusted = 1.5 * total;

就会转换为

adjusted = operator*(1.5,total);

完美解决!关于友元是否违背了面向对象的原则的讨论如下

C++ primer plus(第六版) p391说,oop乍看一下违背了数据隐藏的原则,因为友元函数不属于类成员函数,但是却可以使用类中数据,书中这样说,要把友元看成是类的一个拓展接口,只有类声名可以决定谁是友元,因为只有类声明控制友元访问私有数据,本质上还是数据隐藏

<<运算符重载

一般来说,cout是ostream类的对象,使用cout << data,可以打印出数据,如果想要打印出Time类对象total的数值,就要应用到运算符重载和友元函数的知识.

但是如果利用函数重载的话,那么也就意味着用对象调用运算符函数,比如total.operator<<(ostream& ),意味着编程的时候,就要这样调用total << cout,

原谅我笑出了声音…

语法上肯定是没问题的,但是这样的话就不能跟原有的规则cout<<data相媲美,这样就要用到友元函数

friend void operator<<(std::ostream & os, const Time & t);

这样调用cout << total 的话,就可以自动匹配operator<<(cout , total)这个函数了,看起来很方便,这里,为什么ostream类声名参数的时候要用引用,因为这样就不用把cout赋值给参数值进行操作了,而是给cout起了一个别名叫os,在其中需要执行cout来进行打印,而不是用一个ostream的对象来执行打印,并且不会调用ostream类中的隐藏数据,不涉及到这是两个类的友元

友元函数定义如下:

void operator<<(std::ostream & os, const Time & t)
	{
		os << t.hours << " hours, " << t.minutes << " minutes"; 
	}

调用

Time aida(3, 35);
Time tosca(2, 48);
Time temp;

cout << "Aida and Tosca:\n";
cout << aida<< endl; 
cout << tosca << endl;
temp = aida + tosca;     // operator+()
cout << "Aida + Tosca: " << temp << endl;
temp = aida* 1.17;  // member operator*()
cout << "Aida * 1.17: " << temp << endl;
cout << "10.0 * Tosca: " << 10.0 * tosca << endl;

效果如下:

在这里插入图片描述

但是要考虑到cout << data1 << data2 ,可以一次性输n个数据,但是上面定义的函数就只能输出一个数据,所有要考虑到cout << 的原理如下

int x = 5;
int y = 8;
cout << x << y;

其实cout << x << y中cout << x 本身就是个ostream对象,所以y直接流入这个(cout << x)对象之中,所以按照这个思路改造一下friend void operator<<(std::ostream & os, const Time & t)这个函数为std::ostream & operator<<(std::ostream & os, const Time & t),定义如下.

std::ostream & operator<<(std::ostream & os, const Time & t)
{
    os << t.hours << " hours, " << t.minutes << " minutes";
    return os; 
}

直接返回一个ostream对象的引用,那么就可以的不断流入这个ostream对象

测试

Time aida(3, 35);
Time tosca(2, 48);
Time temp;

cout << "Aida and Tosca:\n";
cout << aida<<"; " << tosca << endl;
temp = aida + tosca;     // operator+()
cout << "Aida + Tosca: " << temp << endl;
temp = aida* 1.17;  // member operator*()
cout << "Aida * 1.17: " << temp << endl;
cout << "10.0 * Tosca: " << 10.0 * tosca << endl;

效果如下

在这里插入图片描述

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值