怎样声明两个类互为友元函数的声明类,该如何解决

您所在的位置: &
浅析C++类的继承
浅析C++类的继承
在C++中,假如C++基类成员中的私有成员是别的类,但是对成员是不能访问的,在进行对C++基类进行操作时,需要很高的细心。
也许大家对与C++基类还不是很了解,看完本文后对您一定会大有帮助,下文除了学习C++基类的基本性质外还对C++基类的新特性进行全面研究,希望大家能够找到自己想要的东西。
而基类中的公有成员在public派生时,不仅可以由派生类对象成员访问,也可以由外部函数访问;而在private派生时,C++基类中的公有成员虽然允许派生类对象中的成员访问,不允许外部访问。
可是再派生出下一级时,由于C++基类的所有成员已经被私有化,其它类成员也不可再访问。实现只许有派生血缘关系的对象成员访问的方法,是在基类中使用具有另一种访问属性的成员――protected成员。
protected成员是一种血缘关系内外有别的成员。它对派生对象而言,是公有成员,可以访问;对血缘关系外部而言,与私有成员一样被隐藏,此外,除了允许使用private与public两种派生方式之外。C++还允许使用protected派生方式。现在将三种访问属性不同的成员经三种派生后在派生类中访问属性的变化情况总结如下表,是对上一表格的增进和补充。
基类的public成员
基类的protected成员
基类的private成员
派生方式引起的访问属性变化概括
private派生
变为private成员
变为private成员
基类中的非私有成员都成为派生类中的私有成员
protected派生
变为protected成员
变为private成员
基类中的非私有成员在派生类中的访问属性都降一级
public派生
仍为public成员
仍为protected成员
基类中的非私有成员在派生类中的访问属性保持不变
需要注意的是,C++基类的private成员无论经过何种派生,在派生类中都是不可见的。
(1)友元函数
通常,类的私有成员只能由本类的成员访问,外部函数只能访问类的成员函数,再由成员函数访问类的私有成员。但是,如果在某个类定义中用friend声明了一个外部函数(也许是其他类的一个成员)后,这个外部函数便可以例外地访问该类的任何私有成员。用friend声明了的外部函数称为这个类的友元函数。
当友元函数是另一个类的成员函数时,应当注意以下几点:
A:友元函数作为一个类的成员函数时,除应当在它所在的类定义中声明之外,还应当在另一个类中声明它的友元关系,声明语句的格式为:
friend 函数类型函数所在类名::函数名(参数列表);
B:友元函数在引用本类对象的私有成员时无需本类对象的引用参数,但在引用生命它是友元的类的对象中的私有成员时必须有友元类对象的引用参数。
C:一个类的成员函数作另一个类的友元函数时,必须先定义,而不是仅仅声明它。
使用友元函数直接访问对象的私有成员,可以免去再调用类的成员函数所需的开销。同时,友元函数作为类的一个接口,对已经设计好的类,只要增加一条声明语句,便可以使用外部函数来补充它的功能,或架起不同类对象之间联系的桥梁。然而,它同时也破坏了对象封装与信息隐藏,使用时需要谨慎小心。
(2)友元类
也可以把一个类而不仅仅是一个函数声明为另一个C++基类的友元类。这时,只需先声明它而不一定需要先定义。应当注意,友元关系是单向的,并且只在两个类之间有效。即使类X是类Y的友元。
类Y是否是类X的友元也要看类X中是否有相应的声明。即友元关系不具有交换性。若类X是类Y的友元,类Y是类Z的友元,也不一定就说明类X是类Z的友元,即友元关系也不具有传递性。当一个类要和另一个类协同工作时,使一个类成为另一个类的友元类是很有用的。这时友元类中的每一个成员函数都成为了对方的友元函数。【责任编辑: TEL:(010)】
关于的更多文章
想要理解大数据,使之更贴近大多数人,最重要的手段的之一就是数
随着云计算、物联网、大数据、移动互联网的大发展,你应该知道这些。
讲师: 13人学习过讲师: 9人学习过讲师: 11人学习过
1314的的日子在,在忙忙碌碌中过去了。一周五天,中间
本期开发频道重点推荐是2013年开发频道重点推荐的最后
如何看将流行的JavaScript MVC框架Ember.js? 这款Java
本书详细介绍了AJAX在Web开发上的应用。主要内容包括:ASP.NET AJAX技术概述、实现异步局部更新页面、UpdatePanel编程功能、Page
51CTO旗下网站用户名:水之真谛
文章数:176
评论数:337
访问量:1297981
注册日期:
阅读量:1297
阅读量:3317
阅读量:444805
阅读量:1130551
51CTO推荐博文
……技术这东西,就是走走停停的,一段时间就会遇到一个瓶颈、迷茫一下,然后发现与其因为迷茫而停滞不前,不如瞄准一个大方向勇敢地游下去。
这两天有几个正在学习的同学问我什么是“友元”、里有没有“友元”,里的关键字是不是就相当于里的protected关键字。
OK,今天就在这里稍微讲解一下。
从面向对象思想的角度上来看,所持有的思想是“传统的”或者是“正统的”思想。是对传统思想的改进和创新。比如,中包含的“属性”()就是对传统思想中“私有变量函数对”的一种改进革新;中的“委托”()就是对中函数指针的升级――不但安全,而且多播(请大家参阅我的《深入浅出话委托》一文);中的“事件”本质仍然是委托,因为它就是一个被关键字修饰的委托的实例,但对于类与类之间的消息传递来说,这绝对称得上是微软的一种创新。总之,微软对思想的革新使在语言层面上实现起来更加方便、更加自然。
OO的三大特征:封装、继承、多态(也有说是“自定义类、继承、多态”的,见《》)中,设计类的继承是个很庞杂的工作。其中需要反复思量的一个问题就是类中所封装的变量与方法的可访问性(或者称为“访问级别”)――谁可以访问到?谁不能访问到?这里的“谁”指代的是包括被访问的实体(比如一个变量或一个函数)的宿主(即包含这个实体的类)在内的程序中所有的类。
对于一个被封装的实体来说,谁都访问不到它显然是不行的――那就彻底没用了。那么如果让所有类都能访问得到,岂不美哉?的确美哉!前提是使用这个类的其它程序员一定要像这个类的设计人员一样了解它。这就有两个问题出现了:
l类的使用者要被迫接受很多信息,知识量大,过于庞杂,不能专注于应该了解的功能。这个问题导致的直接后果就是使开发速度降低。
l一个好的类,内部设计是非常精巧的(高内聚),每个被封装的实体都可能起着或重要、或微妙的作用,使用者一不小心就会产生“牵一发而动全身”的错误,造成对整个项目的影响――我们称这种错误为类的“用户级错误”(与类的“设计级错误”相区别)。
所以,一个类留给用户程序员的接口并不是功能越多越好,而是越干净越好。换句话说就是:该隐藏的隐藏,该暴露的暴露,不给使用者留有犯错误的机会。
本着这个原则,给出了三个访问控制级别――――要么所有类都能访问得到(),要么只有宿主类里的成员可以访问得到(),要么只有派生类可以访问得到(protected)。这三个访问级别对付大多数项目已经足够了,而在语言层面上又进行了改进,使之方便了不少:
l保留关键字:与一致,表示程序中的所有类均可以访问其修饰目标。
l保留关键字:与一致,表示仅宿主类可以访问其修饰目标。
l保留关键字:与一致,表示可以在宿主类的派生类中访问到其修饰目标。
l新增关键字:表示同一个程序集()中的所有类可以访问到其修饰目标。
注意:程序集是技术的一个新概念,又称“托管程序集”,一般情况下一个程序集不是一个托管的文件就是一个托管的文件。所谓“托管”就是这样的文件只能运行在平台上,即把这些程序“拜托”给虚拟机来管理执行了。而且,使用,只能写出一个只包含一个程序集的程序来,如果你想在一个里包含多个程序集(就像双黄蛋那样),对不起,你得自己用命令行手动编译。请参阅《》或者这本书的上一个版本。
一直到这儿“友元”还没露面儿……
呵呵,不把上面的东西搞明白,怎么能知道“友元”的威力呢?上面提到的都是“规规矩矩”的访问控制。然而,总是规规矩矩办事,难免会有不方便的时候――比如我向我同桌的刘蕾同学借把剪刀也要经过领导进行一下资产借调审批流程、算一算部门折旧与均摊,要么领导疯掉,要么我被勒令去开弱智证明……
好吧,不瞒您说,“友元”就是一个“不规矩”的产物――在的某个类中,使用了关键字修饰的实体就称为“友元”――能被关键字修饰的实体只有两种,要么是个类(称为“友元类”)要么是个函数(称为“友元函数”)。被修饰之后,朋友吗!那就可以随便一些、不那么规矩啦!所以,友元类友元函数可以访问到将它声明为友元的类的成员――而不去管它有没有继承关系、是不是的。
看下面的例子:
#include &iostream&//友元的宿主类class Man{friend class T //声明友元类public:&&&&&&&& Man(): money(100){}; //构造函数,将初始的金钱数设置为100&&&&&&&& &&&&&&&& void SpendMoney()&&&&&&&& {&&&&&&&&&&&&&&&&&& money --;&&&&&&&& }&&&&&&&& int ReportMoney()&&&&&&&& {&&&&&&&&&&&&&&&&&& return&&&&&&&& }private:&&&&&&&& int};//友元类的定义class Thief{public:&&&&&&&& void ThieveMoney(Man& haplessMan)&&&&&&&& {&&&&&&&&&&&&&&&&&& haplessMan.money -= 10;&&&&&&&& }};int main(int argc, char *argv[]){&&&&&&&& M&&&&&&&& man.SpendMoney();&&&&&&&& std::cout&&man.ReportMoney()&&std::&&&&&&&& T&&&&&&&& thief.ThieveMoney(man);&&&&&&&& std::cout&&man.ReportMoney()&&std::&&&&&&&& return 0;}
输出的结果是这样的:
显然,我们这位“盗贼朋友”是偷窃成功了的!请大家注意:类中的成员是的,而正因为它用关键字修饰了――这种以贼为“友”、引狼入室的行为,造成了类也可以访问兜里的
呵呵,其实上面只是举个例子说明友元的工作原理,并不是要说明友元是危险的。恰恰相反,正因为有了友元,所以才使访问变得不但方便而且安全了――
l方便:想让谁能访问到,在类里把它修饰成就行了。
l安全:有限度地向需要访问的类函数开放访问权限,总比声明成要安全得多吧J
C#中没有友元的概念,只是把访问级别、访问范围划分的更细致了。而且在中,就是,除了修饰目标的宿主类之外,谁也访问不到级别的封装实体。
也是一种“有限度”的开放,不过它的开放范围不是面向某一个类(只面向类,不面向函数)而是面向其所有的直接间接派生类。之所以称为,意思是说这种开放是具有“家族性”的,是“传家宝”而需要保护起来、不让继承链之外的类看到――哪怕是个破笤帚疙瘩也要一代一代传下去!哈哈。
最后再妄自揣测一下里为什么没有吧……其实,当你向一个类中添加友元的时候,实际上是添加了这两个类之间的耦合,而且是个别添加而非面向某一组具有特定条件的类――这似乎是一种无规则的、无原则的“感情用事”,很危险,会把程序搞乱。
末了,抛给大家一个问题:如果一个类继承自,但这个类并未声名为的友元,那么它能不能访问的成员呢?
class LThief: Thief{&&&&&&&& //类体为空即可。};呵呵……这个问题会引出另一个问题――继承与继承的区别。咱们有空再侃!
征婚啦、征婚啦!!
小猫是我的好朋友,希望大家在转载这篇文章的时候不要把征婚这一栏去掉――去掉算你篡改原文,谁去掉我跟谁急!!但愿给小猫征婚的消息能够随着我的文章漂洋过海让可爱的小猫遇到知心善良的……
尽一份朋友的责任,愿每个朋友都找到自己的知心伴侣,愿天下有情人终成眷属!!
了这篇文章
类别:┆阅读(0)┆评论(0)两个类互为对方友元_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
两个类互为对方友元
阅读已结束,如果下载本文需要使用1下载券
想免费下载本文?
定制HR最喜欢的简历
你可能喜欢用户名:青乡之b
文章数:17
访问量:14685
注册日期:
阅读量:1297
阅读量:3317
阅读量:444805
阅读量:1130551
51CTO推荐博文
一、类的组合
引言:由于需要分工合作,所以便有了类与类之间的组合关系。一个类可以只干它自己关心的事,而当另一个类需要使用它的功能时,直接嵌套它即可。
#include &stdafx.h&
#include&iostream&
#include&cmath&
//定义Point类
class Point{
&Point(int x=0,int y=0): x(x),y(y){}
&int getX(){}
&int getY(){}
//定义Line类
class Line{
&Line(Point p1,Point p2);
&double getLength(){}
&Point p1,p2;//①所谓类的组合,指的是类中内嵌其它类的对象作为成员数据!
Line::Line(Point p1,Point p2): p1(p1),p2(p2){
&double x=static_cast&double&(p1.getX()-p2.getX());//类的组合中,还是只能通过对象引用其成员函数(对于两个类而言,它们只是分工不同而已,它们并没有特殊关系)
&double y=static_cast&double&(p1.getY()-p2.getY());
&length=sqrt(x*x+y*y);
int _tmain(int argc, _TCHAR* argv[])
&Point myp1(1,1),myp2(4,5);//定义Point类的对象
&Line line(myp1,myp2);//定义Line类的对象!
&cout&&&the length of line:&;
&cout&&line.getLength()&&;
&return 0;
二、友元类:描述的是类与类之间的关系,也即共享。
&void display(){ cout&&x&&}
&int getX(){}
&friend class B;//声明B是A的友元类
&void set(int i);
&void display();
void B::set(int i){
&a.x=i;//在类B中,可以通过对象访问类A的私有成员
【注意】 ①友元类是单向关系,即B&&&A;②友元类不能派生,即B跟A的子类无关;③友元类也不能传递,即C&&&B&&&A不能直接C&&&A。
关于友元函数:描述的是友元函数与该类的关系,似乎没起到什么大的用处,下面的程序是求线段长的另一种方法。
#include &stdafx.h&
#include&iostream&
#include&cmath&
//定义Point类
class Point{
&Point(int x=0,int y=0): x(x),y(y){}
&int getX(){}
&int getY(){}
&friend float dist(Point &p1,Point &p2);//①友元函数是在类中声明的非成员函数!
//友元函数的实现
float dist(Point &p1,Point &p2){&&& //②因为是非成员函数,所以它的实现不需要在前面加上Point::作用域
&double x=p1.x-p2.x;//③友元函数可以通过对象访问类的私有成员数据(这和普通成员函数有区别吗?)
&double y=p1.y-p2.y;
&return static_cast&float&(sqrt(x*x+y*y));
int _tmain(int argc, _TCHAR* argv[])
&Point myp1(1,1),myp2(4,5);
&cout&&&the distance is:&;
&cout&&dist(myp1,myp2)&&
&return 0;
------------------------------------------------------------------------------------------
就本题的求线段长而言,个人觉得使用static成员函数来的更方便、更容易理解,源程序如下:
#include &stdafx.h&
#include&iostream&
#include&cmath&
//定义Point类
class Point{
&Point(int x=0,int y=0): x(x),y(y){}
&int getX(){}
&int getY(){}
&static float dist(Point &p1,Point &p2);//静态成员函数
//静态成员函数的实现
float Point::dist(Point &p1,Point &p2){&&&
&double x=p1.x-p2.x;
&double y=p1.y-p2.y;
&return static_cast&float&(sqrt(x*x+y*y));
int _tmain(int argc, _TCHAR* argv[])
&Point myp1(1,1),myp2(4,5);
&cout&&&the distance is:&;
&cout&&Point::dist(myp1,myp2)&&
&return 0;
【总结】 后两种与类的组合区别是:类的组合中是分工合作,有嵌套,求线段长度依赖于Line类;而后者求线段长度主要依赖于成员函数/友元函数来实现!!
了这篇文章
类别:未分类┆阅读(0)┆评论(0)

我要回帖

更多关于 友元函数声明 的文章

 

随机推荐