注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

慵懒的乌龟

——若有,且珍惜~

 
 
 

日志

 
 

(浅谈)面向对象程序设计的封装性【转】  

2011-01-01 11:36:22|  分类: C/C++ |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一:友元

     友元既可以是不属于任何类的一般函数,也可以是另一个类的成员函数,还可以是整个的一个类(这样,这个类中的所有成员函数都可能成为友元函数)。

   注意:友元关系是单向的,不具有交换性,友元关系也不具有传递性 。

二:C++友元机制来源与规则

前两天,一个朋友学C#问我,在c++中面向对象不是要进行数据封装吗,怎么会出现友元这种情况呢,明显的破坏封装性嘛。呵呵,当时我也没给说出什么来。后来看了点资料才发现,友元原来是这么来的。

      在C++ 刚刚产生的时候,它是符合斯图亚特模型的,具有面向对象的特征。但是后来发现类与类之间的交往比较的少,尽管子类可以访问父类的Protect属性的内容,但是,当时的类看起来就行一个孑然一身的人。后来,它的发明者布亚那决定向其中添入一些新的元素,这就是友元。他创建了友元声明的语法,并以此允许其他的类或函数访问类的私有或受保护的成员。这样,一个类就可以超越它的基类,并服务于它的友元类和友元函数,可以与它的友元类和友元函数合为一体。友元关系的价值在于C++类成员之影响力的传播,友元关系可促进C++王朝的繁荣,为那些有访问需求的类适当开放受保护成员的访问许可。友元关系,象其他访问许可一样,是由类授出的,而不是由友元类单方面攫取的。

    友元机制非常重要。它允许一个函数同时成为两个类的友元,这可以使类的设计者用友元函数来替代那些不适合开放给普通用户的成员函数。换句话说,使用友元函数可以增强接口的清晰度。这是因为,我们在两个类中提供相关的成员函数的做法通常会使接口变得更为复杂,合适的做法往往是将接口声明为全局函数,而普通的全局函数又无法访问类的私有和受保护的成员,这时,我们就需要用友元函数来创建更为清晰的接口。

在两个类间建立友元关系的惟一方法是将第二个类整个声明为第一个类的友元,友元关系不可继承,不可传递,禁止继承友元关系的规定有时并不受欢迎,因为它偶尔会妨碍用户实现某种功能。但是,设想一下,如果允许继承友元关系,那么,用户就有办法单方面攫取访问许可,声明受保护的成员才是允许用户单方面攫取访问许可的机制(对那些显式定义的接口来说)。

    我们知道友元关系将把所有访问权限提供给友元类。为了公平起见,两个类应当相互声明对方为友元。

由此可见,友元机制虽然在一定程度上破坏了数据的封装性,但是对于整个C++语言来说,却是使得C++的类更加鲜活。

下面为摘抄的:

加深概念理解,对“封装”性概念要从三个方面理解。

⑴所有软件的内部都应有明确的范围,清楚的外部边界;这就象公司的各部门有明确的职责范围、各部门之间有确定的权限界限。

⑵每个软件部件都应具有友好的界面或接口,以表明软件部件之间的相互作用和相互联系;这就象公司的各部门相互间的衔接工序

及各部门之间有联系与制约。

⑶完成、保护和隐藏软件的内部实现,用户不必了解其具体的实现;这就像一个工厂的各个车间生产不同的零部件,本车间完成自

己的工作,对外提供成品零部件,使用零部件者不必知道其生产过程。

理解“封装”性的好处。

有了封装性,软件设计人员可以集中精力考虑开发系统各模块之间的关系等重大问题,而模块内部的实现可得到程序设计人员的研

究与完善,可以充分保证模块质量和可靠性,也支持软件工程化思想。

2、通过实例巩固概念通过总结加深概念

在教材的第十章“类与对象”中是封装性的具体体现。在这里我们可以看到对象通过类来定义。也就是说,类是进行封装和数据隐

藏的工具,进行数据和方法封装的基本逻辑单位。类中可以定义公有成员、保护成员和私有成员。公有成员可被任何函数访问;保

护成员可被该类的成员函数和相应的友元函数引用;而私有成员则只能为相应类的成员函数和相应的友元函数引用。引入私有成员

封装性和友元封装性的实例:

⑴通过对象的私有成员实例理解封装

#include

class personnel

{ public:

personnel (int mon)

{ money=mon;

}

void get_salary (personnel &p,int mon)

{ p.money-+mon;

money +=mon;

}

int get_mon ()

{ return money;

}

private:

int money; //私有成员

};

void main() {

personnel boss(1000); // 创建boss 时付给其1000元

personnel employee(0);

cout<<”Before get_salary,boss:”<
<<”employee:”<
employee.get_salary(boss,200); // employee从boss领取200元

cout<<”After get_salary,boss:”<
<<”employee:”<
}

运行结果:

Before get_salary,boss: 1000 employee: 0

After get_salary,boss: 800 employee: 200

通过这个实例我们看到:在程序中定义了一个表示公司所有职员的personnel类,其中的成员函数get_salary (personnel &p,int

mon)可以改变私有成员money。在主函数中,首先在创建boss 时付给其1000元,employee从boss领取200元,即执行

employee.get_salary(boss,200)之后,employee的money增加了200元;而boss的money减少了200元。Employee通过成员函数操作改

