为什么不在c ++容器中实现包含函数?

时间:2017-07-01 18:53:24

标签: c++ containers contains

我最初的问题是:为什么在C ++中contains()函数会错过Containers

所以我找了一个解释,我找到了一些有趣的原因,为什么其他函数没有在所有Containers中实现(主要是因为性能问题和方便)。

我知道您可以使用find库中的algorithm函数,或者您可以使用Iterator编写自己的函数,但我无法理解的是为什么例如,在set中,contains函数(称为find)已实现,而在vectorqueue中则不是

我也很清楚为什么Container类不能像Java一样共享Collections这样的公共接口(感谢this回答)但是在这种情况下我找不到为什么不这样做的原因在所有容器类中实现contains()函数(或至少在某些类似vector中)。

谢谢

4 个答案:

答案 0 :(得分:4)

std::set实现自己的find的原因是"泛型" std::find执行线性搜索,而std::set可以通过在O(log 2 n)中搜索其树表示来做得更好。

另一方面,

std::vectorstd::list的搜索速度要快于线性时间,因此它们依赖于std::find实现。

请注意,您仍然可以将std::find应用于std::set以进行线性搜索;它不会像使用set自己的find成员函数一样高效。

std::set<int> s {1, 2, 3, 4, 5, 6};
auto res = std::find(s.begin(), s.end(), 3);
std::cout << *res << std::endl; // prints 3

答案 1 :(得分:4)

因为这是一个糟糕的设计模式。如果您在线性容器上反复使用“find”,则代码会出现问题。平均和最差情况时间复杂度仍为O(n),这意味着您做出了错误的选择。

例如,std::mapstd::unordered_map具有find成员函数,允许O(logn)和O(1)查找。这是因为容器通过这些方法使用有效的项目查找:这就是容器的使用方式。

如果您已经权衡了所有选项,并确定线性容器是您数据的最佳模型,但确实需要在极少数情况下找到元素,std::find()允许您这样做。你不应该依赖它。我将其视为Python和Java中的反模式,并且在C ++中具有优势。

就像个人笔记一样,3年前,当我是一名初学者时,我写了很多if mydict in list: do_something()。我认为这是一个好主意,因为Python使列表项成员资格成为惯用语。我不知道更好。这导致我产生了糟糕的代码,直到我了解为什么线性搜索与二进制搜索和hashmap查找相比效率低下。编程语言或框架应该能够实现良好的设计模式,并阻止不良模式。启用线性搜索是一种糟糕的设计模式。

答案 2 :(得分:2)

由于某种原因,其他答案集中在广义和每容器find方法的复杂性上。但他们无法解释OP的问题。缺乏有用的实用程序方法的实际原因源于在C ++中使用来自不同文件的类的方式。如果每个不包含任何特殊属性的容器都会contains方法执行具有线性复杂性的通用搜索,那么当每个容器标头还包含<algorithm>或每个容器标头重新实现它时,我们会遇到这种情况。 ; s自己的find算法(更糟糕的是)。在预处理器包含(即复制粘贴)之后,在每个翻译单元的编译期间构建的相当大的文档构建每个包含的标题将变得更大。编译需要更多时间。相当神秘的编译错误消息可能会变得更长。当一个非成员函数可以完成这项工作(并且仅包括你将要使用的东西)时,这个原则是不使成员函数存在是有原因的。

请注意,最近有一项针对uniform call syntax的提案可能允许将实用程序方法混合到类中。如果它上线,可能会编写这样的扩展函数:

template< typename TContainer, typename TItem > bool
contains(TContainer const & container, TItem const & item)
{
     // Some implementation possibly calling container member find
     // or ::std::find if container has none...
}

::std::vector< int > registry;
registry.contains(3); // false

答案 3 :(得分:1)

有一个很好的理由,容器不共享一个共同的(继承的)接口&#34; (就像在Java中),这是C++仿制药如此强大的原因。为什么只能为所有容器编写一次代码时为每个容器编写代码?这是构建STL的主要原则之一。

如果使用依赖于公共继承的接口的成员函数的容器,则必须为每个容器编写 find 函数。这是浪费和糟糕的工程。好的设计说如果你只能在一个地方编写代码,因为你只需要从那个地方删除bug,并在一个地方修复一个bug来修复无处不在的

因此STL背后的理念是 算法 算法一次,它适用于所有容器。一旦调试算法,就会为所有容器调试它。

美中不足的是,由于内部结构,一些容器可以做出更有效的决策。对于那些容器,添加了类型特定的功能,这将利用这种效率。

但是大多数功能应该容器分开。它被称为 decoupling ,它可以减少错误,同时促进代码重用,通常远远超过 polymorphism ,这就像Java容器这样的库使用(一个常见的继承接口) )。