在前一篇文章,我们提到,使用private来代替public以提高class的封装性。这一篇文章,我们将对接口发起攻势。首先来个简单的例子。

class WebBrowser
{
public:
void clearCache();
void clearHistory();
void removeCookies();
void clearEverything()
{
clearCache();
clearHistory();
removeCookies();
}
};

在上面的例子中,我们定义了一个网页浏览器的类,可以清楚cache,历史记录,以及cookies。然后我们有时希望这三个步骤可以一次性执行,所以定义了clearEverything。当然我们也可以在外部定义一个non-member函数实现同样的功能。问题就在于member与non-member之间的抉择。

首先说一个面向对象的守则:数据以及操作数据的那些函数应该捆绑在一起,这意味着使用member似乎会更好一点。其实这是一个误解,面向对象守则要求数据应该尽可能被封装,然而直观上,member函数带来的封装性比non-member函数的封装性差。

为什么这么说呢?我们从封装开始讨论。如果某些东西被封装,它就不再可见。越多东西被封装,越少人可以看到它。而越少人看到它,我们变化它的弹性就越大,因为我们的改变仅仅影响那些看到它的人而已。因此,越多的东西被封装,我们改变那些东西的能力就越大,这就是我们推崇封装的原因:它使我们能够改变事物而只影响有限客户。而我们可以使用一种粗糙的量测方法来考虑数据的封装性:计算能够访问该数据的函数数量。

一个public变量,是毫无封装性的。假如要在member,non-member,non-friend之间做抉择,并且它们的性能一致,导致较大封装性的是non-member,non-friend函数,因为它不增加”能够访问class内private成分"的函数数量。所以在上述的例子中,使用一个non-member,non-friend函数能够让我们获得较大的封装性。

这里特别需要注意的是,这里提供较大封装性的是指non-member,non-friend函数,而不是non-member,因为friend对于封装也具有相当大的冲击力道。

第二件需要注意的是:”A成为B的non-member“不意味着“不能作为C的member”。其实这一点非常容易理解,就是不同的类之间,内部的函数没有任何相互影响。在C++中,有一种比较好的处理方式,就是将non-menber函数与class放在同一个namespace中。如下

namespace WebBrowserStuff
{
class WebBrowser();
void clearBrower();
};

这样,一个WebBrowser就能够拥有许多便利的函数,并且不会对其它的代码造成影响。比如我们有一些跟书签有关的便利函数,一些跟cookie相关的便利函数,我们可以使用如下的方法组织起来。

//WebBrowser.h
namespace WebBrowserStuff
{
class WebBrowser();
... //放入所有客户都可能用到的代码,non-member函数
}
//WebBrowserBookmark.h
namaspace WebBrowserStuff
{
... //放入与书签相关的代码,non-member函数
}
//WebBrowseCookies.h
namespace WebBrowserStuff
{
... //放入与cookies的代码,non-member函数
}

注意:这里是将non-member函数根据功能放入到不同的文件中,不是将WebBrowser类放入不同的文件。那样将导致,编译器只认可第一个声明的class(visual studio2013测试)

当我们只需要WebBrowser的基本功能的时候,只需要导入WebBrowser.h,当我们需要与书签相关的功能时,再导入WebBrowserBookmark.h。当我们需要cookies相关的功能,而不需要与书签相关的功能时,再导入WebBrowseCookies.h。其实这正是STL库的组织形式。比如,vector,list等都有自己的头文件,但是他们都是处在同一个命名空间namespace中。当我们需要使用到其中的某个容器的时候,就包含相关的头文件。

总结一下

  1. 使用non-member,non-friend函数提高class的封装性
  2. 使用non-member,non-friend时可以借助namespace来提高程序的可拓展性。

