太过亲密往往不好——用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- ...
随机推荐
- [c/c++]指针(3)
在指针2中提到了怎么用指针申配内存,但是,指针申配的内存不会无缘无故地 被收回.很多poj上的题都是有多组数据,每次地数组大小会不同,所以要重新申请 一块内存.但是原来的内存却不会被收回,也是说2.3 ...
- fedora中使用 mariadb数据库建库和建表-- mariadb数据库服务无法启动?
/proc(进程, 过程等含义) 文件系统是一个虚拟文件系统,通过它可以使用一种新的方法在 Linux® 内核空间(内核)和用户空间(用户)之间进行通信.在 /proc 文件系统中,我们可以将对虚拟文 ...
- C#学习笔记(二):变量、数据类型和运算符
变量.数据类型 给变量赋值,如果想指定float,在数字后面加上F/f:如果想指定decimal,在数字后面加上m. 0-9:48-57 A-Z:65-90 a-z:97-122 ...
- 【eclipse】阿里巴巴代码检测插件在线安装
https://p3c.alibaba.com/plugin/eclipse/update
- LA 3135 阿格斯(优先队列)
https://vjudge.net/problem/UVALive-3135 题意: 你的任务是编写一个称为Argus的系统.该系统支持一个Register的命令 Register Q_num Pe ...
- 【Django】【五】开发Web接口
[HTTP协议与JSON] 1. HTTP协议 特点如下: (1)支持客户/服务器模式. 简单快速:客户向服务器请求服务时,只需传送请求方法和路径.请求方法常用的有GET.POST.每种方法规定了客户 ...
- Django本地开发,debug模式引用静态文件
debug为true ,不用设置static_root debug 为false ,设置static_root STATIC_ROOT = ( os.path.join(BASE_DIR, 'stat ...
- Could NOT find SDL (missing: SDL_LIBRARY SDL_INCLUDE_DIR)
sudo apt-get install libsdl-dev或 sudo apt-get install libsdl1.2-dev
- html css 伪样式
伪类的分类及作用: 引自W3School教程伪元素的分类及作用: 这里就不进行多的描述,原文地址:http://www.it165.net/design/html/201406/2643.html
- php 获取自己的公网IP
<?php $externalContent = file_get_contents('http://checkip.dyndns.com/'); preg_match('/Current IP ...