-
举个例子,当你有个class表示浏览器时,有众多功能需要提供,其中可能有清除下载元素的高速缓存区,清除访问过的URLs,清除系统中所有的cookies。
示例代码class WebBrowser { public: void ClearCache(){std::cout<<"clear cache"<<"n";} void ClearHistory(){std::cout<<"clear history"<<"n";} void ClearCookies(){std::cout<<"clear cookies"<<"n";} void ClearEverything() { ClearCache(); ClearHistory(); ClearCookies(); } }; inline void ClearWebBrowserEverything(WebBrowser& WebBrowser) { WebBrowser.ClearCache(); WebBrowser.ClearHistory(); WebBrowser.ClearCookies(); }问题来了,如果想要提供给用户清除所有的功能,究竟是使其成为member函数,还是non-member函数,两者似乎都可以实现功能。
-
面向对象守则要求:数据应该和操作数据的那些函数捆绑在一块,这意味着它建议member函数是较好的选择。不幸的是,这是对基于对象真实含义的一个误解。面向对象守则要求数据尽可能被封装,而与直观相反的是,non-member函数带来的封装性要比member函数高。此外,提供non-member函数可以提供更多的机能上的包裹弹性,而且可以降低编译相依度,增加对象的可延伸性。
-
让我们从封装开始讨论。如果某些东西被封装,它就不再可见。愈多东西被封装,愈少人可以看见它们。而愈少人看见,我们就有愈大的弹性去改变它们,因为我们改变仅仅影响到看到的那些人的事物。因此愈多东西被封装,我们改变事物的能力就愈大。这也是我们推崇封装的原因。
-
现在考虑对象内的数据,愈多函数可以访问它们,数据的封装性就越低。条款22曾说,成员变量应该是private,能访问private的成员函数就只有class的member函数和friend函数。所以如果member函数和non-member,non-friend函数可以提供相同的机能,那么能提供较大封装性的应该是后者。
-
注意non-member函数不意味着它不可以是另一个class的member函数,
-
non-member函数在C++中比较自然的做法是让class和non-member函数在一个namespace(命名空间):
namespace WebBrowserStuff { class WebBrowser { public: void ClearCache(){std::cout<<"clear cache"<<"n";} void ClearHistory(){std::cout<<"clear history"<<"n";} void ClearCookies(){std::cout<<"clear cookies"<<"n";} }; inline void ClearWebBrowserEverything(WebBrowser& WebBrowser) { WebBrowser.ClearCache(); WebBrowser.ClearHistory(); WebBrowser.ClearCookies(); } }当然不只是为了自然一些,要知道namespaces和classes不同,前者可以跨越多个源文件,而后者不能。这很重要,意味着可以在不同的源文件提供这些non-member便利函数。
注意这正是C++标准库的组织方式。标准程序库并不是拥有单一、整体、庞大的
,内含std命名空间的每一项东西,而是有数十个头文件( ,, ),每一个头文件声明一定的机能,用户只需引用所必要的头文件便可使用对应的功能。
- 宁可拿 non-member non-friend函数替换 member 函数。这样做可以增加封装性、包裹弹性(package flexibility)和机能扩展性。