变了boss的money。

上述实例告诉我们:程序编译过程中,编译器认为通过personnel &p的声明,p本身已经属于personnel 类,在get_salary

(personnel &p,int mon)中直接操作其私有数据成员属合法操作,如果有非成员函数对其进行操作就属于非法了。

⑵通过友元的实例理解封装

#include

#include

class point //定义一个类

{ public:

point (float xi,float yi)

{ X=xi,Y=yi;

}

float Get_X() {return X;}

float Get_Y() {return Y;}

friend float distance (point &a,point &b); //声明为类的友元

private:

float X,Y; //类的私有成员

};

float distance (point &a,point &b)

{

float dx = a.X-b.X; //因为是友元函数而访问类的私有成员X

float dy = a.Y-b.Y; //因为是友元函数而访问类的私有成员Y

return sqrt (dx*dx+dy*dy);

}

void main() {

point p1(3.0,5.0),p2(4.0,6.0); //创建类的对象

float d = distance (p1,p2); //通过友元函数访问类的私有成员

cout <<”The distance is:”<
}

运行结果:

The distance is:1.41421

在上面的例子中,我们看到只使用了float d = distance (p1,p2)就完成了对point类的对象p1和p2的访问。如果不采用友元,主函

数应该增加:

float dx = p1.Get_X()-p2.Get_X();

float dy = p1.Get_Y()-p2.Get_Y();

float distance = sqrt(dx*dx+dy*dy);

才能完成上述程序的功能。由此可见引入友元后可以简化程序设计者的烦琐工作。

3、通过总结加深概念

当同学们按照上述两步完成自学后,应当根据自己的体会进行总结。下面是引导性总结提纲:

⑴在C++面向对象程序设计语言中封装单元是什么?(类型)

⑵判断多个对象是否属于同一类的依据是什么?(是否符合相同的类型系统)

⑶程序中相同类的不同实例对象之间是否可以互访对方的私有成员?(可以——例1)

⑷引入友元的优点是什么?(节省成员函数调用的开销,提高程序效率——例2)

⑸类的设计者是否必须在考虑好该类的各种可能的使用情况后才能开始设计该类,为什么?(不必,类的使用者可以根据具体要求

通过友元增加类的接口)

⑹使用友元要注意什么问题?(引入友元是人为地破坏了对象间的松耦合关系,削弱C++的封装和数据隐藏,可能导致程序的可维护

性和可读性较差,必须引起足够重视)

 

仁者见仁智者见智:(下面是别人对OO的封装的见解)

一:

  我觉的封装的目的在于把问题简单化,为了可以实现更强大,更复杂的软件.把问题局部话,便于检查和解决.  
  可以提高开发的效率.  
   
  举个例子吧.  
  我要组装一台电脑,只要买来主机箱,cpu,硬盘,光驱,主机板,内存,显示器就可以了.  
  那我就没有必要从开采提炼单晶硅开始做起.即使电脑的光驱坏了,我也只需要把光驱拆下了,换一个就行了,没有必要把整台电脑都扔了,全部换新的.  
   
   
  对于你说的强制类型转换,我也举一个例子来说明:  
  一般来讲,我们认为把钱放到银行的保险柜里面是比较安全的.  
  但是当你碰到了一个强大的超人.他动用包括核子武器在内的一切武器来工具银行时,请问你的钱还安全吗?

 

二:

     面向对象的意义就在于对象的共享,这也是多态和继承特性的目的,同时为了保护共享的安全,又引入了封装这个概念。如果开发出来的对象或者组件只是为本地使用的话,程序员根本就没必要设计面向对象的程序,事实上,很多人写的C++代码只能称得上带类的C,根本也不会去考虑封装继承什么的,我这个帖子讨论的当然也不是针对这些程序的。

 

三:

封装是为了使接口和实现分离,而不是为了方便。实际上封装往往会降低使用的方便度。  
  但是接口和实现分离,使得内部实现的修改不影响外部的调用者,从而提高程序对修改的适应度。  
   
  一种方法可以实现完全封装,即使强制转换也无用。  
   
  在头文件中声明:  
  class   A   {  
  public:  
          void   method();  
          //   ...  
  private:  
          friend   struct   A_Impl;  
          A_Impl*   impl;  
  };  
   
  然后在源文件中声明:  
  struct   A_Impl   {  
          //   all   A's   data   members...  
  };  
   
  void   A::method()   {   //   ...   }  
   
  //...  
   
  所有的数据成员都放到   A_Impl   的结构中,这样   A   的数据成员对外界完全不可见。  
  此法对大工程里提高编译速度较有用

 

四:我的几点总结  

  1)友元函数破坏了数据的隐蔽性,违背了面向对象程序设计的思想。

  2)  类的使用者可以根据具体要求通过友元增加类的接口。

3)在类的外部不能使用类的私有成员,这儿的私有成员包括:私有的数据成员和私有的成员函数。

  4)尽可能隐藏类内部的实现细节,对外形成一个边界或对外形成一个屏障。

5)this代表本对象的属性

6)JAVA里有垃圾回收机制,而C++需要回收垃圾内存。

 

 

感谢:http://blog.163.com/jdxxf@126/blog/static/141007095201044111124421/

  评论这张
 
阅读(537)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017