10

最近,在阅读了这篇博文后,我的脑海中出现了一个危险的想法。这个想法可以这样表达:

我不需要 C++ 标准库提供的大部分内容。那么,为什么我不实现一个不太通用但更易于使用的版本呢?

例如,使用 STL 会吐出大量难以理解的编译器错误。但是,我不关心分配器、迭代器等。那么,为什么我不花几个小时来实现一个易于使用的链表类呢?

我想从 StackOverflow 社区知道的是:对于 C++ 中的大多数现有功能,“自行开发”有哪些危险、可能的缺点和可能的优点?

编辑:我觉得人们误解了我的这个想法。我的想法是了解我是否可以实现一个非常小的 STL 功能集,并大大简化了 - 更多的是作为一个项目来教我数据结构等。我不建议从头开始重新发明整个轮子,只是我需要和想要了解的部分。我想我想弄清楚的是,使用 STL 的复杂性是否需要创建更小、更简单的版本。

重复使用 boost 或类似的。

我编写的大部分代码都是为大学编写的,我们不允许使用外部库。所以它要么是 C++ 标准库,要么是我自己的类。

这个问题的客观性。

这个问题不是主观的。它也不应该是社区 Wiki,因为它不是民意调查。我想要具体的论点来突出我的方法可能出现的一个优点或一个缺点。与流行的看法相反,这不是意见,而是基于经验或良好的逻辑论证。

格式。

请每个答案只发布一个缺点或一个优点。这将允许人们一次评估单个想法而不是您的所有想法。

请...

没有宗教战争。我不是任何语言的粉丝。我使用任何适用的。对于图形和数据压缩(我目前正在做的工作),这似乎是 C++。请限制您对该问题的回答,否则他们将被否决。

4

20 回答 20

76

那么,为什么我不实现一个不太通用但更易于使用的版本呢?

因为你做不到。因为不管你对 C++ 说什么,它都不是一门简单的语言,如果你还不是很擅长它,那么你的链表实现就会出错。

老实说,您的选择很简单:

学习 C++,或者不要使用它。是的,C++ 通常用于图形,但 Java 也有 OpenGL 库。C#、Python 和几乎所有其他语言也是如此。或 C。您不必使用 C++。

但是,如果您确实使用它,请学习并正确使用它。

如果您想要不可变的字符串,请将您的字符串创建为 const。

无论其底层实现如何,STL 都非常易于使用。

可以读取C++ 编译器错误,但需要一些练习。但更重要的是,它们并不是 STL 代码独有的。无论您做什么,无论您使用哪些库,您都会遇到它们。所以习惯他们。如果你已经习惯了它们,你也可以使用 STL。

除此之外,还有一些其他的缺点:

  • 没有其他人会理解您的代码。如果您在 SO 上询问有关 std::vector 或双向迭代器的问题,那么熟悉 c++ 的每个人都可以回答。如果您询问 My::CustomLinkedList,没有人可以帮助您。这是不幸的,因为滚动你自己也意味着会有更多的错误需要寻求帮助。
  • 你试图治愈症状,而不是原因。问题是你不懂C++。STL 只是其中的一个症状。避免 STL 不会神奇地使您的 C++ 代码更好地工作。
  • 编译器错误。是的,它们读起来很讨厌,但它们就在那里。STL 中的大量工作已用于确保在大多数情况下错误使用会触发编译器错误。在 C++ 中,很容易编写可以编译但不起作用的代码。或者似乎有效。或者在我的电脑上工作,但在其他地方神秘地失败了。您自己的链表几乎肯定会将更多错误转移到运行时,它们会在一段时间内未被发现,并且更难追踪。
  • 再一次,这将是错误的。相信我。我见过该死的优秀 C++ 程序员在 C++ 中编写一个链表,只是为了在隐蔽的边界情况下发现一个又一个的错误。而 C++ 都是边界情况。您的链表会正确处理异常安全吗?如果创建新节点(并因此调用对象类型的构造函数)引发异常,它会保证一切都处于一致状态吗?它不会泄漏内存,会调用所有适当的析构函数吗?它会是类型安全的吗?它会像以前一样高效吗?在用 C++ 编写容器类时,有很多令人头疼的问题需要处理。
  • 您错过了使用任何语言的现有最强大和最灵活的库之一。即使使用 Java 庞大的类库,STL 也可以做很多令人痛苦的事情。C++ 已经够难了,没有必要放弃它提供的一些优势。

我不关心分配器、迭代器等

可以安全地忽略分配器。你几乎不需要知道它们的存在。迭代器虽然很棒,但弄清楚它们会为您省去很多麻烦。要有效地使用 STL,您只需要了解三个概念:

  • 容器:你已经知道这些了。向量、链表、映射、集合、队列等。
  • 迭代器:允许您导航容器(或容器的子集,或任何其他值序列,在内存中,以流的形式在磁盘上,或在运行中计算)的抽象。
  • 算法:适用于任何一对迭代器的常用算法。您有 sort、for_each、find、copy 和许多其他功能。

