理由一: 语法一致性。对客户而言每样东西都是函数。
理由二: 可通过成员函数严格控制其读写权限、添加约束条件。
根本理由: 封装性。
声明为private则客户只能通过函数接口访问,客户不用关心实现方法,这为“所有可能实现”提供了弹性。并且以接口替代直接对变量的访问,从代码破坏量来说,前者将远远小于后者。客户很可能只需重新编译,甚至可以消除重新编译(条款31)。
然后记得,protected并不比public更具封装性。public变量将影响所有客户码,而protected变量将影响所有继承类。
尽管成员函数可以提高封装性,这里却有一个反直观的结论。有个浏览器类,有各种清理缓存的方法:
class WebBrowser
{
public:
void clearCache();
void clearHistory();
void clearBookmarks();
void clearCookies();
};
为了客户方便清理所有缓存,提供一个便利函数,调用所有清理方法:
void clearBrowser(WebBrowser& wb)
{
wb.clearCache();
wb.clearHistory();
wb.clearBookmarks();
wb.clearCookies();
}
那么此便利函数是该定义为成员,还是非成员函数呢?
答案是以非成员、非友元函数替代成员函数。
对于一个成员变量,我们以”能够访问其的函数数量“作为丈量其封装性的依据。对于一个成员变量,我们以”能够访问其的函数数量“作为丈量其封装性的依据。即能够访问的函数越多,封装性越差。而友元和成员函数具有相同权限,对封装型造成的冲击是一样的,因此也不予考虑。
对于一个成员变量,我们以”能够访问其的函数数量“作为丈量其封装性的依据。即能够访问的函数越多,封装性越差。而友元和成员函数具有相同权限,对封装型造成的冲击是一样的,因此也不予考虑。
对于非成员函数clearBrowser,比较自然做法是将其和WebBrowser置于同一namespace内:
namespace WebBrowserStuff{
class WebBrowser
{
public:
void clearCache();
void clearHistory();
void clearBookmarks();
void clearCookies();
};
void clearBrowser(WebBrowser& wb);
}
便利函数只是方便客户实现特定机能,像WebBrowser这个类可能具有大量的便利函数。由于命名空间可以跨越多个文件但单个类做不到,因此可以将不同的便利函数声明于不同的头文件中。用户想使用特定机能只需包含某个头文件而已,这允许客户只对他们所用的那部分系统形成编译相依,而这正是STL库的组织方式(以分散头文件定义std namespace):
//webbrowser.h
namespace WebBrowserStuff{
class WebBrowser
{
//核心机能
};
void clearBrowser(WebBrowser& wb); //公共便利函数
}
//webbrowserbookmarks.h
namespace WebBrowserStuff{
class WebBrowser;
void clear_Bookmarks(WebBrowser& wb);
}
//webbrowsercookies.h
namespace WebBrowserStuff{
class WebBrowser;
void clear_Cookies(WebBrowser& wb);
}
注意,定义命名空间内的函数时也要放在namespace内(须包含webbrowser.h,因为调用webbrowser函数需要看到完整定义式):
#include "webbrowser.h"
#include "webbrowserbookmarks.h"
using namespace std;
namespace WebBrowserStuff{
void clear_Bookmarks(WebBrowser& wb)
{
wb.clearBookmarks();
}
}
用户想要扩充便利函数也非常容易,只需另加头文件,并将函数声明置于其中,包裹在WebBrowserStuff命名空间即可。当然核心类WebBrowser对客户是不可扩展的。
综上,以非成员非友元函数替换成员函数能够增加封装性、包裹弹性和机能扩充性。