前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布

友元

作者头像
用户7272142
发布2023-10-19 14:45:04
1280
发布2023-10-19 14:45:04
举报
文章被收录于专栏:Republic博客Republic博客

友元函数

之前我们在Time类的示例中,我们重载乘法运算符的参数和其他参数不一样,因为有两种不同的类型,即Time和double类型,限制了调用的方式,我们成员函数调用的过程是b..opertaor*(2.75)写成运算符的形式是b*2.75 那如果是2.75*B可以吗?当然不行,因为虽然在我们看来二者是一样的,但这样写就不对应成员函数了,2.75不是一个对象,因此他不能作为左操作数。编译器也不能使用成员函数替换这个表达式。

image.png
image.png

解决办法是,让他不由对象调用,而由非成员函数调用,但是非成员函数又无法访问类对象内的数据。怎么办。。。 对于非成员重载运算符函数来说,运算符表达式左边的操作数对应运算符函数的第一个参数,运算符表达式右边的操作数对应运算符的第二个参数。而原来的成员函数按相反的顺序处理操作数,即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;
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-10-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 友元函数
  • 创建友元函数
  • 重载<<运算符
    • 方式1:
      • 方式2
      • 作为成员函数还是非成员函数
      • 友元类
        • 友元类的特性
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档