友元

2023-10-19 14:45:04 浏览数 (1)

友元函数

之前我们在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这样虽然可以,但是看起来很不合理,所以我们选择友元函数

代码语言:javascript复制
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;
}

0 人点赞