问题实例
</br>
假定现有一个class网页浏览器,其提供了一些清除cache,历史记录,或者cookies的成员函数:1
2
3
4
5
6
7
8class WebBrowser{
public:
...
void clearCache();
void clearHistory();
void removeCookies();
...
};
可能有用户会试图一起执行这些操作,因此WebBrowser也提供这样一个member函数:1
2
3
4
5
6class WebBrowser{
public:
...
void clearEverything();//调用三个成员函数
...
};
当然,这个功能也可以由一个non-member函数完成:1
2
3
4
5void clearBrowser(WebBrowser& wb){
wb.clearCache();
wb.clearHistory();
wb.removeCookies();
}
那么二者孰优孰劣?
封装性
</br>
根据面向对象原则,数据及操作数据的函数应当尽量被捆绑到一起,所以说member函数似乎比non-member函数更能体现封装性。但是事实并非如此,member函数的封装性较低。另外,non-member函数可允许对WebBrowser相关机能有较大的packaging flexibility,而这导致了较低的编译相依度,增加了WebBrowser的可延伸性。
封装的意义
如果某些东西被封装,这意味着它不再对外可见,因此我们也拥有了较大的弹性去改变它。因此,愈多东西被封装,我们能够改变它们的能力也就越大。封装让我们改变事物但只影响有限客户。
封装性的度量
我们可以粗略地认为,有越多的函数可以访问某数据,该数据的封装性也就越低。
上节说过,成员变量应该是private,否则这意味着有无限的函数可以访问它。那么显然的,如果一个non-member和一个member实现相同的功能,那我们应该优先使用non-member函数,因为它可以访问的数据很相对较少,也就是拥有更好的封装性。
non-member non_friend函数
</br>
以上论述需要注意两点:
- 我们讨论的其实是non-member non-friend函数,因为friend函数与member函数具备相同的封装冲击性。所以从封装的角度而言,这里并非是在member函数与non-member函数之间选择,而是在member函数与non-member non-friend之间选择。
- 因为在意封装性从而让函数成为class的non-member,并不意味着它不可以是另一个class的member。
比如令令成为某个工具类的static member函数,只要它不是WebBrowser的一部分(或成为其friend),就不会影响WebBrowser的private成员封装性。
实现策略
在C++中,比较自然的方法是令clearBrowser成为一个non-member函数并与WebBrowser处在同一个namespace内:1
2
3
4namespace WebBrowserStuff{
class WebBrowser {...};
void clearBrowser(WebBrowser& wb);
}
namespace的一大优点在于它可以跨越多个源码文件。我们为了实现编译分离,可以在不同的头文件内声明各类函数,然后把他们放在同一个命名空间内。如果我们需要更多的non-member函数,那么我们只需要往namespace里面添加就好了。这种方式允许客户只对他们使用的那一小部分形成编译相依。