Effictive C++
条款24
- 宁以non-member,no-friend,替换member函数
想象有个class用来表示网页浏览器,这样的class可能提供的众多函数中,一些用来清除下载元素的高速缓冲区,历史记录以及cookies
class WebBrowser
{
...
void clearCache();
void clearHistory();
void removeCookies();
...
}
若采用成员函数方案,我们可能会考虑提供这样一个函数:
class WebBrowser
{
...
void clearEverything();//调用clearCache, clearHistory, removeCookies
}
同时也可以由非成员函数提供
void clearBrowser(WebBrowser& wb)
{
wb.clearCache();
wb.clearHistory();
wb.removeCookies();
}
- 孰优孰劣?
从面对对象守则要求来看,数据以及操作数据的那些函数应该被捆绑在一块,这意味它建议的member函数是较好的选择。不幸的是这是一个误解。面对对象守则要求数据应该尽可能的被封装,然而与直观相反的,member函数带来的封装性比 no-member函数低。此外提供member函数可允许对WebBrowser相关技能有较大的包裹弹性,导致较低的编译相依度,曽家WebBrowser的可延伸性,因此在许多方面no-member比member函数做法更好。why?
从封装谈起,如果某些东西被封装,它就不再可见,愈多东西被封装,愈少人可以看到他,而愈少的人看到意味着我们有更大的弹性去变化它,因为我们的变化仅仅影响看到改变的那些人事物。因此越多东西被封装,我们改变那些东西的能力就越大,这就是首先我们推崇封装:改变事物而只影响有限客户。
现在考虑对象内的数据:越少的代码可以看到数据,越多的数据可被封装。现在我们也就能越自由的改变对象数据,例如改变成员变量的数量,类型等。那么 如何测量“多少代码可以看到某一块数据呢”?做一种粗糙的测量:愈多函数可访问封装性就越低。
接下来就是抉择:member函数:不知可以访问class内的private数据,也可以取用private函数,enums,typedefs等等。 no-member函数:无法访问上述任何。且两者提供相同性能,那么导致较大封装性的是non-member,non-friend函数。因为它并不增加“能访问class内private成分”的函数数量。这就解释了为什么一个clearBrowser 会比 clearEverything更受欢迎的原因:clearBrowser具有较大的封装性。
第二件值得注意的事情。只因在意封装性而让函数“成为class的non-member”并不意味着它“不可以是另一个函数的member”,例如我们可以令clearBrowser成为某工具类的一个static member函数。
在C++中比较自然的方法:让clearBrowser成为一个non-member函数并位于WebBrowswer同一个namespace中:
namespace WebBrowserStuff{
class WebBrowser{...};
void clearBrowser(WebBrowser& wb);
}
这不仅是看起来自然而已,若我们需要添加更多non-member函数到此命名空间内,我们只需在WebBrowserStuff命名空间内建立一个头文件内含那些函数声明即可,这允许客户只对他们所用的那一小部分系统形成编译相依。这是class所无法提供的另一个特质。
请记住
- 宁可拿non-member,non-friend函数代替member函数,这样做可以增加封装性,包裹弹性以及机能扩充性。