C++ STL向量与现实世界中的数组

GRa*_*rdB 21 c++ arrays vector c++-standard-library

我是C++的新手.我正在阅读迈克尔道森的"通过游戏编程开始C++".但是,我对编程并不陌生.我刚刚完成了一个处理向量的章节,所以我对它们在现实世界中的使用有疑问(我是一名计算机科学专业的学生,​​所以我还没有太多真实的经验).

作者在每章末尾都有一个Q/A,其中一个是:

问:我应该何时使用向量而不是数组?

答:差不多总是如此.向量是高效和灵活的.它们确实比阵列需要更多的内存,但这种权衡几乎总是值得的.

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

我不需要被告知两个数据结构之间的差异; 我非常了解他们.我想知道的是,如果作者在他的Q/A中给出了很好的建议,或者他只是想让初学者程序员免于破坏管理固定大小数据结构的复杂性.另外,不管你怎么想作者的意见,你怎么在现实世界往往?

谢谢,

杰拉德

Ton*_*roy 25

答:几乎总是[使用向量而不是数组].向量是高效和灵活的.它们确实比阵列需要更多的内存,但这种权衡几乎总是值得的.

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

  • 的元素是在编译时指定,例如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变量的其他代码尝试使用该数据之前完成构造:最终需要某种方式Singleton(可能是线程安全的,甚至更慢)

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

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

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

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

  • 数组便于从多个进程访问共享内存(默认情况下,vector指向实际动态分配数据的内部指针不在共享内存中或跨进程有意义,强制C++ 03 vector使用共享内存是很有名的像这样,即使指定自定义分配器模板参数).

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

也就是说,如果使用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.


Dan*_*Dan 19

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

当异常未处理时,被调用的唯一事物是堆栈中对象的析构函数.如果在对象外部动态分配内存,并且在删除之前将某个未捕获的异常抛出,则会发生内存泄漏.

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

您还应该检查std::algorithm,它提供了许多常见的算法vector和其他STL容器.

我有几次编写代码vector,回想起来,使用本机数组可能会更好.但在所有这些情况下,a Boost::multi_array或a Blitz::Array都会比其中任何一种都好.

  • `int a [5];`是完美的异常安全.你确定OP正在讨论*动态分配的*数组吗? (9认同)

Nic*_*las 6

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

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


Zac*_*aus 5

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

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

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

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

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

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