为什么通用编程设计更喜欢免费功能而不是成员函数?

时间:2013-07-17 16:23:32

标签: c++ generics boost

我最近介绍了通用编程库的设计,如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”命名空间中定义的。假设我在命名空间“project”中定义了一个新的PropertyMap,定义它的相应“get”函数的最佳位置是什么? “提升”或“项目”

4 个答案:

答案 0 :(得分:6)

有两个不同的原因:

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

  • 可扩展性:在C ++中,一个名称空间定义是打开的(您可以添加它),同时关闭一个类定义;因此,您可以添加免费功能,但不能添加成员功能。

虽然封装更多的是品味,但可扩展性在通用编程中非常重要。您不想放弃第三方类型只是因为它缺少您需要的一种方法......当然,某些类型(例如内置类型或标准库类型)根本无法扩展。

答案 1 :(得分:4)

自由函数与特定实现的耦合程度较低(因为它们仅依赖于容器的公共接口)。这样可以提供更大的灵活性和易维护性。

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

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

答案 2 :(得分:3)

主要动机是偏好非成员非友情功能有助于使课程尽可能简洁。请参阅Herb Sutter的文章:http://www.gotw.ca/publications/mill02.htm

本文还包含问题其他部分的答案,在哪里放置相应的get函数。它是C ++的一个名为Argument Dependent Lookup(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

答案 3 :(得分:2)

我认为线索是问题所在 - 它使它们更具通用性。像std::find()这样的免费函数可以与大量不同的容器一起使用,因此是通用的 - 像std::map<T>.find()之类的成员只能使用std::map,因此根本不是通用的。

通过良好的通用功能,您应该能够设计自己的容器,可以与它们进行交互,从而使您无需在容器中编写自己的方法(显然需要编写自己的自由函数) 。当你有一个容器时,提供一个方法是有意义的,特定操作可以以特定于该容器的方式比在泛型函数中更好地实现(std::map的{​​{1}}方法是显而易见的例子)。