我应该何时更喜欢非会员非朋友功能到会员功能?

时间:2011-10-19 12:29:19

标签: c++ encapsulation

Meyers在他的“Effective C ++”一书中提到,在某些情况下,非成员非朋友函数的封装比成员函数更好。

示例:

// Web browser allows to clear something
class WebBrowser {
public:
  ...
  void clearCache();
  void clearHistory();
  void removeCookies();
  ...
};

许多用户希望一起执行所有这些操作,因此WebBrowser也可能提供执行此操作的功能:

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

另一种方法是定义非成员非朋友函数。

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

非成员函数更好,因为“它不会增加可以访问类的私有部分的函数的数量。”,从而导致更好的封装。

clearBrowser这样的函数是便利函数,因为它们无法提供WebBrowser客户端无法以其他方式获取的任何功能。例如,如果clearBrowser不存在,则客户可以自行调用clearCacheclearHistoryremoveCookies

对我来说,便利功能的例子是合理的。但是当非会员版本擅长时,除了便利功能之外还有其他例子吗?

更一般地说,何时使用的规则是什么?

4 个答案:

答案 0 :(得分:20)

  

更一般地说,何时使用哪些规则?

以下是Scott Meyer的规则(source):

  斯科特有一篇有趣的文章,主张   非成员非朋友函数改进了封装   对于课程。他使用以下算法来确定   放置函数f的地方:

if (f needs to be virtual)
    make f a member function of C;
else if (f is operator>> or operator<<)
{
   make f a non-member function;
   if (f needs access to non-public members of C)
      make f a friend of C;
}
else if (f needs type conversions on its left-most argument)
{
   make f a non-member function;
   if (f needs access to non-public members of C)
      make f a friend of C;
}
else if (f can be implemented via C's public interface)
   make f a non-member function;
else
   make f a member function of C;
     

他对封装的定义涉及到数字   私有数据受影响的功能   成员被改变了。

这几乎总结了一切,在我看来这也很合理。

答案 1 :(得分:3)

我经常选择在特定于应用程序时在我的类之外构建实用程序方法。

应用程序通常处于不同的上下文中,然后是在下面执行工作的引擎。如果我们以网络浏览器为例,3个明确的方法属于Web引擎,因为这是其他任何地方难以实现的功能,但ClearEverything()肯定更具针对性。在这种情况下,您的应用程序可能会有一个小对话框,其中包含一个清除所有按钮,以帮助用户提高效率。也许这不是另一个应用程序重新使用您的Web浏览器引擎所希望做的事情,因此在引擎类中使用它会更加混乱。

另一个例子是数学图书馆。通常,将平均值或标准派生等更高级的功能作为数学类的一部分实现是有意义的。但是,如果您有一种特定于应用程序的方法来计算某些不是标准版本的均值,那么它应该在您的类之外,并且应该是特定于您的应用程序的实用程序库的一部分。

我从来不是强大的硬编码规则的忠实粉丝,以某种方式实现事物,这通常是意识形态和原则的问题。

微米。

答案 2 :(得分:0)

当库的开发人员想要编写可以在具有类类型的任一参数上重载的二元运算符时,通常使用非成员函数,因为如果使它们成为类的成员,则只能在第二个类上重载参数(第一个隐含地是该类的对象)。 complex的{​​{3}}可能就是明确的例子。

在你引用的例子中,动机是另一种:使用仍然允许你完成工作的最少耦合设计

这意味着虽然clearEverything可以(而且,坦率地说,很可能)成为会员,但我们不会将其设为一个,因为它在技术上不一定是。这会给你带来两件事:

  1. 您不必承担在公共界面中使用clearEverything方法的责任(一旦您携带一个方法,您终身与它结婚)。
  2. 可以访问该类private成员的函数数量较少,因此将来的任何更改都将更容易执行,并且不太可能导致错误。
  3. 那就是说,在这个特殊的例子中,我觉得这个概念已经走得太远了,对于这种“无辜”的功能,我倾向于让它成为一个成员。但这个概念是合理的,而在“现实世界”的情况下,事情并不那么简单,它会更有意义。

答案 3 :(得分:0)

本地化并允许类在保持封装的同时提供“足够”的功能是一些需要考虑的事项。

如果在许多地方重用WebBrowser,则依赖关系/客户端可以定义多个便利功能。这使您的课程(WebBrowser)轻松且易于管理。

相反的是WebBrowser最终取悦所有客户,并且变成了一些难以改变的单片兽。

一旦在多个场景中使用该类,您是否发现该类缺少功能?您的便利功能中是否出现了模式?最好(IMO)推迟正式扩展类的接口,直到模式出现并且有充分的理由添加此功能。最小的类更容易维护,但是您不希望在整个地方实施冗余,因为这会将维护负担推到客户端。

如果您的便利功能实现起来很复杂,或者存在可以显着提高性能的常见情况(例如,使用一个锁清空线程安全集合,而不是每次锁定一个元素),那么您可能还想考虑这种情况。

在您使用WebBrowser时,您也会发现{{1}}确实遗漏了某些内容。