友元-----(友元函数)
C++控制对类对象私有部分的访问。通常,公有类方法提供唯一的访问途径,但是有时候这种限制太严格,以至于不适合特定的编程问题。在这种情况下,C++提供了另外一种形式的访问限权:友元的种类:
- 友元函数
- 友元类
- 友元成员函数
通过让函数成为类的友元,可以赋予函数与类的成员函数相同的访问权限
。
1.为何需要友元
看一个例子:
A=B*2.75
其中A,B是同一类型的类。被转换为下面的成员函数调用:
A=B.operator*(2.75)
但是如果这样的语句该如何操作呢?
A=2.75*B
从概念上看,2.75B与B2.75相同,但第一个表达式不对应于成员函数,因为2.75不是类的对象。记住,左侧的操作数应是调用对象,但是2.75不是对象。因此,编译器不能使用成员函数调用来替换该表达式。
解决方法:
一:告诉每一个人只能按照B*2.75这种格式来编写。
二:非成员函数(大多数运算符都可以通过成员或者非成员函数来重载)。非成员函数不是由对象调用,它使用的所有值(包括对象)都是显式参数。编译器能够将下面的表达式:
A=2.75*B
与下面的非成员函数调用匹配:
A=operator*(2.75,B)
该函数原型为:
Myclass operator(double m,const Myclass & t)
对于非成员重载运算符函数来说,运算符表达式左边的操作数对应于运算符的第一个参数,运算符表达式右边的操作数对应于运算符的第二个参数。
使用非成员函数可以按照所需的顺序获得操作数,但引发一个问题:非成员函数不能直接访问类的私有数据,至少常规非成员函数不能访问。
但是有一类特殊的非成员函数可以访问类的私有成员,它们就是友元函数
2.创建友元
创建友元函数的第一步就是将其原型放在类声明中,并在原型声明前加上关键字friend:
friend A operator* (double m,const A &t)
该原型意味着下面两点:
- 虽然operator * ()函数在类中声明的,但它不是成员函数,因此不能使用成员运算符来调用
- 虽然operator *()函数不是成员函数,但它与成员函数的访问权限一致
第二步就是编写函数定义。因为它不是成员函数,所以不要使用A::限定符。另外,不要在定义中使用关键字friend 。定义应该如下:
A operator*(double m,const A &t)
{
t.私有数据
return xx;
}
3.常用的友元
一个很有用的类特征是,可以对<<运算符进行重载,使之能与cout一起来显示对象的内容。
1.<<的第一种重载版本
要使Time类知道使用cout,必须使用友元函数,这是什么原因呢?因为下面这样的语句使用两个对象,其中第一个是ostream类对象(cout)
cout<<trip;
如果使用一个Time成员函数来重载<<,Time对象将是第一个操作数,就像使用成员函数重载*运算符那样。这意味着必须这样使用<<
trip<<cout;
这样会令人迷惑。但通过使用友元函数,可以像下面这样重载运算符:
void operator <<(ostream &os,const Time &t)
{
os<<t.hours<<"hours:"<<t.minutes<<"minutes";
}
这样可以使用
cout<<trip
2.<<的第二种重载版本
前面介绍的实现有一个问题。像下面这样的语句可以正常工作:
cout<<trip;
但这种实现不允许像通常那样重新定义的<<运算符与cout一起使用:
cout<<"trip time"<<trip<<"tuesday \n"<<endl; 错的
理解out操作:
看下面语句:
int x=5;
int y=8;
cout<<x<<y;
C++从左到右读取输出语句:
(cout<<x) <<y;
正如iostream中定义的那样,<<运算符要求左边是一个ostream对象。显然,因为cout是ostream对象,所以表达式cout<<x满足这种要求。然而,因为表达式cont<<x位于<<y的左侧,所以输出语句也要求该表达式是一个ostream类型的对象。因此,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;
}
下面的语句:
cout<<trip;
将被转换为下面的调用:
operator<<(cout,trip)