4

我最近介绍了 STL、boost::graph、boost PropertyMaps 等通用编程库的设计http://www.boost.org/doc/libs/1_54_0/libs/property_map/doc/property_map.html

使用像 get(PropertyMap, key) 这样的自由函数而不是像 PropertyMap.get(key) 这样的成员函数的理由是什么?

我知道这些函数的最通用形式是在“boost”命名空间中定义的。假设我在我的命名空间“项目”中定义了一个新的 PropertyMap,定义它对应的“获取”函数的最佳位置是什么?“提升”或“项目”

4

4 回答 4

6

有两个不同的原因:

  • 更好的封装:通过最小化可以访问类属性的函数的数量,您可以改进封装。

  • 可扩展性:在 C++ 中,命名空间定义是开放的(您可以添加到它),而类定义是关闭的;因此您可以添加自由函数,但不能添加成员函数。

虽然封装更多的是一种品味问题,但可扩展性在泛型编程中极为重要。您不希望仅仅因为第 3 方类型缺少您需要的一种方法而放弃它……当然,某些类型(例如内置类型或标准库类型)根本无法扩展。

于 2013-07-17T17:31:55.690 回答
4

自由函数与特定实现的耦合不太紧密(因为它们仅依赖于容器的公共接口)。这允许更大的灵活性和易于维护。

您会注意到,当特定实现很重要时(例如std::map::find),标准库使用成员函数。

Scott Meyers 有一套规则可以很好地决定如何创建函数:http ://cpptips.com/nmemfunc_encap

于 2013-07-17T16:31:20.637 回答
3

主要动机是偏爱非成员非朋友函数有助于保持类尽可能简洁。请参阅 Herb Sutter 的文章:http ://www.gotw.ca/publications/mill02.htm 。

本文还包含对您问题另一部分的答案,将相应的get函数放在哪里。这是 C++ 的一个特性,称为参数依赖查找 (ADL)。来自 Herb Sutter,他称其为 Koenig 查询,尽管这个名字是有争议的(见下面的评论):

Koenig 查找说,如果您提供类类型的函数参数,那么要查找编译器需要查找的函数名称,不仅在本地范围等通常的位置,而且在包含的名称空间(此处为 NS)中参数的类型。

这是一个例子:

namespace MyNamespace {
    class MyClass {... };
    void func(MyClass);
}

int main(int aArgc, char* aArgv[]) {
    MyNamespace::MyClass inst;
    func(inst);  // Ok, because Koenig says look in the argument's namespace for func
}

所以简而言之,您只需在与您的类相同的命名空间中声明 get 函数。

请注意,如果您必须明确提供模板参数,这不适用于模板化函数 - 请参阅这篇文章:https ://stackoverflow.com/a/2953783/27130

于 2013-07-17T16:51:24.427 回答
2

我认为,线索就在问题中-它使它们更加通用。自由函数 likestd::find()可以与大量不同的容器一起使用,因此是通用的 - 成员 likestd::map<T>.find()仅适用于std::map,因此根本不是通用的。

有了良好的泛型函数,您应该能够设计自己的容器来与它们交互,从而免除您在容器中编写自己的方法的需要(显然也不需要编写自己的自由函数)。当您拥有一个容器时,提供一种方法是有意义的,对于该容器,特定操作可以以特定于该容器的方式比在通用函数中更好地实现(std::mapfind()方法是一个明显的例子)。

于 2013-07-17T17:29:47.723 回答