C++ std::vector vs array在真实的世界中

ax6ht2ek  于 11个月前  发布在  其他
关注(0)|答案(7)|浏览(134)

我是C新手。我正在阅读Michael Dawson的“Beginning C Through Game Programming”。然而,我对编程并不陌生。我刚刚完成了一章关于向量的内容,所以我有一个关于向量在真实的世界中的使用的问题(我是一个计算机科学的学生,所以我还没有太多的实际经验)。
作者在每一章的结尾都有一个Q/A,其中一个是:
问:什么时候应该使用向量而不是数组?
答:几乎总是如此。向量是高效和灵活的。它们确实需要比数组多一点的内存,但这种权衡几乎总是值得的。
你们觉得怎么样?我记得我在一本Java书中学习过向量,但在我的计算机科学入门课和大学的数据结构课上,我们根本没有涉及到向量。我也从未在任何编程作业(Java和C)中看到过向量。这让我觉得它们并没有被经常使用,尽管我知道学校代码和现实世界的代码可能会有很大的不同。
我不需要被告知这两种数据结构之间的差异;我很清楚这些问题。我想知道的是,作者在他的问答中是否给出了好的建议,或者他是否只是试图保存初学者程序员,以免他们因管理固定大小数据结构的复杂性而毁了自己。此外,不管你怎么看待作者的建议,你在现实世界中经常看到什么?

hc2pp10m

hc2pp10m1#

答:几乎总是[使用向量而不是数组]。向量是高效和灵活的。它们确实需要比数组多一点的内存,但这种权衡几乎总是值得的。
这是一种过度简化。使用数组是相当常见的,并且在以下情况下可能很有吸引力:

  • 在编译时指定元素,例如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将构造一个原型元素对象,然后复制构造每个数据成员。这意味着即使对于那些故意将构造保留为无操作的类型,复制数据元素仍然有成本-复制它们的wherver-garbage-was-left-in-memory值。显然,未初始化元素的数组更快。
  • C++的一个强大功能是,你可以编写一个class(或struct)来精确地模拟特定协议所需的内存布局,然后将一个类指针指向你需要使用的内存,以方便地解释或赋值。
  • 有一个几十年前的技巧,将一个元素数组(如果编译器允许将其作为扩展,甚至可以是0)放在结构体/类的末尾,将指向结构体类型的指针指向一些更大的数据区域,并根据内存可用性和内容的先验知识(如果在写入之前进行阅读)访问结构体末尾的数组元素-参见What's the need of array with zero elements?
  • 包含数组的类/结构仍然可以是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

ryoqjall

ryoqjall2#

使用vector而不是数组的最好理由之一是RAII习惯用法。基本上,为了使c++代码是异常安全的,任何动态分配的内存或其他资源都应该封装在对象中。这些对象应该有析构函数来释放这些资源。
当一个异常未被处理时,唯一需要调用的是堆栈上对象的析构函数。如果你在一个对象之外动态分配内存,并且一个未被捕获的异常在被删除之前被抛出,那么你就有了内存泄漏。
这也是一种避免记住使用delete的好方法。
您还应该查看std::algorithm,它为vector和其他STL容器提供了许多常用算法。
我曾经用vector写过几次代码,回想起来,用原生数组可能会更好,但在所有这些情况下,Boost::multi_arrayBlitz::Array都比它们更好。

fjaof16o

fjaof16o3#

一个std::vector只是一个可调整大小的数组。它并没有比这更多的东西。这不是你在数据结构类中会学到的东西,因为它不是一个智能数据结构。
在真实的世界中,我看到了很多数组,但我也看到了很多使用“C with Classes”风格的C++编程的遗留代码库。

wecizke3

wecizke34#

我将在这里提出我对科学和工程中使用的大型数组/向量编码的看法。
在这种情况下,基于指针的数组可以更快一些,特别是对于标准类型。但是指针增加了可能的内存泄漏的危险。这些内存泄漏可能导致更长的调试周期。另外,如果你想使基于指针的数组动态,你必须手工编写代码。
另一方面,vector对于标准类型来说会慢一些,只要你没有在stl vector中存储动态分配的指针,它们也是动态的和内存安全的。
在科学和工程中,选择取决于项目。速度与调试时间有多重要?例如,LAAMPS是一个模拟软件,它使用通过内存管理类处理的原始指针。速度是这个软件的优先级。我正在构建的软件,我必须平衡速度,内存占用和调试时间。我真的不想花很多时间调试,所以我使用STL向量。
我想在这个答案中添加一些更多的信息,这些信息是我从对大规模数组的广泛测试和大量阅读网络中发现的。(100万+)发生在如何为这些数组分配内存中。Stl vector使用std::用于处理内存的分配器类。该类是一个基于池的内存分配器。在小规模加载下,基于池的分配在速度和内存使用方面非常有效。由于向量的大小当内存达到数百万时,基于池的策略就变成了内存占用。这是因为池总是倾向于保持比stl向量当前使用的更多的空间。
对于大规模的向量,最好编写自己的向量类或使用指针(raw或boost或c++库中的某种内存管理系统)。这两种方法都有优点和缺点。选择哪种方法取决于您要处理的具体问题(这里添加的变量太多)。如果你碰巧写了你自己的vector类,确保vector有一个简单的方法来清除它的内存。目前对于Stl vector,你需要使用swap操作来做一些本来就应该内置在类中的事情。

tyu7yeag

tyu7yeag5#

经验法则:如果你事先不知道元素的数量,或者如果元素的数量预计会很大,(比如10个以上),用vector。否则,你也可以用数组。例如,我写了很多几何处理代码,我将一条线定义为2个坐标的ARRAY。一条线由两个点定义,它总是由两个点定义。使用向量而不是数组在很多方面都是矫枉过正的,性能方面也是如此。
还有一件事:当我说“array”时,我真正的意思是array:一个使用数组语法声明的变量,比如int evenOddCount[2];如果你考虑在vector和动态分配的内存块之间选择,比如int *evenOddCount = new int[2];,答案很清楚:USE VECTOR!

twh00eeo

twh00eeo6#

在真实的世界中,处理已知大小的固定集合是一种罕见的情况。在几乎所有的情况下,在程序中将容纳的数据集的确切大小方面都存在一定程度的未知数。事实上,它可以容纳各种可能的场景,这是一个 * 好 * 程序的标志。
以这些(琐碎的)场景为例:

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

在任何一种情况下,都可以很好地证明可变大小的列表(支持动态插入和删除)比简单的数组性能更好。主要的好处是减少了在添加或删除元素时为固定数组分配/释放内存空间的需求。

ql3eal8s

ql3eal8s7#

就数组而言,简单的整数或字符串数组非常容易使用,另一方面,对于搜索、排序、插入、删除你可以使用标准的算法获得更快的速度(内置库函数)特别是如果你使用的是对象的向量。其次有一个巨大的区别,向量的大小可以随着更多的对象被插入而动态地增长。希望有帮助。

相关问题