C++友元函数和友元类

来源:互联网 发布:mac怎么装双系统 编辑:程序博客网 时间:2024/05/07 20:27

今天在准备计算机等级考试的时候,被一系列的友元函数给搞混了,现在结合自己的理解和查阅的资料来总结下友元函数和友元类。

百度百科上对友元函数是这样定义的:友元函数是指某些虽然不是类成员却能够访问类的所有成员的函数。。类授予它的友元特别的访问权。通常同一个开发者会出于技术和非技术的原因,控制类的友元和成员函数(否则当你想更新你的类时,还要征得其它部分的拥有者的同意)。

为什么会有友元函数?

结合着类的特性和类中一般成员函数,我们可以这样理解:类具有封装和信息隐藏的特性。只有类的成员函数才能访问类的私有成员,程序中的其他函数是无法访问私有成员的。非成员函数可以访问类中的公有成员,但是如果将数据成员都定义为公有的,这又破坏了隐藏的特性。另外,应该看到在某些情况下,特别是在对某些成员函数多次调用时,由于参数传递,类型检查和安全性检查等都需要时间开销,而影响程序的运行效率。

为了解决上述问题,提出一种使用友元的方案。友元是一种定义在类外部的普通函数,但它需要在类体内进行说明,为了与该类的成员函数加以区别,在说明时前面加以关键字friend。友元不是成员函数,但是它可以访问类中的私有成员。友元的作用在于提高程序的运行效率,但是,它破坏了类的封装性和隐藏性,使得非成员函数可以访问类的私有成员。

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

 

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;<FONT> 

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<<"Distanceis"<< 

FONT>

}

 

说明:在该程序中的Point类中说明了一个友元函数Distance(),它在说明时前边加friend关键字,标识它不是成员函数,而是友元函数。它的定义方法与普通函数定义一样,而不同于成员函数的定义,因为它不需要指出所属的类。但是,它可以引用类中的私有成员,函数体中a.xb.xa.yb.y都是类的私有成员,它们是通过对象引用的。在调用友元函数时,也是同普通函数的调用一样,不要像成员函数那样调用。

本例中,p1.Getxy()p2.Getxy()这是成员函数的调用,要用对象来表示。而Distance(p1,p2)是友元函数的调用,它直接调用,不需要对象表示,它的参数是对象。

再举几个例子,我们来分析下全局函数作友元、其它类的成员函数作友元、运算符重载中使用友元的情况:

全局函数作友元

要使用全局函数作友元函数,也就是说全局函数中要使用当前这个类的实例,因此,全局函数的定义必须放在这个类的后面,否则,编译时这个类的实例就成了未定义的数据类型了。

 

#include <iostream>

#include <string>

using namespace std;

 

class ca {

   string id;

   void setId(string s) {

       id = s;

    }

protected:

   string name;

   void setName(string s) {

       name = s;

    }

public:

   void print() {

       cout << id << " " << name << " " << endl;

    }

   friend void fun(ca& a);    //申明全局函数作友元,允许它访问私有保护成员

};

 

class derive : public ca { };    //ca类的派生类

 

void fun(ca& a) {        //作友元的函数要定义在那个类的后面

   a.id = "987";        //这是ca类的私有成员

   a.setName("xyz");    //这是ca类的保护成员

}

 

int main ( )

{

   ca a;

   //a.fun(...);    //友元函数不是当前类的成员函数,所以这句编译出错

   fun(a);

   a.print();

 

   derive d;

   fun(d);    //作参数时,类型转换为ca类

   d.print();

 

   return 0;

}


运行结果为


其它类的成员函数作友元

别的类的成员函数作友元,也就是说这2个类相互调用,这样无论谁定义在前在后,编译时都有冲突。要解决这个问题,只要将类的申明、定义、实装分开就可以了。请注意例子中的解说。

 

#include <iostream>

#include <string>

using namespace std;

 

class ca;   //事先申明ca类,确保cb类的定义不出错

 

class cb {    //在ca类之前定义cb类,确保在ca类里申明cb的test()作友元时不出错

public:

   void test(ca& a);    //由于ca类事先申明,这儿不出错

};

 

class ca {

   string id;

   void setId(string s) {

       id = s;

    }

protected:

   string name;

   void setName(string s) {

       name = s;

    }

public:

   void print() {

       cout << id << " " << name << " " << endl;

    }

   friend void cb::test(ca& a);   //申明cb类的test()函数作友元,允许它访问私有保护成员

};

 

void cb::test(ca& a) {  //作友元的成员函数的实装必须在ca类的后面,否则ca类的成员就未定义了。

   a.id = "123";        //这是ca类的私有成员

   a.setName("abc");    //这是ca类的保护成员

}

 

