23

我是 C++ 新手。我正在阅读 Michael Dawson 的“通过游戏编程开始 C++”。但是,我一般对编程并不陌生。我刚刚完成了处理向量的一章,所以我有一个关于它们在现实世界中的使用的问题(我是一名计算机科学专业的学生,​​所以我还没有太多的实际经验)。

作者在每章末尾都有一个问答,其中一个是:

问:什么时候应该使用向量而不是数组?

答:几乎总是。向量高效且灵活。它们确实需要比数组多一点的内存,但这种权衡几乎总是物有所值。

你们有什么感想?我记得在一本 Java 书籍中学习过向量,但在我的 Comp 简介中根本没有涉及它们。科学。课,也不是我在大学的数据结构课。我也从未见过它们用于任何编程任务(Java 和 C)。这让我觉得它们没有被太多使用,尽管我知道学校代码和现实世界的代码可能会有很大的不同。

我不需要被告知两种数据结构之间的差异。我很了解他们。我只想知道作者是否在他的 Q/A 中给出了很好的建议,或者他是否只是想避免初学者程序员因管理固定大小的数据结构的复杂性而自毁。此外,无论您如何看待作者的建议,您在现实世界中更经常看到什么?

4

7 回答 7

26

A:几乎总是[使用向量而不是数组]。向量高效且灵活。它们确实需要比数组多一点的内存,但这种权衡几乎总是物有所值。

那是过于简单化了。使用数组相当普遍,并且在以下情况下可能很有吸引力:

  • 元素在编译时指定,例如const char project[] = "Super Server";const Colours colours[] = { Green, Yellow };

    • std::vector使用 C++11用值初始化 s将同样简洁

  • 元素的数量本质上是固定的,例如const char* const bool_to_str[] = { "false", "true" };Piece chess_board[8][8];

  • 首次使用性能很关键:使用常量数组,编译器通常可以将完全预初始化的对象的内存快照写入可执行映像,然后直接将页面错误放置到位以供使用,因此通常更快运行时堆分配(new[]),然后是对象的序列化构造

    • 编译器生成的const数据表总是可以被多个线程安全读取,而在运行时构造的数据必须在非函数局部static变量的构造函数触发的其他代码尝试使用该数据之前完成构造:你最终需要某种方式单例(可能是线程安全的,它会更慢)

    • 在 C++03 中,vector使用初始大小创建的 s 将构造一个原型元素对象,然后复制构造每个数据成员。这意味着即使对于故意将构造保留为无操作的类型,复制数据元素仍然需要付出代价——复制它们在内存中留下的任何垃圾值。显然,一组未初始化的元素更快。

  • C++ 的一个强大特性是,您通常可以编写一个class(或struct)来精确模拟特定协议所需的内存布局,然后将类指针指向您需要使用的内存,以便方便地解释或分配值。无论好坏,许多此类协议通常嵌入固定大小的小型数组。

  • 有一个几十年前的 hack 将一个包含 1 个元素的数组(如果你的编译器允许它作为扩展,甚至是 0)放在结构/类的末尾,将指向结构类型的指针指向一些更大的数据区域,并访问基于内存可用性和内容的先验知识(如果在写入之前读取),结构末尾的数组元素 - 请参阅零元素数组的需要是什么?

  • 包含数组的类/结构仍然可以是 POD 类型

  • 数组有助于从多个进程访问共享内存(默认情况下vector,指向实际动态分配数据的内部指针不会在共享内存中或跨进程有意义,并且众所周知,强制 C++03vector使用共享内存是很困难的即使在指定自定义分配器模板参数时也是如此)。

  • 嵌入数组可以本地化内存访问需求,提高缓存命中率,从而提高性能

也就是说,如果使用 a vector(在代码简洁、可读性或性能方面)不是一个积极的痛苦,那么你最好这样做:他们已经size()检查了随机访问at(),迭代器,调整大小(这通常成为应用程序所必需的) “成熟”)等。如果vector需要,从其他标准容器更改通常也更容易,并且更安全/更容易应用标准算法(x.end()x + sizeof x / sizeof x[0]任何一天都好)。

更新:C++11 引入了 a std::array<>,它避免了 s 的一些成本vector——在内部使用固定大小的数组来避免额外的堆分配/释放——同时提供一些好处和 API 特性:http://en。 cppreference.com/w/cpp/container/array

于 2011-06-24T07:21:17.220 回答
20

使用 avector而不是数组的最佳理由之一是RAII习惯用法。基本上,为了使 c++ 代码是异常安全的,任何动态分配的内存或其他资源都应该封装在对象中。这些对象应该有释放这些资源的析构函数。

当异常未处理时,唯一需要调用的是堆栈上对象的析构函数。如果您在对象外部动态分配内存,并且在删除之前在某处抛出未捕获的异常,则您有内存泄漏。

