友元函数
之前我们在Time类的示例中,我们重载乘法运算符的参数和其他参数不一样,因为有两种不同的类型,即Time和double类型,限制了调用的方式,我们成员函数调用的过程是b..opertaor*(2.75)
写成运算符的形式是b*2.75
那如果是2.75*B可以吗?当然不行,因为虽然在我们看来二者是一样的,但这样写就不对应成员函数了,2.75不是一个对象,因此他不能作为左操作数。编译器也不能使用成员函数替换这个表达式。
解决办法是,让他不由对象调用,而由非成员函数调用,但是非成员函数又无法访问类对象内的数据。怎么办。。。 对于非成员重载运算符函数来说,运算符表达式左边的操作数对应运算符函数的第一个参数,运算符表达式右边的操作数对应运算符的第二个参数。而原来的成员函数按相反的顺序处理操作数,即Time*double 所以使用非成员函数可以按所需的顺序获得操作数(double Time) ,接下来只有一个问题需要解决了,就是类对象数据的隐藏性,然而,有一类特殊的非成员函数可以访问类的私有成员,即友元函数
创建友元函数
创建友元函数的第一步将原型放在类声明中,并且在声明前加上friend friend Time operator*(double,const Time&T);
首先他是一个非成员函数 不能通过成员运算符调用
有类内隐藏对象的访问权限 然后我们就可以编写他的定义了,既然不是成员函数就不能用类的限定符修饰了,也不要在定义中使用firend。
代码语言:javascript复制Time operator*(double n, const Time& T)
{
Time res;
long totalminutes = T.hours * n * 60 T.minutes * n;
res.hours = totalminutes / 60;
res.minutes = totalminutes % 60;
std::cout << "firend" << "n";
return res;
如此开头的double*Time 就可以转换成 operator*(1.5,p1); 当然也可以修改一下定义,使其不必是友元 如
代码语言:javascript复制Time operator*(double n, const Time& T)
{
return T*n;
}
这样该函数没有访问到数据,但是可以用我们之前的成员函数进行计算,我们这里替换了一下参数的顺序,也有和友元函数一样的作用(最好是友元 可以成为类的接口的一部分)
重载<<运算符
方式1:
我们之前显示类中数据都是通过调用成员函数show来实现,现在我们通过重载<<运算符可以让cout命令显示我们对象的内容
即cout<<p1
需要知道的是其实<<已经被重载多次了 比如左移<< 以及cout对象的输出 之所以cout只能可以识别每种类型,是因为cout对象的类声明对每种类型,对包含了相应的重载,我们可以通过修改Time类,达到cout可以识别Time类的目的,那直接在cout类声明里面修改对Time类的识别可以吗?可以但是没必要 很没有必要
我们按照我们之前重载的方法,我们现在要的形式是cout<<p1
,也就是需要两个对象operator<<(ostream&os);
那么我们调用的时候将会p1<<cout
这样虽然可以,但是看起来很不合理,所以我们选择友元函数
void operator<<(ostream&os,const Time&t)
{
os<<t.hours<<" hours,"<<t.minutes<<" minutes";
}
这样我们就可以使用cout<<p1; 这里我们只是访问了Time类的私有成员而没有访问ostream的私有成员,所以我们只需要Time类的友元而不需要ostream的友元
方式2
方式1可以使用如cout<<p1;的格式,但是对于cout<<"Trip time: "<<trip<<"(Tuesday)n";我们通过之前的重载是没办法实现的 所以我们先来分析这条语句 首先代码调用cout对象显示字符串如果要显示下一个trip变量那么就需要返回一个cout对象 那么就实现了可以连续输出变量 即
代码语言:javascript复制std::ostream& operator<<(std::ostream& os, const Time& t)
{
os << t.hours << " hours" << t.minutes << " minutes";
return os;
}
当然非成员函数的定义可以写作T*n,作为内联函数处理。
作为成员函数还是非成员函数
对于很多运算符来说,可以选择使用成员函数或非成员函数来实现运算符重载,一般来说,非成员函数应该是友元函数,这样才可以访问类里面的私有数据。如加法运算符,成员函数只需要一个参数,因为有一个操作符应该通过隐式传递给了函数。而非成员运算符 两个操作数都需要作为参数传递给函数 如P1 = P2 P3; 成员函数:P2.operator(P3) 非成员函数:operator(p2,p3)
友元类
友元类(Friend C lass)是C 中的另一个重要概念,它允许一个类将另一个类声明为自己的友元,从而使得被声明为友元的类可以访问该类的私有成员。通过友元类,我们可以实现多个类之间的数据和成员函数共享。但同样要谨慎使用,以避免过度暴露类的实现细节。
友元类的特性
(1)友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
(2)友元关系是单向的,不具有交换性。比如在只声明B是A友元的情况下,B可以访问A的私有成员,但是A却不可以访问B的私有成员,即A不是B的友元。
(3)友元关系不能传递:如果B是A的友元,C是B的友元,则不能说明C是A的友元。(我友元的友元不是我的友元)
代码语言:javascript复制class MyClass
{
private:
int value = 10;
// 声明友元类
friend class FriendClass;
};
class FriendClass
{
public:
void printValue(const MyClass& obj)
{
cout << "Value: " << obj.value << endl;
}
};
int main()
{
MyClass obj;
FriendClass fc;
fc.printValue(obj); // 调用友元类的成员函数
return 0;
}