C++中的友元函数

        C++控制对类对象私有部分的访问。通常,公有类方法提供唯一的访问途径,但有时候这种限制太严格,以致于不适合特定的编程问题。在这种情况下,C++提供了另外一种形式的访问权限:友元。友元有3种:友元函数,友元类,友元成员函数。

为什么需要友元?

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;
}
Time A, B;
A = B * 2.75; //A = B.operator*(2.75);
A = 2.75 * B;  //cannot correspond to a member function

        解决这个问题的一种方式—非成员函数(大多数运算符都可以通过成员或非成员函数来重载)。非成员函数不是由对象调用,它使用的所有值(包括对象)都是显式参数。如该函数原型为Time operator*(double m, const Time &t)。使用非成员函数可以按所需的顺序获得操作数,但非成员函数不能直接访问类的私有数据,至少常规非成员函数不能访问。然而有一类特殊的非成员函数可以访问类的私有成员—友元函数。

创建友元:

        创建友元的第一步是将其原型放在类声明中,并在原型声明前加上friend,如

friend Time operator*(double mult, const Time &t);

        这意味着operator*()函数虽然是在类声明中声明的,但它不是成员函数,不能使用成员运算符来调用。operator*()函数不是成员函数,但它与成员函数的访问权限相同。

        第二部编写原型。注意不使用Time:: 限定符,在定义中不使用关键字friend。

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

        所以 A = 2.75 * B;  将转换为 A = operator*(2.75, B);  从而调用刚才定义的友元函数。

        总之,类的友元函数是非成员函数,其访问权限与成员函数相同。

常用的友元:重载<<运算符

1、<<的第一种重载版本

        要使Time类知道使用cout<<trip,必须使用友元函数。因为cout是ostream类对象,如果使用一个Time成员函数来重载<<,Time对象将是第一个操作数,使用<<时就变成了trip<<cout,但通过使用友元函数就可以使用cout<<trip;

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

        新的Time类声明使operator<<()函数成为Time类的一个友元,但该函数不是ostream类的友元。因为operator<<()直接访问Time对象的私有成员,所以它必须是Time类的友元。但由于它并不直接访问ostream对象的私有成员,所以不一定必须是ostream类的友元。

        调用cout<<trip应使用对象本身,而不是它的拷贝,因此该函数按引用(而不是按值)来传递该对象。这样cout<<trip将导致os成为cout的一个别名。Time对象可以按值或者按引用来传递,因为这两种方式都使函数能够使用对象的值。按引用传递使用的内存和时间都比按值传递少。

2、<<的第二种重载版本

        第一种版本存在一个问题。对于cout<<trip;(trip是Time类对象)可以正常工作,但这种实现不允许想通常那样将重新定义的<<运算符与cout一起使用。

cout << "Trip time: " << trip << " (Tuesday)\n"; //can't do

这是因为第一种版本的operator<<()函数返回值为void,而运算符<<要求左边是ostream对象。

        拿cout<<x<<y;这个例子来说(int x=5,y=8;)。ostream类将operator<<()函数实现为返回一个指向ostream对象的引用。也就是说,它返回一个指向调用对象(这里是cout)的引用。因此表达式(cout<<x)本身就是ostream对象cout,从而可以位于运算符的左侧。

        可以对友元函数采用相同的方法,只需修改operator<<()函数,让它返回ostream的引用即可。

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

友元是否有悖于OOP?

        友元是否违反了OOP数据隐藏的原则,因为友元机制允许非成员函数访问私有数据,但这种观点太片面了。相反,应将友元函数看作类的扩展接口的组成部分。例如,从概念上看,double乘以Time和Time乘以double是完全相同的。前一个要求有友元函数,后一个使用成员函数,这是C++句法的结果,而不是概念的差别。通过使用友元函数和类方法,可以用一个用户接口表达这两种操作。另外,只有类声明可以决定哪一个函数是友元,因此类声明仍然控制了哪些函数可以访问私有数据。总之,类方法和友元只是表达类接口的两种不同机制。

猜你喜欢

转载自blog.csdn.net/wxn704414736/article/details/80222068