5

在(否则)优秀的书C++ 编码标准第 44 条,标题为“Prefer writing nonmember nonfriend functions”中,Sutter 和 Alexandrescu 建议只有真正需要访问类成员的函数本身就是该类的成员。仅使用成员函数可以编写的所有其他操作不应该是类的一部分。他们应该是非会员和非朋友。论据是:

  • 它促进了封装,因为需要访问类内部的代码更少。
  • 它使编写函数模板更容易,因为您不必每次都猜测某个函数是否是成员。
  • 它使类保持较小,从而使其更易于测试和维护。

虽然我看到了这些参数的价值,但我看到了一个巨大的缺点:我的 IDE 无法帮助我找到这些函数!每当我有某种对象时,我想看看它上面有哪些可用的操作,我不能再只输入“ pMysteriousObject->”来获取成员函数的列表。

保持简洁的设计最终是为了让您的编程生活更轻松。但这实际上会使我的工作更加困难。

所以我想知道这是否真的值得麻烦。你怎么处理?

4

7 回答 7

5

Scott Meyers 的观​​点与 Sutter 相似,请参见此处

他还明确指出:

“根据他对各种类似字符串的类的工作,Jack Reeves 观察到某些函数在成为非成员时不会“感觉”正确,即使它们可能是非朋友非成员。“最佳”界面因为只有通过平衡许多相互竞争的关注点才能找到一个类,封装程度只是其中之一。”

如果一个函数成为一个成员函数“才有意义”,那就让它成为一个。同样,如果它不是真正的主界面的一部分,并且成为非成员“才有意义”,那么就这样做。

需要注意的是,对于重载版本,例如 operator==(),语法保持不变。所以在这种情况下,你没有理由让它成为与类在同一位置声明的非成员非朋友浮动函数,除非它真的需要访问私有成员(根据我的经验,它很少会这样做)。即使这样,您也可以将 operator!=() 定义为非成员,并根据 operator==()。

于 2008-09-26T04:37:20.137 回答
5

我不认为说他们之间,Sutter、Alexandrescu 和 Meyers 在 C++ 质量方面所做的比其他任何人都多。

他们提出的一个简单问题是:

如果一个实用函数有两个独立的类作为参数,那么哪个类应该“拥有”成员函数?

另一个问题是,您只能在相关类受您控制的情况下添加成员函数。您为 std::string 编写的任何辅助函数都必须是非成员,因为您无法重新打开类定义。

对于这两个示例,您的 IDE 将提供不完整的信息,您将不得不使用“老式方式”。

鉴于世界上最有影响力的 C++ 专家认为具有类参数的非成员函数是类接口的一部分,这更多是您的 IDE 的问题,而不是编码风格的问题。

您的 IDE 可能会在一两个版本中发生变化,您甚至可以让他们添加此功能。如果你改变你的编码风格以适应今天的 IDE,你很可能会发现你将来会遇到更大的问题,即不可扩展/不可维护的代码。

于 2008-09-26T10:17:33.907 回答
3

我将不得不在这一点上不同意 Sutter 和 Alexandrescu 的观点。我认为如果 function 的行为foo()属于 classBar的职责范围,那么foo()应该是bar().

foo()不需要直接访问的成员数据这一事实Bar并不意味着它在概念上不是Bar. 这也可能意味着代码被很好地分解。拥有通过其他成员函数执行所有行为的成员函数并不少见,我不明白为什么会这样。

我完全同意与外围相关的功能应该是类的一部分,但是如果某些东西是类职责的核心,那么它没有理由不应该是成员,无论它是否直接与成员数据混在一起。

至于这些具体点:

它促进了封装,因为需要访问类内部的代码更少。

事实上,直接访问内部的函数越少越好。这意味着让成员函数尽可能多地通过其他成员函数来做是一件好事。将分解良好的函数从类中分离出来只会给你留下一个半类,这需要一堆有用的外部函数。从它们的类中拉出分解良好的函数似乎也阻碍了编写分解良好的函数。

它使编写函数模板更容易,因为您不必每次都猜测某个函数是否是成员。

我完全不明白这一点。如果你从类中拉出一堆函数,你就将更多的责任推给了函数模板。他们被迫假设他们的类模板参数提供的功能更少,除非我们假设从他们的类中提取的大多数函数都将被转换为模板(呃)。

它使类保持较小,从而使其更易于测试和维护。

嗯,当然。它还创建了许多额外的外部功能来测试和维护。我看不到这其中的价值。

于 2008-09-25T20:06:27.680 回答
1

确实,外部函数不应该是接口的一部分。从理论上讲,您的类应该只包含数据并公开其预期的接口,而不是实用功能。向接口添加实用程序函数只会增加类代码库并使其难以维护。我目前维护一个包含大约 50 个公共方法的类,这太疯狂了。

现在,实际上,我同意这并不容易执行。向您的类添加另一个方法通常更容易,如果您使用的 IDE 可以真正简单地向现有类添加新方法,则更容易。

为了使我的类保持简单并且仍然能够集中外部功能,我经常使用与我的类甚至命名空间一起使用的实用程序类。我首先创建将包装我的数据并公开最简单的接口的类。然后,我为与班级相关的每项任务创建一个新班级。

示例:创建一个类 Point,然后添加一个类 PointDrawer 将其绘制到位图,PointSerializer 将其保存等。

于 2008-09-25T20:17:14.690 回答
0

如果你给他们一个共同的前缀,那么你的 IDE 可能会在你输入时提供帮助

::prefix

或者

namespace::prefix
于 2008-09-25T20:10:06.300 回答
0

在许多 OOP 语言中,non-friend non-class 方法是三等公民,它们驻留在孤儿院中,与任何事物都没有联系。当我写一个方法时,我喜欢选择好的父母——一个合适的班级——在那里他们最有机会感到受欢迎和帮助。

于 2008-09-25T21:23:47.417 回答
-1

我会认为 IDE 实际上是在帮助你。

IDE正在从列表中隐藏受保护的函数,因为它们不像类的设计者所期望的那样对公众可用。

如果您在该类的范围内并键入了this->,那么受保护的函数将显示在列表中。

于 2008-09-26T04:09:00.547 回答