友元函数的来由和使用套路

来由
来源:为了解决运算符重载的对象显式调用问题。
class_A = class_B * 2.75;
这里的 * 运算符重载我们可以通过成员函数实现,但是像这样的语句:
class_A = 2.75 * class_B;
就不能了。从概念上说,2.75 * class_B 应与 class_B * 2.75 相同,但第一个表达式不对应于成员函数,因为2.75不是Time类型的对象。 左侧操作数应是调用对象,但2.75不是对象。因此, 编译器不能使用成员函数调用来替换该表达式
解决这个难题的一种方式是,告知每个人(包括程序员自己),只能按 class_B * 2.75 这种格式编写,不能写成 2.75 * class_B 。这是一种对服务器友好-客户警惕的(server-friendly, client-beware)解决方案,与OOP无关。
然而,还有另一种解决方式——非成员函数(记住,大多数运算符都可以通过成员或非成员函数来重载)。非成员函数不是由对象调用的,它使用的所有值(包括对象)都是显式参数。这样, 编译器能够将表达式 class_A = 2.75 * class_B; 与 class_A = operator*(2.75, class_B) 非成员函数调用匹配
对于非成员函数重载运算符来说,运算符表达式左边的操作数对应于运算符函数的第一个参数( 这里的函数特征标有顺序!),运算符表达式右边的操作数对应于运算符函数的第二个参数。而原来的成员函数则按相反的顺序处理操作数,也就是说,double值乘以Time值。
使用非成员函数可以按所需的顺序获得操作数,但是这里有个问题,非成员函数不能直接访问类的私有数据,所以,就引出了我们的 友元函数
套路
  1. 始终记住一点,友元函数是是非成员函数,所以不能使用域限定符 : : 对友元函数进行操作。但是同时,友元函数的访问权限又与成员函数相同
  2. 实际上,我们经常采用在友元函数定义中调用成员函数的方式(这里有代码重用的思想,因为我们已经实现了Time值乘以double值,区别只是在于参数的顺序不同,所以可以直接调用该成员函数而不是重新实现一遍):
Time operator*(double m, const Time & t)
{
     return t * m;
}
一个经验就是,如果要为类重载运算符,并将非类的项作为第一个操作数,则可以 用友元函数来反转操作数的顺序
  1. 重载<<运算符,一般使用如下模式(因为返回值为ostream对象,可以在一个语句中多次调用):
ostream & operator<<(ostream & os, const c_name & obj)
{
     os << ...;
     return os;
}
  1. 重载一个运算符,如果它的功能是计算得到一个新的类对象,则应考虑是否可以使用类构造函数来完成这种工作。这样做不仅可以避免麻烦,而且可以确保新的对象是按照正确的方式创建的(方便,高效,安全)。例如:
Vector Vector::operator+(const Vector & v) const
{
     return Vector(x + v.x, y + v.y);
}
  1. 将加法定义为友元函数可以让程序更容易适应自动类型转换,原因在于,两个操作数都成为函数参数,因此与函数原型匹配。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值