C++ 类设计与IDE:非成员非朋友函数真的值得吗?

C++ 类设计与IDE:非成员非朋友函数真的值得吗?,c++,ide,function,class-design,friend,C++,Ide,Function,Class Design,Friend,在(其他方面)优秀的书籍第44项中,题为“更喜欢编写非成员非朋友函数”,Sutter和Alexandrescu建议,只有真正需要访问类成员的函数才是该类的成员。可以仅使用成员函数编写的所有其他操作都不应该是类的一部分。他们应该是非成员和非朋友。理由是: 它促进了封装,因为需要访问类内部的代码更少 它使编写函数模板变得更容易,因为您不必每次都猜测某个函数是否是成员 它使类保持小规模,从而使测试和维护变得更容易 虽然我看到了这些参数的价值,但我看到了一个巨大的缺点:我的IDE不能帮助我找到这些函

在(其他方面)优秀的书籍第44项中,题为“更喜欢编写非成员非朋友函数”,Sutter和Alexandrescu建议,只有真正需要访问类成员的函数才是该类的成员。可以仅使用成员函数编写的所有其他操作都不应该是类的一部分。他们应该是非成员和非朋友。理由是:

  • 它促进了封装,因为需要访问类内部的代码更少
  • 它使编写函数模板变得更容易,因为您不必每次都猜测某个函数是否是成员
  • 它使类保持小规模,从而使测试和维护变得更容易
虽然我看到了这些参数的价值,但我看到了一个巨大的缺点:我的IDE不能帮助我找到这些函数每当我有某种类型的对象,并且我想查看它上有哪些操作可用时,我不能再键入“
pMysteriousObject->
”并获取成员函数列表

保持一个干净的设计归根结底是为了让你的编程生活更轻松。但这实际上会让我的更难


所以我想知道这是否真的值得麻烦你是如何处理的?

在这一点上,我不同意萨特和亚历山德雷斯库的观点。我认为如果函数
foo()
的行为属于类
Bar
的职责范围,那么
foo()
应该是
Bar()
的一部分

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

我完全同意与外围设备相关的函数不应该是类的一部分,但是如果某些东西是类职责的核心,那么它就没有理由不应该是成员,不管它是否直接处理成员数据

关于这些具体问题:

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

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

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

我一点也不明白。如果你从类中提取了一堆函数,你就把更多的责任推到了函数模板上。他们被迫假设由类模板参数提供的功能更少,除非我们假设从类中提取的大多数函数都将转换为模板(ugh)

它使类保持小规模,从而使测试和维护变得更容易


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

如果您给他们一个通用前缀,那么如果您键入

::prefix


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

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

为了使我的类保持简单,并且仍然能够集中外部函数,我经常使用与我的类甚至名称空间一起工作的实用程序类。 我首先创建一个类,它将包装我的数据并公开最简单的接口。然后,我为必须使用该类执行的每个任务创建一个新类


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

在许多OOP语言中,非朋友非类方法是居住在孤儿院的三等公民,与任何东西都没有连接。当我写一个方法时,我喜欢选择好的家长——一个合适的班级——在那里他们有最好的机会感受到欢迎和帮助。

我本以为IDE实际上在帮助你

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


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

Scott Meyers与Sutter的观点类似,请参阅

他还明确指出:

基于他对各种字符串类的研究,Jack Reeves观察到有些函数在成为非成员时“感觉”不好,即使它们可能是非友元非成员。只有平衡许多相互竞争的关注点,才能找到类的“最佳”接口,其中封装程度只有一个

如果一个函数成为一个成员函数是“有意义的”
namespace::prefix