博客
关于我
条款23:宁以non-member、non-friend替换member函数
阅读量:322 次
发布时间:2019-03-04

本文共 2230 字,大约阅读时间需要 7 分钟。

1、问题引出

(1)假设有一个WebBrower类,代表浏览器,其中有的成员方法有:
  • 清除缓存
  • 清除url
  • 清除cookies
class WebBrower{   public:    void ClearCach();    void ClearHistory();    void RemoveCookies();};
(2)现在有一个需求:把这三个同时同时清除。但是我们实现上述需求有两种方式:
方式一:在WebBrower类中再写一个成员方法,直接在其内部调用其他成员方法。
class WebBrower{   void ClearEverything()    {           ClearCach();        ClearHistory();        RemoveCookies();    }};
方式二:写一个不属于这个类的函数,在函数内调用这个类的三个成员方法。
void ClearWebBrowser(WebBrower& w){       w.ClearCach();    w.ClearHistory();    w.RemoveCookies();}

现在的问题是,哪一种实现方式更好?也就是把它写成成员函数好,还是写成 non-member 、non-friend 函数好呢?

答案是 写成:non-member、non-friend 好。

2、为什么写成non-member、non-friend 好?

首先,对于面向对象的一个误解:数据应该和操作数据的函数绑定在一起。如果按照这种解释,那么应该写成member的。但是实际上,面向对象强调的是封装性。

  • 面向对象真正强调的是封装性,对于上述问题,non-member、non-friend函数的封装性要比member函数好。
  • non-member函数允许对浏览器类有较大的包裹弹性,较大的包裹弹性,将导致较低的编译相依度,增加浏览器类的可延展性。

3、关于上述两个原因的进一步解释一:封装性

(1)为什么强调封装性?

所谓封装就是不可见,越多东西被封装,能够看见它的人越少。越少的人看到它,我们就能够更大弹性的修改它。因此,封装性越好,我们改变实现的能力就越高。推崇封装的原因:我们能够改变事物,而只影响有限的客户。

(2)如何衡量封装性?

我们计算能够访问该数据的成员函数以及其它函数的数量,作为一种粗糙的衡量。越多的函数能够访问它,它的封装性就越低。
例如:public数据,所有的函数都可以访问它,它就是毫无封装性的。private数据,只有friend和member函数可以访问它,它的封装性的高低,就和能够访问它的friend函数和member函数数量有关,数量越大,代表封装性越低,数量越小,代表封装性越高。

总之,在实现同一机能的情况下,面对使用member函数和non-member、non-friend函数的抉择时,后者提供更好的封装性。

这就从封装性的角度解释了上述例子。

(3)注意

关于上述论断,有两个需要注意的点。
第一: non-member、friend函数 和member函数时一样的,都可以访问私有数据。因此只是non-member、non-friend 的函数和member函数之间存在封装性程度的高低不一。

第二:可以将函数写成另一个类类的member函数。例如,可以使得ClearWebBrowser() 函数称为另外一个工具类的static member函数。non-member指的是不能将其写成浏览器类WebBrower 的成员函数。(这对于其它语言的程序员来说一个温暖的慰藉,因为有些语言只能将函数写到class内部。)

4、关于上述两个原因的进一步解释二:编译相依度、包裹弹性

虽然可以将其写到其它类中,但是C++比较自然的做法是,将它写成一个non-member函数,并让它和浏览器类在同一个命名空间中。

这样的做法是由原因的:

(1)原因一:可以降低文件间的编译相依度。

namespace 和class 是不同的,namespace是可以跨越多个文件的,但是class却不能。class 内的是核心技能,但是便利函数只是提供便利的,可有可无的。即使没有便利函数,用户可以通过访问class进行相关的操作。因此说便利函数时外覆的。

一个类可以由不同的机能分化出拥有多个便利函数,与cookies管理有关的、与书签有关的、与打印有关的。用户可能只对其中一部分感兴趣,那就没有必须让他们之间存在编译相依的关系。可以将他们进行分离,与不同模块相关的便利函数写到不同的头文件中,这样用户对哪个模块感兴趣,就包含哪个头文件就可以了。

(2)原因二:可以轻松的扩展像这样的便利函数。(体现包裹弹性)

将所有便利函数放在多个头文件中但隶属于同一个命名空间,意味着客户可以轻松扩展这一组便利函数。他们需要增加什么便利函数时,也添加到这个命名空间就好。class就不能这样扩展。

注意: 原因一种组织代码的 方式也正是C++标准程序库的组织方式。C++并没有将所有的功能都写到一个头文件里,而是写成数十个头文件,每个头文件中包含某些机能。用户需要什么,就包含什么。这样形成一个编译相依的小系统。 这种分割机能的方式不适用于class成员函数,因为class必须整体定义,不能被分割成片段。

转载地址:http://qfgq.baihongyu.com/

你可能感兴趣的文章
AT 杂题泛做
查看>>
StringBuilder拼接字符串,“,”在前还是在后问题
查看>>
给asterisk1.8.7添加menuselct选项
查看>>
ASP.NET Core分布式项目实战(oauth2 + oidc 实现 server部分)--学习笔记
查看>>
ASP.NET Core分布式项目实战(oauth2 + oidc 实现 client部分)--学习笔记
查看>>
组合模式
查看>>
PyQt5之音乐播放器
查看>>
css居中方法与双飞翼布局
查看>>
Redis进阶实践之十八 使用管道模式提高Redis查询的速度
查看>>
多指灵巧手MoveIt!与Gazebo联合仿真框架搭建
查看>>
SQL注入
查看>>
XCTF-upload1
查看>>
LeetCode 题解 | 1. 两数之和
查看>>
#2036:改革春风吹满地
查看>>
MPI Maelstrom POJ - 1502 ⭐⭐ 【Dijkstra裸题】
查看>>
P1379 八数码难题 ( A* 算法 与 IDA_star 算法)
查看>>
按需取余
查看>>
算法学习笔记: 珂朵莉树
查看>>
算法学习笔记:母函数详解
查看>>
Codeforces Round #664 题解(A ~ C)
查看>>