是的,与 Java 的库相比,STL 很小,但是当您结合上述 3 个概念时,它的强大功能令人惊讶。有一点学习曲线,因为它是一个不寻常的库。但是,如果您要花一两天以上的时间使用 C++,那么值得好好学习。

不,我没有遵循你的答案格式,因为我认为实际上给你一个详细的答案会更有帮助。;)

编辑:

很有可能会说滚动自己的优势是您可以学习更多的语言,甚至可能为什么 STL 是它的可取之处之一。但我并不真正相信这是真的。它可能有效,但也可能适得其反。

正如我上面所说,编写似乎可以工作的 C++ 代码很容易。当它停止工作时,很容易重新安排一些事情,比如变量的声明顺序,或者在类中插入一些填充,使其看起来再次工作。你会从中学到什么?那会教你如何编写更好的 C++ 吗?也许。但最有可能的是,它只会告诉你“C++ 很烂”。它会教你如何使用 STL 吗?当然不。一种更有用的方法可能是利用 StackOverflow 的强大功能以正确的方式学习 STL。:)

于 2009-03-12T11:40:12.017 回答
44

缺点:没有人,但你会使用它。

优点:在实现它的过程中,您将了解为什么标准库是一件好事。

于 2009-03-12T10:21:15.387 回答
21

优点:吃自己的狗粮。你得到你所做的。

缺点:吃自己的狗粮。许多人,比我们 99% 的人都聪明,已经花费了数年时间来创建 STL。

于 2009-03-12T10:19:54.407 回答
13

我建议你了解原因:

使用 STL 会吐出大量难以理解的编译器错误

第一的

于 2009-03-12T10:22:58.380 回答
8

缺点:你可能会花更多的时间来调试你的类库,而不是解决你面前的任何大学任务。

优势:你可能会学到很多东西!

于 2009-03-12T10:17:50.997 回答
8

对于神秘的编译器 STL 错误消息,您可以做一些事情。STLFilt 将有助于简化它们。来自STLFilt 网站

STLFilt 简化和/或重新格式化冗长的 C++ 错误和警告消息,重点关注与 STL 相关的诊断(对于 MSVC 6,它完全消除了 C4786 警告及其碎片)。结果使许多甚至是最神秘的诊断都可以理解。

看看这里,如果你使用 VisualC,也可以看看这里

于 2009-03-12T10:58:15.393 回答
8

我认为你应该这样做。

我敢肯定我会为此大吃一惊,但你知道,这里的每个 C++ 程序员都喝了太多的 STLcoolaid。

STL 是一个很棒的库,但我从第一手经验中知道,如果你自己动手,你可以:

1) 对于您的特定用例,使其比 STL 更快。2)您将编写一个仅包含所需接口的库。3)您将能够扩展所有标准的东西。(我无法告诉你我多么希望 std::string 有一个 split() 方法)......

当他们说这将是很多工作时,每个人都是对的。确实如此。

但是,你会学到很多东西。即使你写完之后再回到 STL 并且不再使用它,你仍然会学到很多东西。

于 2009-03-12T15:32:52.537 回答
4

我的一点经验:不久前,我实现了自己的类矢量类,因为我需要对其进行良好的控制。

由于我需要通用性,我制作了一个模板化数组。

我还想不使用 operator[] 而是像使用 C 一样递增指针,所以我不计算 T[i] 在每次迭代时的地址......我添加了两个方法一个返回指针到分配的内存和另一个返回指向末尾的指针。要遍历整数数组,我必须编写如下内容:

for(int * p = array.pData(); p != array.pEnd(); ++p){
  cout<<*p<<endl; 
}

然后,当我开始使用向量的向量时,我发现当有可能时,a 可以分配一大块内存,而不是多次调用 new。这时候我在模板类中添加了一个分配器。

直到那时我才注意到我写了一个完全没用的 std::vector<> 克隆。

至少现在我知道我为什么使用 STL ...

于 2009-03-23T20:49:42.320 回答
3

缺点:恕我直言,重新实现经过测试和验证的库是一个漏洞,几乎可以保证它比它的价值更麻烦。

于 2009-03-12T10:21:12.627 回答
3

另一个缺点

如果你想在大学毕业后找到一份 C++ 工作,大多数想要招聘你的人都会希望你熟悉标准 C++ 库。不一定对实现级别非常熟悉,但肯定熟悉它的用法和习语。如果您以自己的库的形式重新实现轮子,您将错过这个机会。尽管如此,如果你自己动手,你希望能学到很多关于图书馆设计的知识,这可能会根据你面试的地点为你赢得一些额外的布朗尼积分。

于 2009-03-12T10:49:01.653 回答
3

坏处:

您正在引入对您自己的新库的依赖。即使这已经足够了,并且您的实现工作正常,您仍然有依赖关系。这可能会在代码维护方面给您带来很大的麻烦。其他人(包括您自己,在一年甚至一个月内)都不会熟悉您独特的字符串行为、特殊迭代器等。在您开始重构/扩展任何东西之前,需要付出很多努力才能适应新环境。如果您使用 STL 之类的东西,那么每个人都会知道它,它很容易理解并记录在案,没有人需要重新学习您的自定义一次性环境。

