我什么时候应该更喜欢非成员非友元函数而不是成员函数
When should I prefer non-member non-friend functions to member functions?
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
不存在,客户端可以只调用clearCache
、clearHistory
和自己removeCookies
。
对我来说,便利功能的例子是合理的。但是,当非会员版本表现出色时,除了便利功能之外,还有其他例子吗?
更一般地说,何时使用哪个的规则是什么?
更一般地说,何时使用哪个的规则是什么?
以下是斯科特·迈耶的规则(来源(:
斯科特有一篇有趣的文章,倡导 非成员非友元函数改进了封装 对于类。他使用以下算法来确定 函数 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;
他对封装的定义涉及数字 私有数据时受影响的功能 成员已更改。
这几乎总结了这一切,在我看来,这也是相当合理的。
我经常选择在我的类之外构建特定于应用程序的实用程序方法。
应用程序通常处于不同的上下文中,然后引擎在下面执行工作。如果我们以 Web 浏览器为例,3 个 clear 方法属于 Web 引擎,因为这是在其他任何地方都难以实现的必需功能,但是,ClearEverything(( 肯定更特定于应用程序。在这种情况下,应用程序可能有一个小对话框,其中包含一个清除"全部"按钮,以帮助用户提高效率。也许这不是另一个重用您的 Web 浏览器引擎的应用程序想要做的事情,因此将其放在引擎类中只会更加混乱。
另一个例子是数学库中的。通常,将更高级的功能(如平均值或标准推导(作为数学类的一部分实现是有意义的。但是,如果您有特定于应用程序的方法来计算某种类型的均值,但该方法不是标准版本,则它可能应该在您的类之外,并且是特定于您的应用程序的实用程序库的一部分。
我从来都不是强硬编码规则的忠实粉丝,以这种或那种方式实现事物,这通常是意识形态和原则的问题。
米.
当库的开发人员想要编写二进制运算符时,通常使用非成员函数,这些运算符可以在具有类类型的任一参数上重载,因为如果将它们设置为类的成员,则只能在第二个参数上重载(第一个参数隐式是该类的对象(。complex
的各种算术运算符可能是这方面的决定性例子。
在你引用的例子中,动机是另一种:使用耦合最少的设计,仍然允许你完成工作。
这意味着,虽然clearEverything
可以(坦率地说,很有可能(成为会员,但我们没有使其成为会员,因为从技术上讲,它不必成为会员。这给你买了两样东西:
- 您不必承担在公共界面中拥有
clearEverything
方法的责任(一旦您附带了一个方法,您就与它终身结婚(。 - 可以访问类
private
成员的函数数量要少一个,因此将来的任何更改都将更容易执行,并且不太可能导致错误。
也就是说,在这个特殊的例子中,我觉得这个概念走得太远了,对于这样一个"无辜"的功能,我倾向于让它成为成员。但这个概念是合理的,在"现实世界"场景中,事情不是那么简单,它会更有意义。
局部性和允许类在保持封装的同时提供"足够"的功能是需要考虑的一些事项。
如果WebBrowser
在许多地方重用,依赖项/客户端可能会定义多个方便的功能。这使您的类(WebBrowser
(保持轻量级且易于管理。
相反,WebBrowser
最终取悦了所有客户,只是变成了一些难以改变的铁板一块野兽。
您是否发现该类在多个场景中使用后缺乏功能?您的便利功能中会出现模式吗?最好 (IMO( 推迟正式扩展类的接口,直到模式出现并且有充分的理由添加此功能。最小类更易于维护,但您不希望到处都是冗余实现,因为这会将维护负担推给客户端。
如果您的便利函数实现起来很复杂,或者存在可以显着提高性能的常见情况(例如,使用一个锁清空线程安全集合,而不是每次使用一个锁一次清空一个元素(,那么您可能还需要考虑这种情况。
在某些情况下,当您使用它时,您会意识到WebBrowser
中确实缺少某些东西。
- 如何使用指针传递给函数的数组中对象的函数成员
- c++构造函数成员初始化:传递参数
- 创建 std::函数,它返回具有函数成员值的变量.分段错误
- 如何在C++通过公共函数访问私有函数成员?
- 解释了构造函数成员初始化列表
- 调用std::函数成员时内存损坏
- 是否可以为模板类的模板函数成员设置别名?
- 捕获 lambda 函数C++成员变量
- 构造函数成员初始值设定项跨成员列出,安全吗?
- 获取与在模板参数中传递的函数成员类型相同的类
- 如何从公共函数成员访问地图私有成员
- C 构造函数成员分配优化
- 使用命名空间进行函数成员定义
- 函数成员作为 CUDA 内核的参数
- 模板基类函数成员的别名
- 函数成员中用于void和继承的enable_if
- 头文件中是否定义了一个很长的Class函数成员
- 类内/构造函数成员初始化
- 使用指向部分专用函数成员的指针自动填充向量
- 指向函数成员的指针