太过亲密往往不好——用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- ...
随机推荐
- bzoj 4033 树上染色 - 树形动态规划
有一棵点数为N的树,树边有边权.给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑 色,并将其他的N-K个点染成白色.将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的 ...
- python程序转为exe文件
python开发者向普通windows用户分享程序,要给程序加图形化的界面(传送门:这可能是最好玩的python GUI入门实例! http://www.jianshu.com/p/8abcf73ad ...
- Python3基础 os mkdir 创建一层文件夹 在有父目录的情况下创建子目录
Python : 3.7.0 OS : Ubuntu 18.04.1 LTS IDE : PyCharm 2018.2.4 Conda ...
- PHP中namespace和use使用详解
来源于:http://www.jb51.net/article/36389.htm 命名空间一个最明确的目的就是解决重名问题,PHP中不允许两个函数或者类出现相同的名字,否则会产生一个致命的错误.这种 ...
- P4879 ycz的妹子
思路 让你干啥你就干啥呗 查询第x个妹子就get一下再修改 这里稳一点就维护了三个东西,也许两个也可以 代码 #include <iostream> #include <cstdio ...
- P3870 [TJOI2009]开关
思路 重题 代码 #include <iostream> #include <vector> #include <cstdio> #include <cstr ...
- 【Maven】2.使用Nexus3搭建Maven私服+上传第三方jar包到本地maven仓库
参考文章: http://www.cnblogs.com/luotaoyeah/p/3791966.html --------------------------------------------- ...
- Java-master(github)教材整理
helloworld class HelloWorld { public static void main(String[] args) { System.out.println("hell ...
- Nlog、elasticsearch、Kibana以及logstash在项目中的应用(一)
前言 最近在做文档管理中,需要记录每个管理员以及用户在使用过程中的所有操作记录,本来是通过EF直接将操作数据记录在数据库中,在查询的时候直接从数据库中读取,但是这样太蠢了,于是在网上找到了logsta ...
- FOJ-1058-粗心的物理学家
题目:粗心的物理学家 代码: #include<stdlib.h> #include<iostream> #include<cstdio> using namesp ...