太过亲密往往不好——用non-member,non-friend替换member函数的更多相关文章

  1. 理解#define offsetof(struct_t,member) ((int)&((struct_t *)0)->member)

    #define offsetof(struct_t,member) ((int)&((struct_t *)0)->member) 这个东西很多人应该知道: offsetof是用来判断结 ...

  2. Effective C++ -----条款23:宁以non-member、non-friend替换member函数

    宁可拿non-member non-friend函数替换member函数.这样做可以增加封装性.包裹弹性(packaging flexibility)和机能扩充性.

  3. #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

    #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)宏的运行机理:1. ( (TYPE *)0 ) 将零转型为TY ...

  4. [Effective C++ --023]宁以non-member、non-friend替换member函数

    作者在这一节中花了大幅度的篇幅来介绍为什么最好使用non-member.non-friend函数. 思路如下: 场景:如果有一个class用来表示网页浏览器,那么清楚缓存及历史记录的时候,我们可能定义 ...

  5. Effective C++:条款23:宁以non-member、non-friend替换member函数

    (一) 有个class来表示网页浏览器: class WebBrowser { public: void clearChache(); void clearHistory(); void remove ...

  6. 条款23:宁以non-member, non-friend,替换member函数。

    考虑下面这种经常出现的使用方式: class webBroswer{ public: ... void clearCache(); void clearHistory(); void removeCo ...

  7. 条款23:宁一 non-member no-friend 替换member函数(prefer non-member non-friend functions to members functions)

    NOTE : 1.宁可拿non-member non-friend 函数替换member函数.这样做可以增加封装性/包裹单性(packaging flexibility)和机能扩展性.

  8. #define IOFFSETOF(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

    #include <iostream> #define IOFFSETOF(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) usi ...

  9. 【23】宁以non-member、non-friend替换member函数

    1.non-member方法与member方法没有本质区别,对于编译器来说,都是non-member方法,因为member方法绑定的对象,会被编译器转化为non-member方法的第一个形参.non- ...

随机推荐

  1. excel的C#操作教程

    C# Excel Tutorial http://csharp.net-informations.com/excel/csharp-excel-tutorial.htm How to transfer ...

  2. Unity 之 transform

    transform.Translate 1.function Translate (translation : Vector3, relativeTo : Space = Space.Self) : ...

  3. 实现分享功能插件2---jiathis分享插件应用

    博主原创:未经博主允许,不得转载 在上一篇的博文中分享了如何用百度分享插件实现分享功能,现在展示用jiathis进行实现分享功能: 主要代码如下: <body> <div class ...

  4. 网页图片提取助手(支持背景图、选择dom范围)

    网页图片提取助手(支持背景图.选择dom范围) 网页图片下载工具.网页图片批量保存. 使用场景: 作为web前端开发首——学习小生的你我,仿学在线页面是常有的事,但是一些在线资源,比如图片,图片有im ...

  5. UVa 12219 公共表达式消除

    https://vjudge.net/problem/UVA-12219 题意: 用表达式树来表示一个表达式. 思路: 用map来记录出现过的子树.如(b,3,6)表示这棵子树的根为b,左子树为编号为 ...

  6. Nginx 随笔

    使用包管理工具安装nginx Linux(基于deb) sudo apt-get install nginx Linux(基于rpm) sudo yum install nginx FreeBSD s ...

  7. 关于ArrayList.clear()与=null以及new ArrayList<E>()

    ArrayList是常用到的JCF类,用来保存类型相同的一组对象,并通过下标来快速访问指定对象.今天讨论的是当我们使用完ArrayList后应该选择怎样合适的处理方式. 这里现在有三种方式如下: 1. ...

  8. hdu 1325 Is It A Tree? 并查集

    Is It A Tree? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Tot ...

  9. C指针的一些知识

    原文:http://blog.csdn.net/soonfly/article/details/51131141 前言:复杂类型说明 要了解指针,多多少少会出现一些比较复杂的类型,所以我先介绍一下如何 ...

  10. Mutex, semaphore, spinlock

    Mutex是一把钥匙,一个人拿了就可进入一个房间,出来的时候把钥匙交给队列的第一个.一般的用法是用于串行化对critical section代码的访问,保证这段代码不会被并行的运行. Semaphor ...