大学IT网 - 最懂大学生的IT学习网站! QQ资料交流群:367606806
当前位置:大学IT网 > C++技巧 > C++技巧:用非成员非友元函数取代成员函数

C++技巧:用非成员非友元函数取代成员函数

关键词:C++非友元函数成员函数  阅读(486) 赞(12)

[摘要]本文是对非成员非友元函数取代成员函数的讲解,希望对你学习C++编程技术有所帮助!

  想象一个意味 web 阅读器的类。在大批的函数中,这样一个类也许会提供清空已下载成分的缓存。清空已拜访 URLs 的历史,以及从零碎移除一切 cookies 的功用:

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

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

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

  当然,这个功用也能经过非成员函数调用适当的成员函数来提供:

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

  那么哪个更好呢,成员函数 clearEverything 还是非成员函数 clearBrowser?

  面性对象准绳指出:数据和对它们中止操作的函数应该被绑定到一同,而且建议成员函数是更好的选择。不幸的是,这个建议是不正确的。它发作于对面向对象是什么的一个曲解。面向对象准绳指出数据应该尽可以被封装。与直觉不同,成员函数 clearEverything 竟然会构成比非成员函数 clearBrowser 更差的封装性。此外,提供非成员函数允许 WebBrowser 相关功用的更大的包装弹性,而且,可以取得更少的编译依赖和 WebBrowser 扩展性的增进。因此,在很多方面非成员办法比一个成员函数更好。了解它的缘由是十分重要的。

  我们将从封装末尾。假定某物被封装,它被从视野中隐藏。越多的东西被封装,就越少有东西能看见它。越少有东西能看见它,我们改动它的弹性就越大,由于我们的改动仅仅直接影响那些能看见我们变了什么的东西。某物的封装性越强,那么我们改动它的才干就越强。这就是将封装的价值评价为第一的缘由:它为我们提供一种改动事情的弹性,而仅仅影响无限的客户。

  结合一个对象思索数据。越少有代码能看到数据(也就是说,拜访它),数据封装性就越强,我们改动对象的数据的特性的自在也就越大,比方,数据成员的数量,它们的类型,等等。作为多少代码能看到一块数据的粗糙的尺度,我们可以计数能拜访那块数据的函数的数量:越多函数能拜访它,数据的封装性就越弱。

  数据成员应该是 private 的,由于假定它们不是,就有有限量的函数能拜访它们。它们基本就没有封装。关于 private 数据成员,能拜访他们的函数的数量就是类的成员函数的数量加上友元函数的数量,由于只需成员和友元能拜访 private 成员。假定在一个成员函数(能拜访的不只是一个类的 private 数据,还有 private 函数,枚举,typedefs,等等)和一个提供异常功用的非成员非友元函数(不能拜访上述那些东西)之间有一个选择,能取得更强封装性的选择是非成员非友元函数,由于它不会添加能拜访类的 private 局部的函数的数量。这就解释了为什么 clearBrowser(非成员非友元函数)比 clearEverything(成员函数)更可取:它能为 WebBrowser 取得更强的封装性。

  在这一点,有两件事值得留意。首先,这个论证只适用于非成员非友元函数。友元能像成员函数一样拜访一个类的 private 成员,因而异常影响封装。从封装的观念看,选择不是在成员和非成员函数之间,而是在成员函数和非成员非友元函数之间。(当然,封装并不是仅有的观念,假定观念来自隐式类型转换,选择就是在成员和非成员函数之间。)

  需求留意的第二件事是,假定仅仅是为了关注封装,则可以指出,一个函数是一个类的非成员并不意味着它不可以是另一个类的成员。这关于习气了一切函数必需属于类的言语(例如,Eiffel,Java,C#,等等)的顺序员是一个过度的抚慰。例如,我们可以使 clearBrowser 成为一个 utility 类的 static 成员函数。只需它不是 WebBrowser 的一局部(或友元),它就不会影响 WebBrowser 的 private 成员的封装。

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

namespace WebBrowserStuff {
 class WebBrowser { ... };
 void clearBrowser(WebBrowser& wb);
 ...
}

  绝对于方式上的自然,这样更适用于它。无论如何,由于名字空间(不像类)能展开到多个源文件中。这是很重要的,由于相似 clearBrowser 的函数是方便性函数。作为既不是成员也不是友元,他们没有对 WebBrowser 中止专门的拜访,所以他们不能提供任何一种 WebBrowser 的客户不能经过其它办法失掉的功用。例如,假定 clearBrowser 不存在,客户可以直接调用 clearCache,clearHistory 和 removeCookies 自身。

  一个相似 WebBrowser 的类可以有大批的方便性函数,一些是书签相关的,另一些打印相关的,还有一些是 cookie 管理相关的,等等。作为一个普通的常规,少数客户仅对这些方便性函数的集合中的一些感兴味。没有理由让一个只对书签相关的方便性函数感兴味的客户在编译时依赖其它函数,例如,cookie 相关的方便性函数。分隔它们的直截了当的办法就是在一个头文件中声明书签相关的方便性函数,在另一个不同的头文件中声明 cookie 相关的方便性函数,在第三个头文件声明打印相关的方便性函数,等等:

// 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++ 库组织得一样紧密。胜于有一个独自的一体式的 <C++StandardLibrary> 头文件包括 std namespace 中的一切东西,它们在许多头文件中(例如,<vector>,<algorithm>,<memory>,等等),每一个都声明了 std 中的一些机能。仅仅需求 vector 相关机能的客户不需求 #include <memory>,不必 list 的客户没有必要 #include <list>。这就允许客户在编译时仅仅依赖他们实践运用的那局部零碎。当机能来自一个类的成员函数时,用这种办法联络它是不可以的,由于一个类必需作为一个全体来定义,它不能支离破碎。

  将一切方便性函数放入多个头文件中——但是在一个 namespace 中——也意味着客户能容易地扩展方便性函数的集合。他们必需做的全部就是在 namespace 中参与更多的非成员非友元函数。例如,假定一个 WebBrowser 的客户决议写一个关于下载图像的方便性函数,他或她仅仅需求新建一个头文件,包括那些函数在 WebBrowserStuff namespace 中的声明。这个新的函数如今就像其它方便性函数一样可用并被集成。这是类不能提供的另一个特性,由于类定义关于客户是扩展封锁的。当然,客户可以派生新类,但是派生类不能拜访基类中被封装的(也就是说,private 的)成员,所以这样的“扩展机能”只需二等身份。此外,不是一切的类都是作为基类设计的。

  Things to Remember

  ·用非成员非友元函数取代成员函数。这样做可以进步封装性,包装弹性,和机能扩展性。



相关评论