深入浅出话友元

小序:
          有一阵子没来打扫 Blog ……技术这东西,就是走走停停的,一段时间就会遇到一个瓶颈、迷茫一下,然后发现与其因为迷茫而停滞不前,不如瞄准一个大方向勇敢地游下去。
          这两天有几个正在学习 C++ 的同学问我什么是“友元”、 C# 里有没有“友元”, C++ 里的 friend 关键字是不是就相当于 C# 里的 protected 关键字。
         OK ,今天就在这里稍微讲解一下。
 
正文:

正统的面向对象与革新

          从面向对象思想的角度上来看, C++/Java 所持有的思想是“传统的”或者是“正统的” OO 思想。 C# 是对传统 OO 思想的改进和创新。比如, C# 中包含的“属性”( Property )就是对传统 OO 思想中“私有变量 +Set/Get 函数对”的一种改进革新; C# 中的“委托”( Delegate )就是对 C/C++ 中函数指针的升级——不但安全,而且多播(请大家参阅我的《深入浅出话委托》一文); C# 中的“事件”本质仍然是委托,因为它就是一个被 event 关键字修饰的委托的实例 ,但对于类与类之间的消息传递来说,这绝对称得上是微软的一种创新。总之,微软对 OO 思想的革新使 OO 在语言层面上实现起来更加方便、更加自然。
         OO 的三大特征:封装、继承、多态(也有说是“自定义类、继承、多态”的,见《 C++ Primer 4th 》)中,设计类的继承是个很庞杂的工作。其中需要反复思量的一个问题就是类中所封装的变量与方法的可访问性(或者称为“访问级别”)——谁可以访问到?谁不能访问到?这里的“谁”指代的是包括被访问的实体(比如一个变量或一个函数)的宿主(即包含这个实体的类)在内的程序中所有的类。
          对于一个被封装的实体来说,谁都访问不到它显然是不行的——那就彻底没用了。那么如果让所有类都能访问得到,岂不美哉?的确美哉!前提是使用这个类的其它程序员一定要像这个类的设计人员一样了解它。这就有两个问题出现了:
