太过亲密往往不好——用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- ...
随机推荐
- UVa 714 Copying Books - 二分答案
求使最大值最小,可以想到二分答案. 然后再根据题目意思乱搞一下,按要求输出斜杠(这道题觉得就这一个地方难). Code /** * UVa * Problem#12627 * Accepted * T ...
- Django框架(四) Django之视图层
视图函数 一个视图函数,简称视图,是一个简单的Python 函数,它接受Web请求并且返回Web响应.响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片. . ...
- POJ 1830 开关问题(高斯消元)题解
思路:乍一看好像和线性代数没什么关系.我们用一个数组B表示第i个位置的灯变了没有,然后假设我用u[i] = 1表示动开关i,mp[i][j] = 1表示动了i之后j也会跟着动,那么第i个开关的最终状态 ...
- BZOJ4415: [Shoi2013]发牌 树状数组+二分
Description 假设一开始,荷官拿出了一副新牌,这副牌有N张不同的牌,编号依次为1到N.由于是新牌,所以牌是按照顺序排好的,从牌库顶开始,依次为1, 2,……直到N,N号牌在牌库底.为了发完所 ...
- Nlog、elasticsearch、Kibana以及logstash在项目中的应用(一)
前言 最近在做文档管理中,需要记录每个管理员以及用户在使用过程中的所有操作记录,本来是通过EF直接将操作数据记录在数据库中,在查询的时候直接从数据库中读取,但是这样太蠢了,于是在网上找到了logsta ...
- LA 3266 田忌赛马
https://vjudge.net/problem/UVALive-3266 题意: 田忌赛马,赢一局得200两银子,输一局赔200两银子,平局不赔不赚,问最多能赚多少银子. 思路: 先排序,然后比 ...
- dos与unix系统的格式转化
unix 只用\n作为行结束符,而在 dos中是以\r和\n作为行结束符, 如果一个文件是在unix系统下创建,然后想在dos下使用,就要用unix2dos,如 unix2dos file 如果一个文 ...
- 02_DllZZ.def
ZC: 在VC6里面,只要有这个文件就可以了.但是到了 VS2010,需要手动的指定使用这个文件才行:VS2010-->项目-->属性--> 来到窗口"??? 属性页&qu ...
- Codeforces 832D - Misha, Grisha and Underground
832D - Misha, Grisha and Underground 思路:lca,求两个最短路的公共长度.公共长度公式为(d(a,b)+d(b,c)-d(a,c))/2. 代码: #includ ...
- nginx反向代理和正向代理,优缺点
http://blog.csdn.net/lishaojun0115/article/details/53200629 nginx反向代理代理的是服务器,正向代理代理的是客户端, 反向代理是客户点发送 ...