我最近介绍了通用编程库的设计,如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”函数的最佳位置是什么? “提升”或“项目”
答案 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}}方法是显而易见的例子)。