这也是避免必须记住使用delete.

您还应该查看,它为其他 STL 容器std::algorithm提供了许多常用算法。vector

回想起来,我曾几次用vector它编写代码,使用本机数组可能会更好。但在所有这些情况下,aBoost::multi_array或 aBlitz::Array都会比它们中的任何一个都好。

于 2011-06-24T03:37:30.407 回答
6

std::vector 只是一个可调整大小的数组。仅此而已。这不是您在数据结构课程中学到的东西,因为它不是智能数据结构。

在现实世界中,我看到了很多数组。但我也看到许多使用“C with Classes”风格的 C++ 编程的遗留代码库。这并不意味着你应该那样编程。

于 2011-06-24T03:34:46.133 回答
5

我将在这里发表我的观点,以对科学和工程中使用的大型数组/向量进行编码。

在这种情况下,基于指针的数组可能会快很多,尤其是对于标准类型。但是指针增加了可能的内存泄漏的危险。这些内存泄漏会导致更长的调试周期。此外,如果您想让基于指针的数组动态化,您必须手动编写代码。

另一方面,标准类型的向量较慢。只要您不在 stl 向量中存储动态分配的指针,它们也是动态和内存安全的。

在科学和工程中,选择取决于项目。速度与调试时间有多重要?例如,模拟软件 LAAMPS 使用通过其内存管理类处理的原始指针。速度是这个软件的首要任务。我正在构建的软件,我必须平衡速度、内存占用和调试时间。我真的不想花很多时间调试,所以我使用的是 STL 向量。

我想为这个答案添加更多信息,这些信息是我从对大规模阵列的广泛测试和大量阅读网络中发现的。因此,stl 向量和大型数组(一百万以上)的另一个问题是如何为这些数组分配内存。Stl 向量使用 std::allocator 类来处理内存。此类是基于池的内存分配器。在小规模加载下,基于池的分配在速度和内存使用方面非常有效。随着向量的大小达到数百万,基于池的策略成为内存占用。发生这种情况是因为池的趋势是始终拥有比 stl 向量当前使用的空间更多的空间。

对于大规模向量,您最好编写自己的向量类或使用指针(原始或某种来自 boost 或 c++ 库的内存管理系统)。这两种方法都有优点和缺点。选择实际上取决于您要解决的确切问题(此处添加的变量太多)。如果您确实编写了自己的向量类,请确保允许向量以一种简单的方式清除其内存。目前对于 Stl 向量,您需要使用交换操作来做一些真正应该首先内置到类中的事情。

于 2014-03-09T22:36:13.293 回答
4

经验法则:如果您事先不知道元素的数量,或者如果预计元素的数量会很大(例如,超过 10 个),请使用 vector。否则,您也可以使用数组。例如,我编写了很多几何处理代码,并将一条线定义为 2 个坐标的 ARRAY。一条线由两个点定义,并且总是由两个点定义。使用向量而不是数组在许多方面都过分了,在性能方面也是如此。

另一件事:当我说“数组”时,我真的是指数组:使用数组语法声明的变量,例如int evenOddCount[2];如果您考虑在向量和动态分配的内存块之间进行选择,例如int *evenOddCount = new int[2];,答案很明确:使用向量!

于 2011-06-24T07:28:33.467 回答
3

在现实世界中,这是一种罕见的情况,您需要处理已知大小的固定集合。在几乎所有情况下,您将在程序中容纳多大的数据集都存在一定程度的未知性。事实上,一个好的程序的标志是它可以适应各种可能的场景。

以这些(微不足道的)场景为例:

  • 您已经实现了一个视图控制器来跟踪 FPS 中的 AI 战斗人员。游戏逻辑每隔几秒就会在各个区域生成随机数量的战斗人员。玩家正在以只有在运行时才能知道的速度击倒 AI 战斗人员。
  • 一位律师访问了他所在州的市法院网站,并正在查询一夜之间新的酒驾案件数量。他选择通过一组变量过滤列表,这些变量包括事故发生时间、邮政编码和逮捕人员。
  • 操作系统需要维护其上运行的各种程序正在使用的内存地址列表。程序的数量及其内存使用情况以不可预知的方式发生变化。

在任何这些情况下,可以提出一个很好的论点,即可变大小的列表(适应动态插入和删除)将比简单数组执行得更好。主要好处是减少了在固定数组中添加或删除元素时为固定数组分配/释放内存空间的需求。

于 2011-06-24T03:39:08.097 回答
0

As far as arrays are considered, simple integer or string arrays are very easy to use. On the other hand, for common functions like searching,sorting,insertion,removal, you can achieve much faster speed using standard algorithms (built in library functions) on vectors. Specially if you are using vectors of objects. Secondly there is this huge difference that vectors can grow in size dynamically as more objects are inserted. Hope that helps.

于 2015-12-23T05:33:53.340 回答