条款22:将成员变量声明为private。
条款23:以non-member、non-friend替换member函数
考虑下例:
class WebBrowser{
public:
...
void clearCache();
void clearHistory();
void removeCookies();
};
如果想定义某个操作调用上述三个函数,可以另外定义一个成员函数:
public:
...
void clearEverything();//依次调用上述三个函数;
另一个做法是使用一个非成员函数:
void clearBrowser(WebBrowser& wb)
{
wb.clearCache();
wb.clearHistory();
wb.removeCookies();
}
实际上,封装为非成员函数的做法更好。首先讨论封装:如果某些东西被封装,则不再可见。越多封装,则有越大的弹性去改变它。这也是推崇封装的原因:它使我们改变事物只影响有限客户。
条款22提到,成员变量应为private,能够访问private成员变量的函数只有member函数、friend函数。所以non-member、non-friend有更大的封装性,因为它并不增加“能够访问class内private成分”的函数数量。
对于这种做法,一个需要注意的点是,class的non-member函数仍可是另一个class的member函数。例如,我们可以令clearBrowser成为某工具类的一个static member函数(只要不是WebBrowser的一部分或者friend,就不会影响其封装性)。
在c++中,比较自然的做法是将WebBrowser类和clearBrowser放在同一个命名空间内:
namespace WebBrowserStuff{
class WebBrowser{...};
void clearBrowser(WebBrowser& wb);
...
}
namespace的用处: namespace与class不同,前者可以跨越多个源文件而后者不能。这很重要,因为像clearBrowser这样的函数,是non-member、non-friend的,如果一个客户没有这个函数,则他只能依次调用那三个函数。
对于WebBrowser这样的class,可能有很多clearBrowser这样的便利函数,以应对不同客户的需求(因为客户感兴趣的操作可能不同)。
分离他们的直接做法是将不同的相关操作声明在不同的头文件中(同属于一个namespace)
//webbrowser.h
namespace WebBrowserStuff{
class WebBrowser{...};
...//核心机能,几乎所有客户都需要
}
//1.h
namespace WebBrowserStuff{
...//操作1相关函数
}
//2.h
namespace WebBrowserStuff{
...//操作2相关函数
}
这也真是c++标准库的组织方式。例如在namespace std中,有很多相关操作:vector,list,memory等等。当我们需要某些操作,只需要包含对应的头文件即可。而class必须定义整体,不能被分割。
另外,将所有函数放在多个头文件但隶属于同一个命名空间,使得客户可以轻松扩展这一组便利函数–直接添加non-member、non-friend函数到此命名空间即可。而class对客户而言是不能扩展的,虽然客户可以派生出新的class,但derived class无法访问base class中private成员。