我的第一个问题是:为什么C++中EDCOX1的0个函数在EDOCX1中丢失?1?
所以我寻找了一个解释,我发现了一些有趣的东西,关于为什么其他一些功能没有在所有Containers中实现(本质上是因为性能问题和便利性)。
我知道你可以使用algorithm库中的find函数,或者你可以用Iterator编写自己的函数,但我不明白的是,为什么在set中实现了contains函数(这里称为find,而在EDOCX1中实现了〔9〕或queue不是。
我也很清楚为什么容器类在Java中不共享像EDCOX1,11,DO这样的公共接口(由于这个答案),但是在这种情况下,我找不到为什么不在所有容器类中实现EDCOX1×0函数(或者至少在一些像EDCOX1 9)中实现的原因。
谢谢你
- "如果你能用一个自由的函数来做,那么就用一个成员函数来做。"std::find对于所有的容器都很好。有些集装箱采用了.find构件,利用集装箱的内部结构来提高效率。
- 嗯,C++只是丑陋的东西。处理好它。
- @这句话充其量就是有争议的。也许你的C++代码很难看。
- 如果可以对所有外部使用一个函数,为什么要在每个容器中实现一个函数?
- 谢谢,现在很清楚了。
- 注:std::set也有count,这在语义上更接近于contains。
- "CAPPONBOVIOLY,我承认我的代码充满了可怕的东西,如EDOCX1 5",在Access指定或EDCOX1,6,后类定义,但为什么你不在C++标准委员会成员,如果你的是如此华丽?
- @你见过失落方舟的袭击者的末日吗?
因为这是一个糟糕的设计模式。如果您在线性容器上重复使用"find",那么您的代码有问题。平均和最坏情况下的时间复杂性仍然是O(N),这意味着你做了一个错误的选择。
例如,std::map和std::unordered_map具有find成员函数,允许o(logn)和o(1)查找。这是因为容器通过这些方法采用了高效的项查找:这就是容器应该如何使用的方式。
如果您已经权衡了所有选项,并决定线性容器是您的数据的最佳模型,但在极少数情况下确实需要找到一个元素,那么std::find()允许您这样做。你不应该依赖它。我将此视为Python和Java中的反模式,以及C++中的好处。
3年前,当我还是一个初学者的程序员时,我写了很多东西。我认为这是个好主意,因为python使列表项成员成为惯用的。我不太清楚。这导致我产生了糟糕的代码,直到我了解到为什么线性搜索比二进制搜索和hashmap查找效率低。编程语言或框架应该支持好的设计模式,而不鼓励坏的设计模式。启用线性搜索是一种糟糕的设计模式。
std::set实现自己的find的原因是"generic"std::find执行线性搜索,而std::set通过在o(log2n)中搜索其树表示可以做得更好。
另一方面,std::vector和std::list的搜索速度不能超过线性时间,因此它们依赖于std::find的实现。
请注意,您仍然可以将std::find应用于std::set以线性搜索它;它只是不如使用集合自己的find成员函数那么有效。
1 2 3
| 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 |
- 谢谢你的回答。还有一件事:您已经说过find函数可以用于任何容器,其中一些函数由于性能改进而具有特定的功能。我很清楚,不完全清楚的是:例如,为什么不在Vector类中也实现find函数?我的意思是,代码可以和算法查找函数相同,但我会这样做(可能是错误的)只是为了完整性。为什么我错了?
- 我在注释中找到了答案:如果您可以为所有外部使用一个函数,为什么要在每个容器中实现一个函数?谢谢,曼尼66
- @Marcoluszara,这是一个个人的轶事,但我认为在Python中加入List是个好主意。我经常在20K条以上的清单上查找10000条。在我看来,鼓励糟糕的设计模式会产生真正的后果。
由于某些原因,其他答案集中在通用的和按容器的find方法的复杂性上。但是他们没有解释手术的问题。缺少有用的实用方法的真正原因来自C++中使用不同文件类的方式。如果每个不具有任何特殊属性的容器都有contains方法以线性复杂度执行一般搜索,那么我们要么陷入每个容器头也包括的情况,要么每个容器头重新实现它自己的find的情况。]算法(更糟)。在编译每个翻译单元的过程中,在预处理器包含(即复制粘贴)每个包含的头段之后,生成的相当大的文档会增长得更大。编译需要更多的时间。而传递的相当神秘的编译错误可能会更长。当一个非成员函数可以完成任务(并且只包括你将要使用的东西)时,不让成员函数的旧原则是有原因的。
注意,最近有一个关于uniform call syntax的建议,可能允许将实用方法混合到类中。如果它投入使用,可能会编写如下扩展函数:
1 2 3 4 5 6 7 8 9
| 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 |
号
容器不共享"公共(继承)接口"(如在Java中)是一个很好的原因,这正是C++泛型如此强大的原因。当您只能为所有容器编写一次代码时,为什么要为每个容器编写代码?这是STL建立的主要原则之一。
如果使用容器依赖于来自公共继承接口的成员函数,则必须为每个容器编写find函数。这是浪费和糟糕的工程。好的设计表明,如果你只能在一个地方编写代码,那么你就应该这样做,因为你只需要从那个地方删除bug,在一个地方修复bug就可以在任何地方修复bug。
因此,STL背后的原理是将算法与容器分开,这样您只需编写一次算法,它就可以适用于所有容器。一旦对算法进行了调试,将对所有容器进行调试。
这种软膏的一个缺点是,由于容器的内部结构,一些容器可以做出更有效的决策。对于这些容器,已经添加了一个特定于类型的函数,它将利用这种效率。
但是大多数功能应该与容器分开。它被称为去耦(decoupling),它在促进代码重用的同时减少了错误,这通常比多态性(类似于Java容器的库使用的多态性)要多得多。