C++中的friend函数详细解析
发布时间:2026/10/4 12:36:43 作者:尧图编辑部 阅读量:1,286

为什么要使用友元函数在实现类之间数据共享时减少系统开销提高效率。如果类A中的函数要访问类B中的成员例如智能指针类的实现那么类A中该函数要是类B的友元函数。具体来说为了使其他类的成员函数直接访问该类的私有变量。即允许外面的类或函数去访问类的私有变量和保护变量从而使两个类共享同一函数。实际上具体大概有下面两种情况需要使用友元函数1运算符重载的某些场合需要使用友元。2两个类要共享数据的时候。使用友元函数的优缺点优点能够提高效率表达简单、清晰。缺点友元函数破环了封装机制尽量不使用成员函数除非不得已的情况下才使用友元函数。C中的友元机制允许类的非公有成员被一个类或者函数访问友元按类型分为三种普通非类成员函数作为友元,类的成员函数作为友元类作为友元。友元包括友元的声明以及友元的定义。友元的声明默认为了extern就是说友元类或者友元函数的作用域已经扩展到了包含该类定义的作用域所以即便我们在类的内部定义友元函数也是没有关系的。友元可以是一个函数该函数被称为友元函数友元也可以是一个类该类被称为友元类。友元函数的特点是能够访问类中的私有成员的非成员函数。友元函数从语法上看它与普通函数一样即在定义上和调用上与普通函数一样。友元函数的实现可以在类外定义但必须在类内部声明友元函数是可以直接访问类的私有成员的非成员函数。它是定义在类外的普通函数它不属于任何类但需要在类的定义中加以声明声明时只需在友元的名称前加上关键字friend。我们已知道类具有封装和信息隐藏的特性。只有类的成员函数才能访问类的私有成员程序中的其他函数是无法访问私有成员的。非成员函数可以访问类中的公有成员但是如果将数据成员都定义为公有的这又破坏了隐藏的特性。另外应该看到在某些情况下特别是在对某些成员函数多次调用时由于参数传递类型检查和安全性检查等都需要时间开销而影响程序的运行效率。为了解决上述问题提出一种使用友元的方案。友元是一种定义在类外部的普通函数但它需要在类体内进行说明为了与该类的成员函数加以区别在说明时前面加以关键字friend。友元不是成员函数但是它可以访问类中的私有成员。友元的作用在于提高程序的运行效率即减少了类型检查和安全性检查等都需要的时间开销但是它破坏了类的封装性和隐藏性使得非成员函数可以访问类的私有成员。1.普通的非成员函数友元1234567891011121314151617181920212223242526272829303132333435363738#include cmath#include iostreamusingnamespacestd;classPoint{public:Point(doublexx,doubleyy){xxx;yyy;}voidGetXY();frienddoubleDistance(Point a,Point b);protected:private:doublex,y;};voidPoint::GetXY(){//cout(this-x,this-y)endl;cout(x,y)endl;}doubleDistance(Point a,Point b){doublelength;lengthsqrt((a.x-b.x)*(a.x-b.x)(a.y-b.y)*(a.y-b.y));//它可以引用类中的私有成员returnlength;}intmain(void){Point p1(3.0,4.0),p2(6.0,8.0);p1.GetXY();//成员函数的调用方法通过使用对象来调用p2.GetXY();doubled Distance(p1,p2);//友元函数的调用方法同普通函数的调用一样不要像成员函数那样调用coutdendl;system(pause);return0;}说明在该程序中的Point类中说明了一个友元函数Distance()它在说明时前边加friend关键字标识它不是成员函数而是友元函数。它的定义方法与普通函数定义一样而不同于成员函数的定义因为它不需要指出所属的类。但是它可以引用类中的私有成员函数体中的a.xb.xa.yb.y都是类的私有成员它们是通过对象引用的。在调用友元函数时也是同普通函数的调用一样不要像成员函数那样调用。本例中p1.Getxy()和p2.Getxy()这是成员函数的调用要用对象来表示。而Distance(p1, p2)是友元函数的调用它直接调用不需要对象表示它的参数是对象。(该程序的功能是已知两点坐标求出两点的距离。)下面对上面的代码进行输入、输出流的重载1234567891011121314151617181920212223242526272829303132333435363738394041424344454647#include cmath#include iostreamusingnamespacestd;classPoint{public:Point(doublexx,doubleyy){xxx;yyy;}voidGetXY();frienddoubleDistance(Point a,Point b);friendostream operator (ostream a,Point b);protected:private:doublex,y;};// friend ostream operator(ostream o,A another);ostream operator (ostream out,Point b)//在类中声明的时候可以是ostream a函数定义的时候也可以是ostream out{out(b.x,b.y)endl;returnout;}voidPoint::GetXY(){//cout(this-x,this-y)endl;//cout(x,y)endl;cout*this;}doubleDistance(Point a,Point b){doublelength;lengthsqrt((a.x-b.x)*(a.x-b.x)(a.y-b.y)*(a.y-b.y));returnlength;}intmain(void){Point p1(3.0,4.0),p2(6.0,8.0);p1.GetXY();p2.GetXY();doubled Distance(p1,p2);coutdendl;system(pause);return0;}2.类作为友元类作为友元需要注意的是友元类和原始类之间的相互依赖关系如果在友元类中定义的函数使用到了原始类的私有变量那么就需要在友元类定义的文件中包含原始类定义的头文件。但是在原始类的定义中包含友元类声明的那个类就不需要包含友元类的头文件.另外不需要在类定义前去声明友元类因为友元类的声明自身就是一种声明。1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556//A.h#pragma once#include iostreamusingnamespacestd;classA{//friend class B; //如果不写这句话将会出现编译错误public:~A(void);A();private:intm_nItem;};//A.cpp#include A.hA::A(){m_nItem 3;}A::~A(void){}//B.h#pragma onceclassB{public:B(void);~B(void);intfunc();};//B.cpp#include StdAfx.h#include B.h#include A.h //must include A.h#include iostreamB::B(void){}B::~B(void){}intB::func(){coutThis is in Bendl;A a;returna.m_nItem;}3.类成员函数作为友元函数这个稍微有点复杂因为你要类成员函数作为友元你在声明友元的时候要用类限定符所以必须先定义包含友元函数的类但是在定义友元的函数时候又必须事先定义原始类。通常的做法先定义包含友元函数的类再定义原始类这个顺序不能乱。(如果是友元类则没有这种这种必须)如下面所示12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849//A.h#pragma once#include B.hclassA{friendintB::func(A xx);public:A(void):mx(20),my(30){}~A(void){}private:intmx;intmy;};//B.h#pragma onceclassA;classB{public:B(void);~B(void);intfunc(A xx);};//B.cpp#include B.h#include A.hB::B(void){}B::~B(void){}intB::func(A xx){returnxx.mx * xx.my;}//main.cpp#include A.h#include B.h#include iostreamusingnamespacestd;voidmain(){A a;B b;coutb.func(a)endl;system(pause);}4. 友元不具有相互性只具有单项性若类B是类A的友元类A不一定是类B的友元要看在类中是否有相应的声明。5. 友元不能被继承B是A的友元类C是B的子类推不出C是A的友元6. 友元不具有传递性B是A的友元C是B的友元推不出C是A的友元7. 友元函数的使用技巧在用C实现单例模式时可以利用友元函数实例化对象。然后把类的构造函数和析构函数都设计成私有函数。1234567891011121314151617classCMySingleton{public:friendCMySingleton InstanceMEC();private:CMySingleton() {};CMySingleton(constCMySingleton lxSington) {};~CMySingleton(){};};CMySingleton InstanceMEC(){//因为函数InstanceMEC()是类ClxSingletonMEC的友元函数所以可以访问类所有的成员函数.所以不会有编译错误staticCMySingleton Instance;returnInstance;}以上就是本文的全部内容希望对大家的学习有所帮助