在我们的c++课程中,他们建议不要再在新项目中使用c++数组。据我所知,Stroustroup本人建议不要使用数组。但是否存在显著的性能差异?
当前回答
如果不需要动态调整大小,则会有存储容量的内存开销(一个指针/size_t)。就是这样。
其他回答
应该避免使用带有new的c++数组(即使用动态数组)。这里有一个问题,你必须跟踪大小,你需要手动删除它们,做各种各样的家务。
在堆栈上使用数组也是不鼓励的,因为您没有范围检查,并且传递数组将丢失关于其大小的任何信息(数组到指针的转换)。在这种情况下,应该使用std::array,它将c++数组包装在一个小类中,并提供一个size函数和迭代器来迭代它。
现在,std::vector vs.原生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.
#include <vector>
struct S
{
int padding;
std::vector<int> v;
int * p;
std::vector<int>::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在构造时将所有元素初始化为默认值(例如int为0)(感谢@bernie提醒我)。
当你想要一个未初始化的缓冲区(例如用作memcpy()的目标)时,使用std::vector与使用raw数组肯定会有性能影响。vector将使用默认构造函数初始化其所有元素。原始数组则不会。
c++规范中std:vector构造函数接受count参数(这是第三种形式):
从各种数据源构造一个新容器,可选地使用用户提供的分配器alloc。
使用默认插入的t的count个实例构造容器。
复杂性
2-3)计数线性
原始数组不会产生这种初始化代价。
注意,使用自定义分配器,可以避免vector元素的“初始化”(即使用默认初始化而不是值初始化)。请看这些问题了解更多细节:
这是c++ 11和Boost下vector::resize(size_type n)的行为吗?容器正确吗? 如何避免std::vector<>来初始化它的所有元素?
STL is a heavily optimized library. In fact, it's even suggested to use STL in games where high performance might be needed. Arrays are too error prone to be used in day to day tasks. Today's compilers are also very smart and can really produce excellent code with STL. If you know what you are doing, STL can usually provide the necessary performance. For example by initializing vectors to required size (if you know from start), you can basically achieve the array performance. However, there might be cases where you still need arrays. When interfacing with low level code (i.e. assembly) or old libraries that require arrays, you might not be able to use vectors.
为微优化人员准备的序言
记住:
程序员浪费了大量的时间去思考或担心程序中非关键部分的速度,而当考虑到调试和维护时,这些提高效率的尝试实际上会产生强烈的负面影响。我们应该忘记小的效率,大约97%的时候:过早的优化是万恶之源。但我们不应该错过这关键的3%的机会。”
(感谢metamorphosis的完整引用)
不要使用C数组来代替向量(或任何东西),因为你认为它更快,因为它应该是低级别的。你可能错了。
使用默认的向量(或者根据需要使用安全的容器),然后如果你的分析器说这是一个问题,看看你是否可以优化它,要么使用更好的算法,要么改变容器。
话虽如此,我们可以回到最初的问题。
静态/动态数组?
c++数组类比低级C数组表现得更好,因为它们了解自己很多东西,并且可以回答C数组不能回答的问题。他们能够自己打扫卫生。更重要的是,它们通常是使用模板和/或内联编写的,这意味着在调试中出现的大量代码在发布版本中分解为很少或没有代码,这意味着它们与内置的不太安全的竞争没有区别。
总而言之,它分为两类:
动态数组
使用指向malloc-ed/new-ed数组的指针最多和std::vector版本一样快,但安全性要低得多(参见litb的帖子)。
所以使用std::vector。
静态数组
最好使用静态数组:
和std::array版本一样快 更不安全。
所以使用std::array。
未初始化的内存
有时,使用一个向量而不是一个原始缓冲区招致一个可见的成本,因为向量将在构造时初始化缓冲区,而它所取代的代码没有,正如bernie by在他的回答中所说。
如果是这种情况,那么您可以通过使用unique_ptr而不是vector来处理它,或者,如果这种情况在您的代码线中不是例外,实际上编写一个类buffer_owner,它将拥有该内存,并让您轻松安全地访问它,包括调整它的大小(使用realloc?)或任何您需要的奖励。
在c++ 11中使用普通数组的理由就更少了。
从最快到最慢,本质上有3种类型的数组,这取决于它们所具有的特性(当然,实现的质量可以使事情变得非常快,即使是列表中的情况3):
静态的,在编译时大小已知。——std::array<T, N> 动态的,运行时大小已知,从不调整大小。这里的典型优化是,如果数组可以直接分配到堆栈中。——不可用。也许在c++ 14之后,在c++ TS中使用dynarray。在C中有vla 动态的,可在运行时调整大小。——std::向量T > <
为1。常量静态数组,在c++ 11中使用std::array<T, N>。
为2。在运行时指定固定大小的数组,但这不会改变它们的大小,在c++ 14中有讨论,但它已经转移到技术规范,最终由c++ 14制成。
为3。std::vector<T>通常会在堆中请求内存。这可能会影响性能,不过可以使用std::vector<T, MyAlloc<T>>来使用自定义分配器改善这种情况。相对于T mytype[] = new mytype[n];你可以调整它的大小它不会像普通数组那样衰减为指针。
使用上面提到的标准库类型来避免数组退化为指针。如果使用相同的特性集,将节省调试时间,并且性能与普通数组完全相同。