C++ std::vector vs 现实世界中的数组

C++ std::vector vs array in the real world

本文关键字:世界 数组 std vector vs C++      更新时间:2023-10-16

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

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

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

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

你们怎么看? 我记得在一本Java书中学习过向量,但我们在我的Comp.Sci.class入门和大学的数据结构课上都没有涉及它们。 我也从未见过它们在任何编程任务(Java和C(中使用过。 这让我觉得它们没有被大量使用,尽管我知道学校代码和现实世界的代码可能非常不同。

我不需要被告知两种数据结构之间的差异;我非常了解他们。 我想知道的是,作者是否在他的问答中给出了很好的建议,或者他是否只是想让初学者程序员免于因管理固定大小的数据结构的复杂性而破坏自己。 另外,不管你对作者的建议有什么看法,你在现实世界中更经常看到什么?

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

这是一种过度简化。 使用数组是相当普遍的,并且在以下情况下可能很有吸引力:

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

    • 使用 C++11 时,用值

      初始化 std::vector S 同样简洁

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

  • 首次使用性能至关重要:使用常量数组,编译器通常可以将完全预初始化对象的内存快照写入可执行映像中,然后将其直接错误地写入位置以备使用,因此运行时堆分配(new[](通常要快得多,然后是对象的序列化构造

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

    • 在 C++03 中,使用初始大小创建vector将构造一个原型元素对象,然后复制构造每个数据成员。 这意味着,即使对于故意将构造保留为非操作的类型,复制数据元素仍然需要成本 - 复制其内存中剩余的垃圾值。 显然,未初始化元素的数组更快。

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

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

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

  • 数组便于从多个进程访问共享内存(默认情况下vector指向实际动态分配数据的内部指针不会在共享内存中或在进程之间有意义,并且即使指定自定义分配器模板参数,强制 C++03 vector s 像这样使用共享内存也是出了名的困难(。

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

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

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

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

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

这也是避免记住使用的好方法 delete .

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

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

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

在现实世界中,我看到很多数组。但我也看到很多遗留代码库使用"C with Classes"风格的C++编程。这并不意味着你应该以这种方式编程。

我将在这里提出我的观点,用于编码科学和工程中使用的大型数组/向量。

在这种情况下,基于指针的数组可以快得多,特别是对于标准类型。但是指针增加了可能的内存泄漏的危险。这些内存泄漏可能会导致更长的调试周期。此外,如果要使基于指针的数组动态化,则必须手动对其进行编码。

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

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

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

对于大规模向量,

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

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

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

现实世界中,处理已知大小的固定集合的情况很少见。在几乎所有情况下,在程序中容纳的数据集大小方面都存在一定程度的未知数。事实上,一个好的程序的标志是它可以适应各种可能的情况。

以这些(琐碎的(场景为例:

  • 您已实现视图控制器跟踪AI战斗人员a 转数快。游戏逻辑产生随机各地区的战斗人员人数每隔几秒钟。玩家正在以一定的速度击落AI战斗人员仅在运行时知道。
  • 一位律师已经进入市政法院网站在他的州,是查询新酒驾案件数量那是一夜之间进来的。他选择按集筛选列表变量,包括时间发生事故,邮政编码,以及逮捕官员。
  • 操作系统需要维护内存地址列表被各种程序使用在上面运行。程序数量及其内存使用情况的变化不可预测的方式。

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

数组而言,简单的整数或字符串数组非常易于使用。另一方面,对于搜索、排序、插入、删除等常见功能,您可以在向量上使用标准算法(内置库函数(实现更快的速度。特别是如果您使用的是对象的向量。其次,存在一个巨大的差异,即随着插入更多对象,矢量的大小可以动态增长。希望有帮助。