l          类的使用者要被迫接受很多信息,知识量大,过于庞杂,不能专注于应该了解的功能。这个问题导致的直接后果就是使开发速度降低。
l          一个好的类,内部设计是非常精巧的(高内聚),每个被封装的实体都可能起着或重要、或微妙的作用,使用者一不小心就会产生“牵一发而动全身”的错误,造成对整个项目的影响——我们称这种错误为类的“用户级错误”(与类的“设计级错误”相区别)。
所以,一个类留给用户程序员的接口并不是功能越多越好,而是越干净越好。换句话说就是:该隐藏的隐藏,该暴露的暴露,不给使用者留有犯错误的机会。
本着这个原则, C++ 给出了三个访问控制级别—— public、 private和protected ——要么所有类都能访问得到(public),要么只有宿主类里的成员可以访问得到(private),要么只有派生类可以访问得到(protected)。这三个访问级别对付大多数项目已经足够了,而 C# 在语言层面上又进行了改进,使之方便了不少:
l          保留 public 关键字:与 C++ 一致,表示程序中的所有类均可以访问其修饰目标。
l          保留 private 关键字:与 C++ 一致,表示仅宿主类可以访问其修饰目标。
l          保留 protected 关键字:与C++一致,表示可以在宿主类的派生类中访问到其修饰目标。
l          新增 internal 关键字:表示同一个程序集( Assembly 中的所有类可以访问到其修饰目标。
注意:程序集是 .NET 技术的一个新概念,又称“托管程序集”,一般情况下一个程序集不是一个托管的 .DLL 文件就是一个托管的 .EXE 文件。所谓“托管”就是这样的文件只能运行在 .NET 平台上,即 Windows 把这些程序“拜托”给 .NET 虚拟机来管理执行了。而且,使用 Visual Studio ,只能写出一个 DLL/EXE 只包含一个程序集的程序来,如果你想在一个 DLL/EXE 里包含多个程序集(就像双黄蛋那样),对不起,你得自己用命令行手动编译。请参阅《 CLR via C# 》或者这本书的上一个版本。

友元

          一直到这儿“友元”还没露面儿……
          呵呵,不把上面的东西搞明白,怎么能知道“友元”的威力呢?上面提到的都是“规规矩矩”的访问控制。然而,总是规规矩矩办事,难免会有不方便的时候——比如我向我同桌的刘蕾同学借把剪刀也要经过领导进行一下资产借调审批流程、算一算部门折旧与均摊,要么领导疯掉,要么我被勒令去开弱智证明……
          好吧,不瞒您说,“友元”就是一个“不规矩”的产物——在 C++ 的某个类中,使用了 friend 关键字修饰的实体就称为“友元”——能被 friend 关键字修饰的实体只有两种,要么是个类(称为“友元类”)要么是个函数(称为“友元函数”)。被 friend 修饰之后,朋友吗!那就可以随便一些、不那么规矩啦!所以,友元类 / 友元函数可以访问到将它声明为友元的类的 private 成员——而不去管它有没有继承关系、是不是 public 的。
          看下面的例子:
#include  <iostream>

//
友元的宿主类
class  Man
{
friend class Thief; //
声明友元类

public :
         Man(): money(100){}; //
构造函数,将初始的金钱数设置为100
         
         void SpendMoney()
         {
                   money --;
         }

         int ReportMoney()
         {
                   return money;
         }
private:
         int money;
};

//
友元类的定义
class  Thief
{
public:
         void ThieveMoney(Man& haplessMan)
         {
                   haplessMan.money -= 10;
         }
};

int main(int argc, char *argv[])
{
         Man man;
         man.SpendMoney();
         std::cout<<man.ReportMoney()<<std::endl;

         Thief thief;
         thief.ThieveMoney(man);
         std::cout<<man.ReportMoney()<<std::endl;
         return 0;
}


输出的结果是这样的:
显然,我们这位“盗贼朋友”是偷窃成功了的!请大家注意: Man 类中的 money 成员是 private 的,而正因为它用 friend 关键字修饰了 class Thief ——这种以贼为“友”、引狼入室的行为,造成了 Thief 类也可以访问 Man 兜里的 monry…
          呵呵,其实上面只是举个例子说明友元的工作原理,并不是要说明友元是危险的。恰恰相反,正因为有了友元,所以才使访问变得不但方便而且安全了——
l          方便:想让谁能访问到,在类里把它修饰成 friend 就行了。
l          安全:有限度地向需要访问的类 / 函数开放访问权限,总比声明成 public 要安全得多吧 J

关于C#

         C# 中没有友元的概念,只是把访问级别、访问范围划分的更细致了。而且在 C# 中, private 就是 private ,除了修饰目标的宿主类之外,谁也访问不到 private 级别的封装实体。
protected 也是一种“有限度”的开放 ,不过它的开放范围不是面向某一个类(只面向类,不面向函数)而是面向其所有的直接 / 间接派生类。之所以称为 protected ,意思是说这种开放是具有“家族性”的,是“传家宝”而需要保护起来、不让继承链之外的类看到——哪怕是个破笤帚疙瘩也要一代一代传下去!哈哈。
最后再妄自揣测一下 C# 里为什么没有 friend 吧……其实,当你向一个类中添加友元的时候,实际上是添加了这两个类之间的耦合,而且是个别添加而非面向某一组具有特定条件的类——这似乎是一种无规则的、无原则的“感情用事”,很危险,会把程序搞乱。
 

问题

          末了,抛给大家一个问题:如果一个类继承自 Thief ,但这个类并未声名为 Man 的友元,那么它能不能访问 Man money 成员呢?
class  LThief: Thief
{
         //
类体为空即可。
};
         
呵呵……这个问题会引出另一个问题—— public 继承与 private 继承的区别 。咱们有空再侃!









本文转自 水之真谛 51CTO博客,原文链接:http://blog.51cto.com/liutiemeng/18749,如需转载请自行联系原作者
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值