我知道向量的大小,这是初始化它的最佳程序?:
选项1
vector<int> vec(3); //in .h
vec.at(0)=var1; //in .cpp
vec.at(1)=var2; //in .cpp
vec.at(2)=var3; //in .cpp
Run Code Online (Sandbox Code Playgroud)
选项2
vector<int> vec; //in .h
vec.reserve(3); //in .cpp
vec.push_back(var1); //in .cpp
vec.push_back(var2); //in .cpp
vec.push_back(var3); //in .cpp
Run Code Online (Sandbox Code Playgroud)
我猜选项2优于1.是吗?其他选择?
Seb*_*ach 45
两种变体都有不同的语义,即你比较苹果和橘子.
第一个给出了n个默认初始化值的向量,第二个变量保留了内存,但没有初始化它们.
选择更适合您需求的东西,即在某种情况下"更好"的东西.
Unc*_*ens 43
"最好"的方式是:
vector<int> vec = {var1, var2, var3};
Run Code Online (Sandbox Code Playgroud)
提供支持C++ 11的编译器.
通过标题或实现文件中的操作不确定您的意思.一个可变的全球对我来说是禁忌.如果它是类成员,则可以在构造函数初始化列表中初始化它.
否则,如果您知道将要使用多少项,则通常会使用选项1,默认值(int为0)将非常有用.在此处
使用at
意味着您无法保证索引有效.像这样的情况令人担忧.即使您能够可靠地检测到问题,但使用起来肯定更简单,push_back
并且不再担心索引正确.
在选项2的情况下,无论您是否保留内存,通常它都会产生零性能差异,因此更容易保留*.除非矢量包含复制非常昂贵的类型(并且不能在C++ 11中提供快速移动),否则矢量的大小将会非常大.
*来自Stroustrups C++风格和技术常见问题解答:
人们有时会担心std :: vector的成本会逐渐增加.我曾经担心这一点,并使用reserve()来优化增长.在测量我的代码并且在实际程序中反复找不到reserve()的性能优势之后,我停止使用它,除非需要避免迭代器失效(在我的代码中很少见).再次:在优化之前进行测量.
虽然您的示例基本相同,但可能是在使用的类型不是int
您的选择时.如果你的类型没有默认构造函数,或者你以后必须重新构造每个元素,我会使用reserve
.只是不要陷入我所做的陷阱并使用reserve
然后operator[]
进行初始化!
std::vector<MyType> myVec(numberOfElementsToStart);
int size = myVec.size();
int capacity = myVec.capacity();
Run Code Online (Sandbox Code Playgroud)
在该第一情况下,使用构造,size
并且numberOfElementsToStart
将等于和capacity
将大于或等于它们.
将myVec视为包含MyType
可以访问和修改的多个项目的向量,push_back(anotherInstanceOfMyType)
将其附加到向量的末尾.
std::vector<MyType> myVec;
myVec.reserve(numberOfElementsToStart);
int size = myVec.size();
int capacity = myVec.capacity();
Run Code Online (Sandbox Code Playgroud)
使用该reserve
函数时,size
将0
直到您向数组添加元素并且capacity
将等于或大于numberOfElementsToStart
.
可以将myVec想象成一个空向量,它可以使用新的项目附加到它上面,至少对第一个元素push_back
没有内存分配numberOfElementsToStart
.
请注意,push_back()
仍需要进行内部检查以确保大小<容量和增加大小,因此您可能需要将其与默认构造的成本进行权衡.
std::vector<MyType> myVec{ var1, var2, var3 };
Run Code Online (Sandbox Code Playgroud)
这是初始化矢量的附加选项,虽然它仅适用于非常小的矢量,但它是一种用已知值初始化小矢量的明确方法.size
将等于您初始化它的元素数量,并且capacity
将等于或大于size.现代编译器可以优化临时对象的创建并防止不必要的复制.
选项2更好,因为reserve只需要保留内存(3*sizeof(T)),而第一个选项为容器内的每个单元调用基类型的构造函数.
对于类C类型,它可能是相同的.
不知何故,一个完全错误的非答案性答案一直被接受,并且在大约7年中被最强烈地接受。这不是一个苹果和桔子的问题。这不是用模糊的陈词滥调来回答的问题。
遵循以下简单规则:
...但这可能不是您最大的担忧。
首先,差异很小。其次,随着我们加快编译器优化的步伐,两者之间的差异变得越来越小。例如,在我的gcc-5.4.0上,运行级别3编译器优化(-O3
)时,差异可以说是微不足道的:
因此,总的来说,我建议您在遇到这种情况时使用方法1。但是,如果您不记得哪一个是最佳的,可能就不值得花时间去寻找。只需选择其中一个并继续前进,因为这不可能导致整个程序整体明显变慢。
通过从正态分布中采样随机向量大小,然后使用两种方法对这些大小的向量进行初始化计时,来运行这些测试。我们保留一个虚拟的sum变量,以确保向量初始化未得到优化,并且我们对向量大小和值进行随机化,以努力避免由于分支预测,缓存和其他此类技巧而导致的任何错误。
main.cpp
:
/*
* Test constructing and filling a vector in two ways: construction with size
* then assignment versus construction of empty vector followed by push_back
* We collect dummy sums to prevent the compiler from optimizing out computation
*/
#include <iostream>
#include <vector>
#include "rng.hpp"
#include "timer.hpp"
const size_t kMinSize = 1000;
const size_t kMaxSize = 100000;
const double kSizeIncrementFactor = 1.2;
const int kNumVecs = 10000;
int main() {
for (size_t mean_size = kMinSize; mean_size <= kMaxSize;
mean_size = static_cast<size_t>(mean_size * kSizeIncrementFactor)) {
// Generate sizes from normal distribution
std::vector<size_t> sizes_vec;
NormalIntRng<size_t> sizes_rng(mean_size, mean_size / 10.0);
for (int i = 0; i < kNumVecs; ++i) {
sizes_vec.push_back(sizes_rng.GenerateValue());
}
Timer timer;
UniformIntRng<int> values_rng(0, 5);
// Method 1: construct with size, then assign
timer.Reset();
int method_1_sum = 0;
for (size_t num_els : sizes_vec) {
std::vector<int> vec(num_els);
for (size_t i = 0; i < num_els; ++i) {
vec[i] = values_rng.GenerateValue();
}
// Compute sum - this part identical for two methods
for (size_t i = 0; i < num_els; ++i) {
method_1_sum += vec[i];
}
}
double method_1_seconds = timer.GetSeconds();
// Method 2: reserve then push_back
timer.Reset();
int method_2_sum = 0;
for (size_t num_els : sizes_vec) {
std::vector<int> vec;
vec.reserve(num_els);
for (size_t i = 0; i < num_els; ++i) {
vec.push_back(values_rng.GenerateValue());
}
// Compute sum - this part identical for two methods
for (size_t i = 0; i < num_els; ++i) {
method_2_sum += vec[i];
}
}
double method_2_seconds = timer.GetSeconds();
// Report results as mean_size, method_1_seconds, method_2_seconds
std::cout << mean_size << ", " << method_1_seconds << ", " << method_2_seconds;
// Do something with the dummy sums that cannot be optimized out
std::cout << ((method_1_sum > method_2_sum) ? "" : " ") << std::endl;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我使用的头文件位于: