友元函数有啥作用,它主要用在哪些情况下?

author author     2023-04-15     306

关键词:

友元函数的作用就是提供直接访问对象的私有成员的接口。通过友元,一个不同函数或另一个类中的成员函数可以访问类中的私有成员和保护成员。

使用情况:

1)必须在类的说明中说明友元函数,说明时以关键字friend开头,后跟友元函数的函数原型,友元函数的说明可以出现在类的任何地方,包括在private和public部分;

2)注意友元函数不是类的成员函数,所以友元函数的实现和普通函数一样,在实现时不用"::"指示属于哪个类,只有成员函数才使用"::"作用域符号;

3)友元函数不能直接访问类的成员,只能访问对象成员;

4)友元函数可以访问对象的私有成员,但普通函数不行;

5)调用友元函数时,在实际参数中需要指出要访问的对象;

6)类与类之间的友元关系不能继承;

7)一个类的成员函数也可以作为另一个类的友元,但必须先定义这个类。

扩展资料:

友元函数的成员函数:

假设f是想正确声明的函数,c是和它相关的类;

1、虚函数必须是成员函数。如果f必须是虚函数,就让它成为c的成员函数。

2、operator>>;和operator<<;决不能是成员函数。如果f是operator>>;或operator<<;,让f成为非成员函数。如果f还需要访问c的非公有成员,让f成为c的友元函数;

3、只有非成员函数对最左边的参数进行类型转换。如果f需要对最左边的参数进行类型转换,让f成为非成员函数。如果f还需要访问c的非公有成员,让f成为c的友元函数;

4、其它情况下都声明为成员函数;

5、如果以上情况都不是,让f成为c的成员函数。

参考资料来源:百度百科-友元函数

参考技术A 友元函数的作用就是提供直接访问对象的私有成员的接口。友元函数是可以直接访问类的私有成员的非成员函数。类授予它的友元特别的访问权。通常同一个开发者会出于技术和非技术的原因,控制类的友元和成员函数(否则当你想更新你的类时,还要征得其它部分的拥有者的同意)。
形式:
friend <返回类型> <函数名> (<参数列表>);
然后在类体外对友元函数进行定义,定义的格式和普通函数相同,但可以通过对象作为参数直接访问对象的私有成员。
注意:
1)必须在类的说明中说明友元函数,说明时以关键字friend开头,后跟友元函数的函数原型,友元函数的说明可以出现在类的任何地方,包括在private和public部分;
2)注意友元函数不是类的成员函数,所以友元函数的实现和普通函数一样,在实现时不用"::"指示属于哪个类,只有成员函数才使用"::"作用域符号;
3)友元函数不能直接访问类的成员,只能访问对象成员,
4)友元函数可以访问对象的私有成员,但普通函数不行;
5)调用友元函数时,在实际参数中需要指出要访问的对象,
6)类与类之间的友元关系不能继承。
7)一个类的成员函数也可以作为另一个类的友元,但必须先定义这个类。
实例:
#include <iostream>
#include <math.h>
using namespace std;
class Point

public:
Point(double xx, double yy) x=xx; y=yy;
void Getxy();
friend double Distance(Point &a, Point &b);
private:
double x, y;
;
void Point::Getxy()

cout<<"("<<x<<","<<y<<")"<<endl;

double Distance(Point &a, Point &b)

double dx = a.x - b.x;
double dy = a.y - b.y;
return sqrt(dx*dx+dy*dy);

void main()

Point p1(3.0, 4.0), p2(6.0, 8.0);
p1.Getxy();
p2.Getxy();
double d = Distance(p1, p2);
cout<<"Distance is "<<d<<endl;
参考技术B 友元函数

友元函数的特点是能够访问类中的私有成员的非成员函数。友元函数从语法上看,它与普通函数一样,即在定义上和调用上与普通函数一样。下面举一例子说明友元函数的应用。

class Point
public:
Point(double xx, double yy)

x=xx;
y=yy;

void Getxy();
friend double Distance(Point &a, Point &b);
private:
double x, y;
;
void Point::Getxy()

cout<<"("<<<","<<Y<<")"<<endl;

double Distance(Point &a, Point &b)

double dx = a.x - b.x;
double dy = a.y - b.y;
return sqrt(dx*dx+dy*dy);

void main()

Point p1(3.0, 4.0), p2(6.0, 8.0);
p1.Getxy(); p2.Getxy();
double d = Distance(p1, p2);
cout<<"Distance is"<< d<<endl;

说明:在该程序中的Point类中说明了一个友元函数Distance(),它在说明时前边加friend关键字,标识它不是成员函数,而是友元函数。它的定义方法与普通函数定义一样,而不同于成员函数的定义,因为它不需要指出所属的类。但是,它可以引用类中的私有成员,函数体中 a.x,b.x,a.y,b.y都是类的私有成员,它们是通过对象引用的。在调用友元函数时,也是同普通函数的调用一样,不要像成员函数那样调用。
参考技术C 友元是为了解决运行效率和封装性而设立的概念。
设想一个问题:输入两个(x,y)坐标,输出两点间距离。(用面向对象方法编写程序)
首先建立类Point。
class Point
private:
double x,y;
public:
Point(double xx,double yy)x=xx;y=yy; //构造函数
...

然后,我们要计算两点间的距离。
把这个函数叫Distance(,),参数是Point类的两个对象;Distance()要计算距离,就必须访问对象里的私有变量x,y。如下:
Distance(Point &a, Point &b)
  double dx = a.x - b.x;
  double dy = a.y - b.y;
  return sqrt(dx*dx+dy*dy);

这个函数可能是三种情况之一:独立于类Point的全局函数,类Point的成员函数,类Point的友元函数。1)非类Point成员函数首先被排除,因为Distance(,)需要访问类Point对象的Private变量,这是不允许的。2)类Point的成员函数,理论上被允许,但调用Distace(,)则必须通过对象,增加了程序的复杂度,许多成员需要被反复调用。
3)友元函数应运而生,加上friend,它可以访问类内的private变量,而调用时不必增加而外开销。
完整程序:
#include <iostream>
#include <math.h>
using namespace std;

class Point
private:
double x,y;
public:
Point(double xx,double yy)x=xx;y=yy; //构造函数
void Getxy(); //获取输入的(x,y)坐标并显示
friend double Distance( Point &a,Point &b );//友元函数Distance(,)用于计算两点间距离
;
void Point::Getxy()
cout<<"("<<x<<","<<y<<")"<<endl;

double Distance( Point &a,Point &b )
double dx = a.x - b.x ;
double dy = a.y - b.y ;
return sqrt( dx*dx + dy*dy );


int main()
cout<<"输入两点坐标:";
double a,b,c,d;
cin>>a>>b>>c>>d;
Point p1(a,b);
Point p2(c,d);
p1.Getxy();
p2.Getxy();
double re = Distance(p1,p2);
cout<<"两点间距离是:"<<re<<endl;
return 0;


当然,另外的方法是友元类;把Distance(,)放在这个友元类里。这个问题友元函数就可以解决了,较大的系统里可以使用友元类,涉及设计模式时比较常用的方法。
参考技术D 定义格式(c++):
  friend <返回类型> <函数名> (<参数列表>);
分清成员函数,非成员函数和友元函数
  成员函数和非成员函数最大的区别在于成员函数可以是虚拟的而非成员函数不行。所以,如果有个函数必须进行动态绑定(见条款38),就要采用虚拟函数,而虚拟函数必定是某个类的成员函数。关于这一点就这么简单。如果函数不必是虚拟的,情况就稍微复杂一点。 (条款38: 决不要重新定义继承而来的缺省参数值)
举例
  看下面表示有理数的一个类:
  class rational
  public:
  rational(int numerator = 0, int denominator = 1);
  int numerator() const;
  int denominator() const;
  private:
  ...
  ;
  这是一个没有一点用处的类。(用条款18的术语来说,接口的确最小,但远不够完整。)所以,要对它增加加,减,乘等算术操作支持,但是,该用成员函数还是非成员函数,或者,非成员的友元函数来实现呢?
  当拿不定主意的时候,用面向对象的方法来考虑!有理数的乘法是和rational类相联系的,所以,写一个成员函数把这个操作包到类中。
  class rational
  public:
  ...
  const rational operator*(const rational& rhs) const;
  ;
  (如果你不明白为什么这个函数以这种方式声明——返回一个const值而取一个const的引用作为它的参数——参考条款21-23。)
  条款21: 尽可能使用const
  条款22: 尽量用“传引用”而不用“传值”
  条款23: 必须返回一个对象时不要试图返回一个引用
  现在可以很容易地对有理数进行乘法操作:
  rational oneeighth(1, 8);
  rational onehalf(1, 2);
  rational result = onehalf * oneeighth; // 运行良好
  result = result * oneeighth; // 运行良好
  但不要满足,还要支持混合类型操作,比如,rational要能和int相乘。但当写下下面的代码时,只有一半工作:
  result = onehalf * 2; // 运行良好
  result = 2 * onehalf; // 出错!
  这是一个不好的苗头。记得吗?乘法要满足交换律。
  如果用下面的等价函数形式重写上面的两个例子,问题的原因就很明显了:
  result = onehalf.operator*(2); // 运行良好
  result = 2.operator*(onehalf); // 出错!
  对象onehalf是一个包含operator*函数的类的实例,所以编译器调用了那个函数。而整数2没有相应的类,所以没有operator*成员函数。编译器还会去搜索一个可以象下面这样调用的非成员的operator*函数(即,在某个可见的名字空间里的operator*函数或全局的operator*函数):
  result = operator*(2, onehalf); // 错误!
  但没有这样一个参数为int和rational的非成员operator*函数,所以搜索失败。
  再看看那个成功的调用。它的第二参数是整数2,然而rational::operator*期望的参数却是rational对象。怎么回事?为什么2在一个地方可以工作而另一个地方不行?
  秘密在于隐式类型转换。编译器知道传的值是int而函数需要的是rational,但它也同时知道调用rational的构造函数将int转换成一个合适的rational,所以才有上面成功的调用(见条款m19)。换句话说,编译器处理这个调用时的情形类似下面这样:
  const rational temp(2); // 从2产生一个临时
  // rational对象
  result = onehalf * temp; // 同onehalf.operator*(temp);
  当然,只有所涉及的构造函数没有声明为explicit的情况下才会这样,因为explicit构造函数不能用于隐式转换,这正是explicit的含义。如果rational象下面这样定义:
  class rational
  public:
  explicit rational(int numerator = 0, // 此构造函数为
  int denominator = 1); // explicit
  ...
  const rational operator*(const rational& rhs) const;
  ...
  ;
  那么,下面的语句都不能通过编译:
  result = onehalf * 2; // 错误!
  result = 2 * onehalf; // 错误!
  这不会为混合运算提供支持,但至少两条语句的行为一致了。
例子结论
  然而,我们刚才研究的这个类是要设计成可以允许固定类型到rational的隐式转换的——这就是为什么rational的构造函数没有声明为explicit的原因。这样,编译器将执行必要的隐式转换使上面result的第一个赋值语句通过编译。实际上,如果需要的话,编译器会对每个函数的每个参数执行这种隐式类型转换。但它只对函数参数表中列出的参数进行转换,决不会对成员函数所在的对象(即,成员函数中的*this指针所对应的对象)进行转换。这就是为什么这个语句可以工作:
  result = onehalf.operator*(2); // converts int -> rational
  而这个语句不行:
  result = 2.operator*(onehalf); // 不会转换
  // int -> rational
  第一种情形操作的是列在函数声明中的一个参数,而第二种情形不是。
  尽管如此,你可能还是想支持混合型的算术操作,而实现的方法现在应该清楚了:使operator*成为一个非成员函数,从而允许编译器对所有的参数执行隐式类型转换:
  class rational
  ... // contains no operator*
  ;
  // 在全局或某一名字空间声明,
  // 参见条款m20了解为什么要这么做
  const rational operator*(const rational& lhs,
  const rational& rhs)
  
  return rational(lhs.numerator() * rhs.numerator(),
  lhs.denominator() * rhs.denominator());
  
  rational onefourth(1, 4);
  rational result;
  result = onefourth * 2; // 工作良好
  result = 2 * onefourth; // 万岁, 它也工作了!
  这当然是一个完美的结局,但还有一个担心:operator*应该成为rational类的友元吗?
  这种情况下,答案是不必要。因为operator*可以完全通过类的公有(public)接口来实现。上面的代码就是这么做的。只要能避免使用友元函数就要避免,因为,和现实生活中差不多,友元(朋友)带来的麻烦往往比它(他/她)对你的帮助多。
成员的函数与类接口
  然而,很多情况下,不是成员的函数从概念上说也可能是类接口的一部分,它们需要访问类的非公有成员的情况也不少。
  让我们回头再来看看本书那个主要的例子,string类。如果想重载operator>>和operator<<来读写string对象,你会很快发现它们不能是成员函数。如果是成员函数的话,调用它们时就必须把string对象放在它们的左边:
  // 一个不正确地将operator>>和
  // operator<<作为成员函数的类
  class string
  public:
  string(const char *value);
  ...
  istream& operator>>(istream& input);
  ostream& operator<<(ostream& output);
  private:
  char *data;
  ;
  string s;
  s >> cin; // 合法, 但
  // 有违常规
  s << cout; // 同上
  这会把别人弄糊涂。所以这些函数不能是成员函数。注意这种情况和前面的不同。这里的目标是自然的调用语法,前面关心的是隐式类型转换。
正确用法
  istream& operator>>(istream& input, string& string)
  
  delete [] string.data;
  read from input into some memory, and make string.data
  point to it
  return input;
  
  ostream& operator<<(ostream& output,
  const string& string)
  
  return output << string.data;
  
  注意上面两个函数都要访问string类的data成员,而这个成员是私有(private)的。但我们已经知道,这个函数一定要是非成员函数。这样,就别无选择了:需要访问非公有成员的非成员函数只能是类的友元函数。
本条款得出的结论
  假设f是想正确声明的函数,c是和它相关的类:
  ·虚函数必须是成员函数。如果f必须是虚函数,就让它成为c的成员函数。
  ·operator>>和operator<<决不能是成员函数。如果f是operator>>或operator<<,让f成为非成员函数。如果f还需要访问c的非公有成员,让f成为c的友元函数。
  ·只有非成员函数对最左边的参数进行类型转换。如果f需要对最左边的参数进行类型转换,让f成为非成员函数。如果f还需要访问c的非公有成员,让f成为c的友元函数。
  ·其它情况下都声明为成员函数。如果以上情况都不是,让f成为c的成员函数。

摘自百度百科:http://baike.baidu.com/view/534170.htm

什么是友元?

友元函数就是以friend开头的一种破坏类的封装性的一种用法友元类的私有和保护成员在类外不可以使用设计了一个后门,通过友元函数可以访问该对象的受限成员1。该函数放在类内类外声明均可2。需要通过类的对象访问如friendv... 查看详情

友元函数友元类.

友元能够理解为是类的“朋友”。它能够訪问类的保护和私有成员。友元的作用在于提高程序的执行效率,可是,它破坏了类的封装性和隐藏性。友元能够是一个函数,该函数被称为友元函数;友元也能够是一个类,该类被称为... 查看详情

友元函数都有哪些特点?

友元函数和友元类:通过友元,一个普通函数或类的成员函数可以访问另一个类的私有数据成员。友元分为友元函数和友元类,关键字friend放在函数名之前,该函数就是友元函数,放在类名之前,该类就是友元类。友元声明在公... 查看详情

单片机c语言中常用的头文件有哪些?分别有啥作用啊?

...,以确定各存储空间的绝对地址。stdlib.h,动态内存分配函数。string.h缓冲区处理函数。stdio.h输入输出流函数,流函数通过8051的窜 查看详情

下面的代码有啥作用? C++ [重复]

...板上写了一些我看不懂的代码。它似乎是一个派生类构造函数调用一个基类构造函数,但我不确定。如下:classBaseintx,y;public:Base(int,int);;classDerive 查看详情

类3(友元)

一、友元介绍我们知道,类的成员函数可以访问同类的其他成员函数,包括公有、私有和保护成员。而类的外部函数只能访问类的公有成员。友元是一种允许非类成员函数访问类的非公有成员的一种机制。可以把一个函数指定为... 查看详情

在java中map类的主要作用

map类在Java中如何去使用,在哪些情况下要使用它MAP是个接口,主要用途就是存储键值对,键是不可重复的,可以存储object类型,值是可重复的。用途,实现类HASHMAP,TREEMAP可以用来实现如同javabean一样的存储功能,MAP的用法更灵... 查看详情

友元函数和友元类

类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。友元可以是一个函数,该函数被称为友元函数;友元... 查看详情

mipmap到底有啥作用

...新渲染纹理目标的mipmap非常有用。在OpenGLES中,可以使用函数glGenerateMipmap来实现。在Vulkan中没有这样的内置函数,开发人员必须手动生成它们。参考技术Amipmap指的是平面纹理的清晰程度。它是方块经过渲染之后显示的清晰程度... 查看详情

android中adapter有啥作用?常见的adapter都有哪些?

Adapter即适配器,适配器的作用是允许硬件或电子接口与其它硬件或电子接口相连,也可以是信息接口。常见的Adapter如:电源适配器、三角架基座转接部件、USB与串口的转接设备等。适配器模式主要应用于,当接口里定义的方法... 查看详情

在哪些情况下调用 C++ 复制构造函数?

】在哪些情况下调用C++复制构造函数?【英文标题】:InwhichsituationsistheC++copyconstructorcalled?【发布时间】:2014-02-0723:47:19【问题描述】:我知道在c++中会调用复制构造函数的以下情况:当一个现有对象被分配一个它自己的类的对... 查看详情

tkinter 中的“重量”有啥作用?

】tkinter中的“重量”有啥作用?【英文标题】:Whatdoes\'weight\'dointkinter?tkinter中的“重量”有什么作用?【发布时间】:2018-02-0111:48:24【问题描述】:我一直在搜索不同的网站,试图找出重量在tkinter中的作用。我从TkDocs得到这个... 查看详情

spring中的核心类都有哪些,各有啥作用!

spring、struts2和hibernate是三大框架,但是我感觉struts2和hibernate的作用很明显,但是spring的作用似乎不明显,谁能讲清楚它到底有什么作用?spring的核心模块式它的IOC容器,通过IOC容器可以将Action,业务逻辑(service),持久层整合... 查看详情

类和对象(16)——友元(代码片段)

1、友元  采用类的机制后实现了数据的隐藏与封装,类的数据成员一般定义为私有成员,成员函数一般定义为公有的,依此提供类与外界间的通信接口。但是,有时需要定义一些函数,这些函数不是类的一部分,但又需要频繁地访问... 查看详情

python中的“@”(@)符号有啥作用?

...号在python中是装饰器的意思。装饰器对一个可调用对象(函数、方法、类等等)进行装饰,它返回的也是一个可调用对象。一般情况下,装饰器是对被装饰对象的修饰与增强。用现实事物类比的话,可以类比为中间商:中间商不... 查看详情

为啥切片函数在不明确使用 dplyr 的情况下不起作用

】为啥切片函数在不明确使用dplyr的情况下不起作用【英文标题】:whyslicefunctionnotworkingwithoutexplicituseofdplyr为什么切片函数在不明确使用dplyr的情况下不起作用【发布时间】:2019-06-0704:33:55【问题描述】:我正在学习使用dplyr并遇... 查看详情

gre协议有啥作用?用在那里?

参考技术AGRE规定了如何用一种网络协议去封装另一种网络协议的方法.GRE的隧道由两端的源IP地址和目的IP地址来定义,允许用户使用IP包封装IP、IPX、AppleTalk包,并支持全部的路由协议(如RIP2、OSPF等)比如现在信利软件公司就... 查看详情

gitbash是啥,主要是用在哪些方面

...行工具。基于msysGNU环境,有git分布式版本控制工具,也主要用于git。GNU环境,就是说如果你喜欢linux/unix的环境,就可以选择使用gitbash。里面有你熟悉的linux工具,tar,grep,awk等,且可以安装编译环境gcc,make等。可以参考msys:h... 查看详情