有一段时间我一直在设计我的类接口是最小的,更喜欢命名空间包装的非成员函数而不是成员函数.基本上遵循Scott Meyer在非成员函数如何改进封装的文章中的建议.
我在一些小规模的项目中一直在这方面做得很好,但我想知道它在更大规模上的运作情况.是否有任何大型的,备受推崇的开源C++项目,我可以看看,也许参考这个建议被强烈遵循的地方?
更新:感谢所有的意见,但我并不是真的对意见感兴趣,而是在大规模的实践中找出它的效果.尼克的答案在这方面最接近,但我希望能够看到代码.任何形式的实践经验的详细描述(积极,消极,实际考虑等)也是可以接受的.
Nic*_*ick 11
我在我工作的项目上做了很多这样的事情; 在我目前的公司中最大的是大约2M线,但它不是开源的,所以我不能提供它作为参考.但是,一般来说,我会说我同意这个建议.您可以越多地将未严格包含的功能与该对象中的一个对象分开,您的设计就越好.
举个例子,考虑一下经典的多态性示例:一个带有子类的Shape基类和一个虚拟的Draw()函数.在现实世界中,Draw()需要采用一些绘图上下文,并且可能知道正在绘制的其他内容的状态,或者一般的应用程序.一旦将所有这些都放入Draw()的每个子类实现中,您可能会有一些代码重叠,或者您的大多数实际Draw()逻辑将在基类中或其他地方.然后考虑如果你想重用一些代码,你需要在界面中提供更多的入口点,并且可能使用与绘图形状无关的其他代码污染函数(例如:多形图绘制相关逻辑).不久之后,它会变得一团糟,你会希望你有一个绘制函数,它采用了一个形状(和上下文,
无论如何,这是我的经验/建议,值得的.
我认为随着项目规模的增加,非成员函数的好处也会增加.标准库容器,迭代器和算法库就是证明.
如果您可以从数据结构中分离算法(或者,以另一种方式表达,如果您可以将对象的操作与内部状态的操作分离),则可以减少类之间的耦合并更好地利用通用代码.
斯科特迈耶斯并不是唯一支持这一原则的作者; Herb Sutter也有,特别是在Monoliths Unstrung中,以指南结束:
在可能的情况下,更喜欢将函数写为非成员非朋友.
我认为该文章中一个不必要的成员函数的最佳例子之一是std::basic_string::find; 它没有理由存在,真的,因为std::find它提供了完全相同的功能.
以非成员非友元的身份编写函数的一个实际优势是,这样做可以显着减少彻底测试和验证代码所需的时间。
例如,考虑序列容器成员函数insert和push_back。至少有两种实现方法push_back:
insert(它的行为是根据insert无论如何定义的)insert(可能调用私有辅助函数)而无需实际调用insert显然,在实现序列容器时,您可能希望使用第一种方法。 push_back只是insertand (据我所知)的一种特殊形式,您无法通过实现push_back其他方式(至少不是 for list, deque, or vector)来真正获得任何性能优势。
但是,要彻底测试这样的容器,您必须push_back单独进行测试:由于push_back是成员函数,因此它可以修改容器的任何和所有内部状态。从测试的角度来看,您应该(必须?)假设它push_back是使用第二种方法实现的,因为它有可能使用第二种方法实现。不能保证它是在 方面实现的insert。
如果push_back实现为非成员非友元,则不能触及容器的任何内部状态;它必须使用第一种方法。当你为它编写测试时,你知道它不能破坏容器的内部状态(假设实际的容器成员函数被正确实现)。您可以使用这些知识来显着减少为充分执行代码而需要编写的测试数量。
OpenCV库执行此操作。他们有一个cv :: Mat类,可显示3D矩阵(或图像)。然后它们在cv名称空间中具有所有其他功能。
OpenCV库非常庞大,并且在其领域中得到广泛认可。