您的位置:首页 > 编程语言 > C语言/C++

[翻译] Effective C++, 3rd Edition, Item 23: 用 non-member non-friend functions(非成员非友元函数)取代 member functions(成员函数)

2005-08-03 00:55 555 查看
[b]Item 23: 用 non-member non-friend functions(非成员非友元函数)取代 member functions(成员函数)[/b]

作者:Scott Meyers

译者:fatalerror99 (iTePub's Nirvana)

发布:http://blog.csdn.net/fatalerror99/

想象一个代表 web browsers(网页浏览器)的 class。在这样一个 class 可能提供的大量函数中包括清空已下载部分的缓存,清空已访问 URLs 的历史,以及从系统移除所有 cookies 的功能:

class WebBrowser {
public:
...
void clearCache();
void clearHistory();
void removeCookies();
...
};

很多用户希望能一起执行全部这些动作,所以 WebBrowser 可能也会提供一个函数去这样做:

class WebBrowser {
public:
...
void clearEverything(); // calls clearCache, clearHistory,
// and removeCookies
...
};

当然,这个功能也能通过用一个 non-member function(非成员函数)调用适当的 member functions(成员函数)来提供:

void clearBrowser(WebBrowser& wb)
{
wb.clearCache();
wb.clearHistory();
wb.removeCookies();
}

那么哪个更好呢,member function(成员函数)clearEverything 还是 non-member function(非成员函数)clearBrowser?

object-oriented(面向对象)原则指出:数据和对它们进行操作的函数应该被绑定到一起,而且建议 member function(成员函数)是更好的选择。不幸的是,这个建议是不正确的。它产生于对 object-oriented(面向对象)是什么的一个误解。object-oriented(面向对象)原则指出数据应该尽可能 encapsulated(被封装)。与直觉不同,member function(成员函数)clearEverything 居然会造成比 non-member function(非成员函数) clearBrowser 更弱的 encapsulation(封装)。此外,non-member function(非成员函数)为 WebBrowser 相关功能提供了更大的包装机动性,而且,可以获得更少的 compilation dependencies(编译依赖)和 WebBrowser 可扩展性的增强。因而,因此在很多方面 non-member(非成员)的方法比一个 member function(成员函数)更好。理解它的原因是非常重要的。

我们将从 encapsulation(封装)开始。如果某物被封装,它被从视线中隐藏。某物被封装得越强,就越少有东西能看见它。越少有东西能看见它,我们改变它的机动性就越大,因为我们的改变仅仅直接影响那些能看见我们变了什么的东西。那么,某物被封装得越强,我们改变它的能力就越强。这就我们首先重视 encapsulation(封装)的原因:它为我们提供一种用仅仅影响有限的客户的方法改变事情的机动性。

考虑组成一个 object 的数据。越少有代码能看到数据(也就是说,访问它),数据被封装得就越强,我们改变 object 的数据的特性的自由也就越大,比如,data members(数据成员)的数量,它们的类型,等等。作为度量多少代码能看到一片数据的粗略的尺度,我们可以计数能访问那片数据的函数的数量:越多函数能访问它,数据的被封装得越弱。

Item 22 说明了 data members(数据成员)应该是 private(私有)的,因为如果它们不是,就有无限量的函数能访问它们。它们根本就没有 encapsulation(封装)。对于 private(私有)的 data members(数据成员),能访问他们的函数的数量就是 class 的 member functions(成员函数)的数量加上 friend functions(友元函数)的数量,因为只有 members(成员)和 friends(友元)才能访问 private members(私有成员)。假设在一个 member function(成员函数)(能访问的不只是一个 class 的 private data(私有数据),还有 private functions(私有函数),enums(枚举),typedefs,等等)和一个提供同样功能的 non-member non-friend function(非成员非友元函数)(不能访问上述那些东西)之间有一个选择,能导致更强 encapsulation(封装)的选择是 non-member non-friend function(非成员非友元函数),因为它不会增加能访问 class 的 private part(私有构件)的函数的数量。这就解释了为什么 clearBrowser(non-member non-friend function(非成员非友元函数))比 clearEverything(member function(成员函数))更可取:它能导致 WebBrowser class 内更强的封装。

在这一点上,有两件事值得注意。首先,这个论证只适用于 non-member non-friend functions(非成员非友元函数)。friends(友元)能像 member functions(成员函数)一样访问一个 class 的 private members(私有成员),因此同样影响 encapsulation(封装)。从 encapsulation(封装)的观点看,选择不是在 member(成员)和 non-member functions(非成员函数)之间,而是在 member functions(成员函数)和non-member non-friend functions(非成员非友元函数)之间。(当然,encapsulation(封装)并不是仅有的观点,Item 24 说明如果观点来自 implicit type conversions(隐式类型转换),选择就是在 member(成员)和 non-member functions(非成员函数)之间。)

第二件事需要注意正因为关系到 encapsulation(封装),它指出,一个函数是一个 class 的 non-member(非成员)并不意味着它不可以是另一个 class 的 member(成员)。这对于习惯了所有函数必须在 classes 中的语言(例如,Eiffel,Java,C#,等等)的程序员是一个适度的安慰。例如,我们可以使 clearBrowser 成为某些 utility class 的 static member function(静态成员函数)。只要它不是 WebBrowser 的一部分(或一个 friend(友元)),它就不会影响 WebBrowser 的 private members(私有成员)的 encapsulation(封装)。

在 C++ 中,一个更自然的方法是使 clearBrowser 成为与 WebBrowser 在同一个 namespace(名字空间)中的 non-member function(非成员函数):

namespace WebBrowserStuff {

class WebBrowser { ... };

void clearBrowser(WebBrowser& wb);

...
}

然而,相对于形式上的自然,这样更适用于它,因为 namespaces名字空间,与 classes 不同,能展开到多个源文件中。这是很重要的,因为类似 clearBrowser 的函数是 convenience functions(便利函数)。既不是 members(成员)也不是 friends(友元),它们没有对 WebBrowser 进行专门的访问,所以他们不能提供任何一种 WebBrowser 的客户不能通过其它方法获得的功能。例如,如果 clearBrowser 不存在,客户可以直接调用 clearCache,clearHistory 和 removeCookies 本身。

一个类似 WebBrowser 的 class 可以有大量的 convenience functions(便利函数),一些是书签相关的,另一些打印相关的,还有一些是 cookie 管理相关的,等等。作为一个一般的规则,多数客户仅对这些 convenience functions(便利函数)集中的一部分感兴趣。没有理由让一个只对书签相关的 convenience functions(便利函数)感兴趣的客户在编译时依赖其它函数,例如,cookie 相关的 convenience functions(便利函数)。分隔它们的直截了当的方法就是在一个头文件中声明书签相关的 convenience functions(便利函数),在另一个不同的头文件中声明 cookie 相关的 convenience functions(便利函数),在第三个头文件声明打印相关的 convenience functions(便利函数),等等:

// header "webbrowser.h" — header for class WebBrowser itself
// as well as "core" WebBrowser-related functionality
namespace WebBrowserStuff {

class WebBrowser { ... };

... // "core" related functionality, e.g.
// non-member functions almost
// all clients need
}
// header "webbrowserbookmarks.h"
namespace WebBrowserStuff {
... // bookmark-related convenience
} // functions
// header "webbrowsercookies.h"
namespace WebBrowserStuff {
... // cookie-related convenience
} // functions

...

注意这就是标准 C++ 库的严密的组织方式。存在有许多头文件中(例如,<vector>,<algorithm>,<memory>,等等),每一个都声明了 std 中的一些机能,这胜于在一个单独的一体式的 <C++StandardLibrary> 头文件包含 std namespace 中的所有东西。只需要 vector 相关机能的客户不需要 #include <memory>,不用 list 的客户没有必要 #include <list>。这就允许客户在编译时只依赖于他们实际使用的系统构件。(参见 Item 31 对减少 compilation dependencies(编译依赖)的其它方法的讨论。)当机能来自一个 class 的 member functions(成员函数)时,用这种方法分割它是不可能的,因为一个 class 必须作为一个整体来定义,它不能四分五裂。

将所有 convenience functions(便利函数)放入多个头文件中——但是在一个 namespace(名字空间)中——也意味着客户能容易地扩充 convenience functions(便利函数)集。他们全部要做的就是在这个 namespace(名字空间)中加入更多的 non-member non-friend functions(非成员非友元函数)。例如,如果一个 WebBrowser 的客户决定写一个与下载图像相关的 convenience functions(便利函数),他或她仅仅需要新建一个包含那些函数在 WebBrowserStuff namespace(名字空间)中的 declarations(声明)的头文件。这个新的函数现在就像其它 convenience functions(便利函数)一样可用并被集成。这是 classes 不能提供的又一个特性,因为 class definitions(类定义)是关闭了客户扩充的。当然,客户可以派生新 classes,但是 derived classes(派生类)不能访问 base class(基类)中被封装的(也就是 private(私有)的)members(成员),所以这样的 "extended functionality"(“扩充机能”)只有二等身份。此外,就像 Item 7 中解释的,不是所有的 classes 都是作为 base classes(基类)设计的。

Things to Remember

用 non-member non-friend functions(非成员非友元函数)取代 member functions(成员函数)。这样做可以提高 encapsulation(封装性),packaging flexibility(包装机动性)和 functional extensibility(机能扩展性)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