在我们的C++课程中,他们建议不再在新项目中使用C++数组.据我所知,Stroustroup本人建议不要使用数组.但是有显着的性能差异吗?
new
应避免使用C++数组(即使用动态数组).有问题你必须跟踪大小,你需要手动删除它们,并做所有类型的内务管理.
不鼓励在堆栈上使用数组,因为您没有范围检查,并且传递数组将丢失有关其大小的任何信息(数组到指针转换).boost::array
在这种情况下,您应该使用它将C++数组包装在一个小类中,并提供一个size
函数和迭代器来迭代它.
现在std :: vector与原生C++数组(取自互联网):
// Comparison of assembly code generated for basic indexing, dereferencing, // and increment operations on vectors and arrays/pointers. // Assembly code was generated by gcc 4.1.0 invoked with g++ -O3 -S on a // x86_64-suse-linux machine. #includestruct S { int padding; std::vector v; int * p; std::vector ::iterator i; }; int pointer_index (S & s) { return s.p[3]; } // movq 32(%rdi), %rax // movl 12(%rax), %eax // ret int vector_index (S & s) { return s.v[3]; } // movq 8(%rdi), %rax // movl 12(%rax), %eax // ret // Conclusion: Indexing a vector is the same damn thing as indexing a pointer. int pointer_deref (S & s) { return *s.p; } // movq 32(%rdi), %rax // movl (%rax), %eax // ret int iterator_deref (S & s) { return *s.i; } // movq 40(%rdi), %rax // movl (%rax), %eax // ret // Conclusion: Dereferencing a vector iterator is the same damn thing // as dereferencing a pointer. void pointer_increment (S & s) { ++s.p; } // addq $4, 32(%rdi) // ret void iterator_increment (S & s) { ++s.i; } // addq $4, 40(%rdi) // ret // Conclusion: Incrementing a vector iterator is the same damn thing as // incrementing a pointer.
注意:如果您分配使用数组new
和分配的非类对象(如平原int
没有用户定义的构造函数)或类和您不希望您的元素开始初始化,使用new
-allocated阵列可以具有性能优势,因为std::vector
初始化所有元素在构造上的默认值(例如0表示int)(为了记住我而归功于@bernie).
记得:
"程序员浪费了大量的时间来考虑或担心程序中非关键部分的速度,而这些效率尝试实际上在考虑调试和维护时会产生很大的负面影响.我们应该忘记效率低下,比如说97%的时间:过早优化是一切罪恶的根源.但我们不应该放弃我们在那个关键的3%中的机会".
(感谢完全引用的变态)
不要使用C数组而不是向量(或其他)只是因为你认为它更快,因为它应该是更低级别.你错了.
默认情况下使用矢量(或适合您需要的安全容器),然后如果您的探查器说它有问题,请查看您是否可以通过使用更好的算法或更改容器来优化它.
这说,我们可以回到最初的问题.
C++数组类比低级C数组表现得更好,因为它们对自己了解很多,并且可以回答C数组不能解决的问题.他们能够自己清理.更重要的是,它们通常使用模板和/或内联编写,这意味着调试中的许多代码看起来很容易解决在发布版本中产生的很少或没有代码,这意味着它们内置的安全性较低的竞争没有区别.
总而言之,它分为两类:
使用指向malloc-ed/new-ed数组的指针最好与std :: vector版本一样快,并且安全性要低得多(参见litb的帖子).
所以使用std :: vector.
使用静态数组充其量只是:
与std :: array版本一样快
而且安全性要低得多.
所以使用std :: array.
有时,使用vector
而不是原始缓冲区会产生可见的成本,因为vector
它将在构造时初始化缓冲区,而它替换的代码则没有,正如bernie在他的回答中所说的那样.
如果是这种情况,那么你可以使用a unique_ptr
而不是a 来处理它vector
,如果你的代码行中没有例外,实际上写一个buffer_owner
将拥有该内存的类,并让你轻松安全地访问它,包括奖金如调整大小(使用realloc
?),或任何你需要的奖金.
矢量是引擎盖下的阵列.表现是一样的.
在一个可能遇到性能问题的地方,没有正确调整向量的大小.
当向量填充时,它将自行调整大小,这可能意味着一个新的数组分配,接着是n个拷贝构造函数,然后是大约n个析构函数调用,然后是数组删除.
如果你的构造/破坏是昂贵的,那么你最好将矢量设置为正确的大小.
有一种简单的方法可以证明这一点.创建一个简单的类,显示何时构造/销毁/复制/分配.创建这些东西的向量,并开始在向量的后端推送它们.当向量填充时,随着向量调整大小,将会有一系列活动.然后使用大小为预期元素数量的向量再次尝试.你会看到差异.
要回应Mehrdad所说的话:
但是,可能存在您仍需要数组的情况.当与低级代码(即程序集)或需要数组的旧库接口时,您可能无法使用向量.
根本不是真的.如果使用的话,向量会很好地降级为数组/指针:
vectorvector; vector.push_back(42); double *array = &(*vector.begin()); // pass the array to whatever low-level code you have
这适用于所有主要的STL实现.在下一个标准中,它将被要求工作(即使它今天也很好).
在C++ 11中使用普通数组的原因更少.
从最快到最慢,有3种阵列,具体取决于它们具有的功能(当然,即使列表中的情况3,实现的质量也可以使事情变得非常快):
在编译时已知大小的静态.---std::array
动态,在运行时已知大小,从未调整大小.这里的典型优化是,如果可以直接在堆栈中分配数组.- 不可用.也许dynarray
在C++ 14之后的C++ TS中.在C中有VLA
在运行时动态和可调整大小.---std::vector
对于1.具有固定数量的元素纯静态数组,使用std::array
在C++ 11.
对于在运行时指定的2.固定大小的数组,但不会改变它们的大小,在C++ 14中有讨论,但它已经转移到技术规范并最终由C++ 14制成.
对于3. std::vector
通常会在堆中请求内存.这可能会产生性能影响,但您可以使用std::vector
自定义分配器来改善这种情况.T mytype[] = new MyType[n];
与之相比,优势在于您可以调整大小并且不会像普通数组那样衰减到指针.
使用提到的标准库类型来避免 数组衰减到指针.您将节省调试时间和性能完全相同,如果你使用相同的功能集相同,与普通的数组.
跟STL一起去.没有性能损失.这些算法非常有效,它们可以很好地处理我们大多数人都不会想到的细节.
STL是一个高度优化的库.实际上,甚至建议在可能需要高性能的游戏中使用STL.数组太容易出错,无法在日常任务中使用.今天的编译器也很聪明,可以用STL真正生成出色的代码.如果您知道自己在做什么,STL通常可以提供必要的性能.例如,通过将向量初始化为所需大小(如果从开始就知道),您基本上可以实现数组性能.但是,可能存在您仍需要数组的情况.当与低级代码(即程序集)或需要数组的旧库接口时,您可能无法使用向量.
关于杜丽的贡献.
结论是整数数组比整数向量更快(在我的例子中是5倍).但是,对于更复杂/未对齐的数据,数组和向量的速度相同.