太过亲密往往不好——用non-member,non-friend替换member函数
在前一篇文章,我们提到,使用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中。当我们需要使用到其中的某个容器的时候,就包含相关的头文件。
总结一下
- 使用non-member,non-friend函数提高class的封装性
- 使用non-member,non-friend时可以借助namespace来提高程序的可拓展性。
太过亲密往往不好——用non-member,non-friend替换member函数的更多相关文章
- 理解#define offsetof(struct_t,member) ((int)&((struct_t *)0)->member)
#define offsetof(struct_t,member) ((int)&((struct_t *)0)->member) 这个东西很多人应该知道: offsetof是用来判断结 ...
- Effective C++ -----条款23:宁以non-member、non-friend替换member函数
宁可拿non-member non-friend函数替换member函数.这样做可以增加封装性.包裹弹性(packaging flexibility)和机能扩充性.
- #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)宏的运行机理:1. ( (TYPE *)0 ) 将零转型为TY ...
- [Effective C++ --023]宁以non-member、non-friend替换member函数
作者在这一节中花了大幅度的篇幅来介绍为什么最好使用non-member.non-friend函数. 思路如下: 场景:如果有一个class用来表示网页浏览器,那么清楚缓存及历史记录的时候,我们可能定义 ...
- Effective C++:条款23:宁以non-member、non-friend替换member函数
(一) 有个class来表示网页浏览器: class WebBrowser { public: void clearChache(); void clearHistory(); void remove ...
- 条款23:宁以non-member, non-friend,替换member函数。
考虑下面这种经常出现的使用方式: class webBroswer{ public: ... void clearCache(); void clearHistory(); void removeCo ...
- 条款23:宁一 non-member no-friend 替换member函数(prefer non-member non-friend functions to members functions)
NOTE : 1.宁可拿non-member non-friend 函数替换member函数.这样做可以增加封装性/包裹单性(packaging flexibility)和机能扩展性.
- #define IOFFSETOF(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#include <iostream> #define IOFFSETOF(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) usi ...
- 【23】宁以non-member、non-friend替换member函数
1.non-member方法与member方法没有本质区别,对于编译器来说,都是non-member方法,因为member方法绑定的对象,会被编译器转化为non-member方法的第一个形参.non- ...
随机推荐
- CP2102
1概述 CP2102其集成度高,内置USB2.0全速功能控制器.USB收发器.晶体振荡器.EEPROM及异步串行数据总线(UART),支持调制解调器全功能信号,无需任何外部的USB器件.CP2102与 ...
- HttpClient4.5简单使用
一.HttpClient简介 HttpClient是一个客户端的HTTP通信实现库,它不是一个浏览器.关于HTTP协议,可以搜索相关的资料.它设计的目的是发送与接收HTTP报文.它不会执行嵌入在页面中 ...
- 【集群搭建】Zookeeper集群环境配置
1.下载解压安装文件 2.配置文件:conf/zoo.cfg tickTime=2000 dataDir=/usr/sunny/logs/zookeeper/data dataLogDir=/usr/ ...
- 转:java项目经验面试总结
转载: 在前期已经讲过怎样分析自己,对自己进行一个准确的定位,选择一个合适的求职方向!并结合自己的实际个人情况来写一份针对性很强的个人简历!个人简历就是个人的广告!好的简历可以更受到用人需求单位青睐! ...
- HDU 5877 Weak Pair(树状数组+dfs+离散化)
http://acm.hdu.edu.cn/showproblem.php?pid=5877 题意: 给出一棵树,每个顶点都有权值,现在要你找出满足要求的点对(u,v)数,u是v的祖先并且a[u]*a ...
- POJ 1018 Communication System(DP)
http://poj.org/problem?id=1018 题意: 某公司要建立一套通信系统,该通信系统需要n种设备,而每种设备分别可以有m1.m2.m3.....mn个厂家提供生产,而每个厂家生产 ...
- 关于xargs cp中,如何确定拷贝的源和目的 (copied)
Seker: find . -name "*" |xargs cp ???? 这里 xargs cp 怎么区分cp源 和 cp目的 例如:想把 查询到的文件 都copy到/home ...
- 2:JavaScript中的基本运算
今天说的是JavaScript中的数据基本运算 在上一节中已经说了关于JavaScript中的基本数据类型 那么数据有了 剩下来就是数据之间的运算 表达式-------预算符(赋值 比较 算数 逻辑 ...
- 【NOI2014】动物园
题目链接:http://uoj.ac/problem/5 求:$${\prod _{i=1}^{L}num[i]\%(1e9+7)}$$,${num\left [ i \right ]}$表示:由字符 ...
- UTC和GMT时间辨析
一.UTC和GMT 每个地区都有自己的本地时间,在网上以及无线电通信中时间转换的问题就显得格外突出. 整个地球分为二十四时区,每个时区都有自己的本地时间.在国际无线电通信场合,为了统一起见,使用一个统 ...