int main ( )

{

   ca a;

   cb b;

   b.test(a);

   a.print();

 

   return 0;

}


运算符重载中使用友元

操作符重载由类的函数改为全局函数,也就是说,这个运算符不是这个类的,而是正常使用的操作符。注意重载函数的写法与上一章略有不同。不过,改为友元方式其实并没有必要,纯粹是为了讲解“友元”的用法。

#include <iostream>

#include <string>

using namespace std;

 

class rect {

   int x1, y1, x2, y2;    //矩形座标

public:

   rect() {

       x1 = 0, y1 = 0, x2 = 0, y2 = 0;

    }

   rect(int m1, int n1, int m2, int n2) {

       x1 = m1, y1 = n1, x2 = m2, y2 = n2;

    }

   void print() {

       cout << "  x1="<< x1;

       cout << "  y1="<< y1;

       cout << "  x2="<< x2;

       cout << "  y2="<< y2;

       cout << endl;

    }

   //rect operator++();                //这是类的运算符的重载

   friend rect operator++(rect &ob);  //这是全局运算符的重载

};

 

rect operator++(rect &ob) {

   ob.x1++, ob.y1++;

   ob.x2++, ob.y2++;

 

   return ob;

}

 

int main ( )

{

   rect r(12, 20, 50, 40);

    r.print();

 

   rect obj;

   obj = r++;

   obj.print();

 

   return 0;

}



并不是所有操作符都可以定义成友元,例如“=”就不能用友元方式重载


友元类

友元除了前面讲过的函数以外,友元还可以是类,即一个类可以作另一个类的友元。当一个类作为另一个类的友元时,这就意味着这个类的所有成员函数都是另一个类的友元函数。

使用友元类时注意:

(1) 友元关系不能被继承。

(2) 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。

(3) 友元关系不具有传递性。若类B是类A的友元,类CB的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明

总结起来:

(1)友元关系不可以继承,但对已有的方法来说访问权限不改变。

(2)如果改写基类的方法则访问权限改变

(3)友元关系不具有传递性

若类B是类A的友元,类CB的友元,类C不一定是类A的友元。

#include <iostream> 

 

using namespace std; 

 

class CObj 

public: 

   CObj() : mX(0), mY(0) {} 

   friend class CFriend; 

private: 

   void PrintData() const 

   { 

       cout << "mX = " << mX << endl 

            << "mY = " << mY << endl; 

   } 

   int mX; 

   int mY; 

}; 

 

class CFriend 

public: 

   CFriend(int x, int y) 

   { 

       mObj.mX = x;    //直接调用类CObj的私有数据成员 

       mObj.mY = y; 

   } 

   void ShowData() const 

   { 

       mObj.PrintData();   //直接调用类CObj的私有成员函数 

   } 

private: 

   CObj mObj; 

}; 

 

int main() 

{  

   CFriend one(3, 4); 

   one.ShowData(); 

   return 0; 

}

 

执行结果:

mX = 3

mY = 4



文章来源

http://www.cnblogs.com/fzhe/archive/2013/01/05/2846808.html


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 苹果手机出现不停播报情况怎么办 手做俯卧撑抬不起来怎么办 如果qq号被冻结了怎么办 买的qq号被冻结怎么办 联想平板怎么下载东西怎么办 所有浏览器都安装不了怎么办 忘记微博登录邮箱怎么办 丰巢验证码丢了怎么办 微博昵称忘了怎么办 163邮箱账号忘记了怎么办 苹果手机id邮箱忘记了怎么办 微信号没注销了怎么办 微信昵称换不了怎么办 微博支付密码忘记怎么办 微博支付密码忘记了怎么办 华为手机看不见截屏怎么办 支付宝密码错误被锁定怎么办 实名身份证的微信限额怎么办 工行网银登陆密码忘了怎么办 美亚买的东西坏了怎么办 海带宝转运到香港怎么办 海带宝转运仓库入库错误怎么办 三星s7edge密码忘了怎么办 ie网页被拦截了怎么办 手机打开百度网页弹出广告怎么办 网站策略服务已被禁用怎么办 谷歌浏览器打不开网页怎么办 mac用户名密码忘记了怎么办 苹果7网速太慢怎么办 ps中图片选不中怎么办 加装硬盘不显示怎么办 win10网络无权限访问怎么办 电脑装机时c盘隐藏怎么办 word不允许修改锁定了是怎么办 word文档只读不能编辑怎么办 word无法读取文档时怎么办 ps超出2g存不了怎么办 qq帐号不记得了怎么办 苹果手机wifi密码输错怎么办 qq搜索关键字屏蔽了怎么办 手机数据被屏蔽了怎么办