于 2009-03-12T15:21:23.407 回答
3

您可能对EASTL感兴趣,这是对 STL Electronic Arts 的重写,不久前记录在案。他们的设计决策主要是由多平台视频游戏编程中的特定愿望/需求驱动的。链接文章中的摘要很好地总结了它。

于 2009-03-23T20:24:53.097 回答
1

缺点:你的大学课程可能是这样布置的。事实上,你已经被它激怒了(讽刺不是故意的),这可能表明你没有得到范式,当你有范式转变时会受益很多。

于 2009-03-12T10:24:35.800 回答
1

优势

如果您研究 MFC,您会发现您的建议已经在生产代码中使用 - 并且已经使用了很长时间。MFC 的集合类都没有使用 STL。

于 2009-03-12T10:29:46.630 回答
1

为什么不看看现有的 C++ 库。在 C++ 还没有那么成熟的时候,人们经常编写自己的库。看看 Symbian(虽然很可怕),Qt 和 WxWidgets(如果我没记错的话)有基本的集合和东西,可能还有很多其他的。

我的观点是,STL 的复杂性源于 C++ 语言的复杂性,您几乎无法改进 STL(除了使用更合理的命名约定)。如果可以的话,我建议简单地切换到其他语言,或者只是处理它。

于 2009-03-12T10:38:41.447 回答
1

缺点:重新实现所有这些(即以高质量水平)肯定需要许多优秀的开发人员几年的时间。

于 2009-03-12T11:30:00.667 回答
1

例如,使用 STL 会吐出大量难以理解的编译器错误

其原因本质上是 C++ 模板。如果您使用模板(如 STL 那样),您将收到大量难以理解的错误消息。因此,如果您实现自己的基于模板的集合类,您将不会处于更好的位置。

您可以制作非基于模板的容器并将所有内容存储为 void 指针或某些基类,例如但是您将失去编译时类型检查,并且 C++ 作为一种动态语言很糟糕。这样做不像在 Objective-C、Python 或 Java 中那样安全。原因之一是 C++ 没有针对所有类的根类来对所有对象进行所有自省以及在运行时进行一些基本错误处理。相反,如果您对类型有误,您的应用程序可能会崩溃并烧毁,并且您不会得到任何错误的线索。

于 2009-03-25T21:19:27.817 回答
0

对于 C++ 中的大多数现有功能,“自行开发”有哪些危险、可能的缺点和可能的优点?

你能负担得起并可能证明重新发明轮子所花费的精力/时间/金钱吗?

重复使用 boost 或类似的。

很奇怪,您不能使用 Boost。IIRC,大部分贡献来自与大学相关/在大学工作的人(想想 Jakko Jarvi)。使用 Boost 的好处太多了,这里就不一一列举了。

关于不“重新发明轮子”

缺点:虽然你学到了很多东西,但当你想到你真正的项目目标是什么时,你也会让自己倒退。

优点:对于要继承它的人来说,维护更容易。

于 2009-03-12T10:18:59.777 回答
0

STL 非常复杂,因为它需要用于通用库。

STL 是这样的原因:

  • 基于交互器,因此标准算法只需要针对不同类型的容器的单一实现。
  • 设计为在面对异常时行为正确。
  • 设计为在多线程应用程序中是“线程”安全的。

但是,在许多应用程序中,您确实拥有以下内容:

  • 字符串类
  • O(1) 查找的哈希表
  • 带有排序/和二进制搜索的向量/数组排序集合

如果你知道:

  • 您的课程不会在构造或作业上抛出异常。
  • 您的代码是单线程的。
  • 您不会使用更复杂的 STL 算法。

然后您可能可以编写自己的更快的代码,使用更少的内存并产生更简单的编译/运行时错误。

没有 STL 的更快/更容易的一些示例:

  • Copy-on-Write 字符串,带有引用计数的字符串缓冲区。(不要在多线程环境中执行此操作,因为您需要锁定引用计数访问。)
  • 使用良好的哈希表而不是 std::set 和 std::map。
  • 可以作为单个对象传递的“Java”样式迭代器
  • 不需要知道容器类型的迭代器类型(为了更好的编译时代码解耦)
  • 具有更多实用功能的字符串类
  • 矢量容器中的可配置边界检查。(所以不是 [] 或 .at 而是具有编译或运行时标志的相同方法,用于从“安全”模式转到“快速”模式)
  • 容器设计为使用指向将删除其内容的对象的指针。
于 2009-03-12T15:12:41.270 回答
0

看起来您更新了问题,所以现在确实有两个问题:

  1. 如果我认为 std:: 库对我的需要来说太复杂了,我该怎么办?

设计您自己的类,在内部使用相关的 std:: 库功能为您完成“繁重的工作”。这样你就不会出错,而且你仍然可以发明自己的编码接口。

  1. 如果我想学习数据结构是如何工作的,我应该怎么做?

从头开始设计您自己的一组数据结构类。然后试着弄清楚为什么标准的更好。

于 2009-03-23T20:15:24.617 回答