[英]When should I prefer non-member non-friend functions to member functions?
Meyers mentioned in his book Effective C++ that in certain scenarios non-member non-friend functions are better encapsulated than member functions. Meyers在他的书“Effective C ++”中提到,在某些情况下,非成员非朋友函数比成员函数更好地封装。
Example: 例:
// Web browser allows to clear something
class WebBrowser {
public:
...
void clearCache();
void clearHistory();
void removeCookies();
...
};
Many users will want to perform all these actions together, so WebBrowser
might also offer a function to do just that: 许多用户希望一起执行所有这些操作,因此
WebBrowser
可能还提供了一个功能:
class WebBrowser {
public:
...
void clearEverything(); // calls clearCache, clearHistory, removeCookies
...
};
The other way is to define a non-member non-friend function. 另一种方法是定义非成员非朋友函数。
void clearBrowser(WebBrowser& wb)
{
wb.clearCache();
wb.clearHistory();
wb.removeCookies();
}
The non-member function is better because "it doesn't increase the number of functions that can access the private parts of the class.", thus leading to better encapsulation. 非成员函数更好,因为“它不会增加可以访问类的私有部分的函数的数量。”,从而导致更好的封装。
Functions like clearBrowser
are convenience functions because they can't offer any functionality a WebBrowser
client couldn't already get in some other way. 像
clearBrowser
这样的函数是便利函数,因为它们无法提供WebBrowser
客户端无法以其他方式获得的任何功能。 For example, if clearBrowser
didn't exist, clients could just call clearCache
, clearHistory
, and removeCookies
themselves. 例如,如果
clearBrowser
不存在,客户端可以只调用clearCache
, clearHistory
和removeCookies
本身。
To me, the example of convenience functions is reasonable. 对我而言, 便利功能的例子是合理的。 But is there any example other than convenience function when non-member version excels?
但是当非会员版本擅长时,除了便利功能之外还有其他例子吗?
More generally, what are the rules of when to use which ? 更一般地说, 什么时候使用哪些规则 ?
More generally, what are the rules of when to use which?
更一般地说, 什么时候使用哪些规则?
Here is what Scott Meyer's rules are ( source ): 以下是Scott Meyer的规则( 来源 ):
Scott has an interesting article in print which advocates that non-member non-friend functions improve encapsulation for classes.
Scott有一篇有趣的文章,主张非成员非朋友函数改进了类的封装。 He uses the following algorithm to determine where a function f gets placed:
他使用以下算法来确定函数f的放置位置:
if (f needs to be virtual) make fa member function of C; else if (f is operator>> or operator<<) { make fa non-member function; if (f needs access to non-public members of C) make fa friend of C; } else if (f needs type conversions on its left-most argument) { make fa non-member function; if (f needs access to non-public members of C) make fa friend of C; } else if (f can be implemented via C's public interface) make fa non-member function; else make fa member function of C;
His definition of encapsulation involves the number of functions which are impacted when private data members are changed.
他对封装的定义涉及私有数据成员更改时受影响的函数数量。
Which pretty much sums it all up, and it is quite reasonable as well, in my opinion. 在我看来,这几乎总结了一切,这也很合理。
I often choose to build utility methods outside of my classes when they are application specific. 我经常选择在我的类之外构建实用程序方法,因为它们是特定于应用程序的。
The application is usually in a different context then the engines doing the work underneath. 应用程序通常处于不同的环境中,然后是引擎完成下面的工作。 If we take you example of a web browser, the 3 clear methods belongs to the web engine as this is needed functionality that would be difficult to implement anywhere else, however, the ClearEverything() is definitely more application specific.
如果我们以网络浏览器为例,3个明确的方法属于Web引擎,因为这是其他任何地方难以实现的功能,但ClearEverything()肯定更具针对性。 In this instance your application might have a small dialog that has a clear all button to help the user be more efficient.
在这种情况下,您的应用程序可能会有一个小对话框,其中包含一个清除所有按钮,以帮助用户提高效率。 Maybe this is not something another application re-using your web browser engine would want to do and therefor having it in the engine class would just be more clutter.
也许这不是另一个应用程序重新使用您的Web浏览器引擎想要做的事情,因此在引擎类中使用它会更加混乱。
Another example is a in a mathematic libraries. 另一个例子是数学图书馆。 Often it make sense to have more advanced functionality like mean value or standard derivation implemented as part of a mathematical class.
通常,将平均值或标准派生等更高级的功能作为数学类的一部分实现是有意义的。 However, if you have an application specific way to calculate some type of mean that is not the standard version, it should probably be outside of your class and part of a utility library specific to you application.
但是,如果您有一种特定于应用程序的方法来计算某些非标准版本的均值,则它应该在您的类之外,并且应该是特定于您的应用程序的实用程序库的一部分。
I have never been a big fan of strong hardcoded rules to implement things in one way or another, it's often a matter of ideology and principles. 我从来不是强大的硬编码规则的忠实粉丝,以这种或那种方式实现事物,这通常是意识形态和原则的问题。
M. M.
Non-member functions are commonly used when the developer of a library wants to write binary operators that can be overloaded on either argument with a class type, since if you make them a member of the class you can only overload on the second argument (the first is implicitly an object of that class). 当库的开发人员想要编写可以在具有类类型的任一参数上重载的二元运算符时,通常使用非成员函数,因为如果使它们成为类的成员,则只能在第二个参数上重载(首先是隐含的那个类的对象)。 The various arithmetic operators for
complex
are perhaps the definitive example for this. complex
的各种算术运算符可能是这方面的明确例子。
In the example you cite, the motivation is of another kind: use the least coupled design that still allows you to do the job . 在你引用的例子中,动机是另一种: 使用仍然允许你完成工作的最小耦合设计 。
This means that while clearEverything
could (and, to be frank, would be quite likely to) be made a member, we don't make it one because it does not technically have to be. 这意味着虽然
clearEverything
都可以(而且,坦率地说,很可能)成为一个成员,但我们不会将其作为一个,因为它在技术上不是必须的。 This buys you two things: 这给你买了两件东西:
clearEverything
method in your public interface (once you ship with one, you 're married to it for life). clearEverything
方法的责任(一旦您携带一个方法,您终身嫁给它)。 private
members of the class is one lower, hence any changes in the future will be easier to perform and less likely to cause bugs. private
成员的函数数量较少,因此将来的任何更改都将更容易执行,并且不太可能导致错误。 That said, in this particular example I feel that the concept is being taken too far, and for such an "innocent" function I 'd gravitate towards making it a member. 也就是说,在这个特殊的例子中,我觉得这个概念已经走得太远了,对于这种“无辜”的功能,我倾向于让它成为一个成员。 But the concept is sound, and in "real world" scenarios where things are not so simple it would make much more sense.
但这个概念是合理的,而在“现实世界”的情况下,事情并非如此简单,它会更有意义。
Locality and allowing the class to provide 'enough' features while maintaining encapsulation are some things to consider. 在保持封装的同时允许类提供“足够”功能的位置是一些需要考虑的事项。
If WebBrowser
is reused in many places, the dependencies/clients may define multiple convenience functions. 如果
WebBrowser
在许多地方重用,则依赖关系/客户端可以定义多个便利功能。 This keeps your classes ( WebBrowser
) lightweight and easy to manage. 这使您的类(
WebBrowser
)轻量级且易于管理。
The inverse would be that the WebBrowser
ends up pleasing all clients, and just becomes some monolithic beast that is difficult to change. 相反的是,
WebBrowser
最终取悦了所有客户端,并且变成了一些难以改变的单片兽。
Do you find the class is lacking functionality once it has been put to use in multiple scenarios? 一旦在多种场景中使用该类,您是否发现该类缺乏功能? Do patterns emerge in your convenience functions?
您的便利功能中是否出现了模式? It's best (IMO) to defer formally extending the class's interface until patterns emerge and there is a good reason to add this functionality.
最好(IMO)推迟正式扩展类的接口,直到模式出现并且有充分的理由添加此功能。 A minimal class is easier to maintain, but you don't want redundant implementations all over the place because that pushes the maintenance burden onto your clients.
最小的类更易于维护,但您不希望在整个地方进行冗余实施,因为这会将维护负担推到客户端上。
If your convenience functions are complex to implement, or there is a common case which can improve performance significantly (eg to empty a thread safe collection with one lock, rather than one element at a time with a lock each time), then you may also want to consider that case. 如果您的便利功能实现起来很复杂,或者存在可以显着提高性能的常见情况(例如,使用一个锁清空线程安全集合,而不是每次锁定一个元素),那么您也可以我想考虑那个案子。
There will also be cases where you realize something is genuinely missing from the WebBrowser
as you use it. 还有一些情况,当您使用它时,您会发现
WebBrowser
确实遗漏了某些内容。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.