我正在使用一个充分利用std::vector
的程序。还有很多分配/解除分配,数十亿分配,我试图避免尽可能多的分配/解除分配。
由于我对C ++比较陌生,所以我在使用向量时会有一些关于分配的问题(例如,在向其添加元素时)。
我在Win7 64位机器上,程序是32位,我正在使用当前版本的MinGW编译器。
我想知道,在下列情况下会发生什么,即如果复制了矢量,作为参考传递,......
1
std::vector<T> fillVector() {
std::vector<T> returnVector;
...
return returnVector;
}
std::vector<T> myVector = fillVector();
2
std::vector<T>& fillVector() {
std::vector<T>* returnVector = new std::vector<T>;
...
return (*returnVector);
}
std::vector<T> myVector = fillVector();
3
std::vector<T>* fillVector() {
std::vector<T>* returnVector = new std::vector<T>;
...
return returnVector;
}
std::vector<T>* myVector = fillVector();
以下,不同的操作:
4
std::vector<T> myVector1;
... (myVector1 being filled)
std::vector<T> myVector = myVector1;
5
std::vector<T>* myVector1 = new std::vector<T>;
... (myVector1 being filled)
std::vector<T> myVector = (*myVector1);
假设我不想更改myFunction
中的参数/ myVector
中myFunction
的更改不会影响程序的其余部分:
6
void myFunction(std::vector<T> myParam) {
...
}
std::vector<T> myVector;
... (myVector being filled)
myFunction(myVector);
7
void myFunction(std::vector<T>& myParam) {
...
}
std::vector<T> myVector;
... (myVector being filled)
myFunction(myVector);
如果我的理解是正确的,最快的选择(意味着传递参考而不是创建副本并传递它们)将是2 / 3,5和7.如果我错了,请纠正我!
答案 0 :(得分:7)
1
std::vector<T> fillVector() {
std::vector<T> returnVector;
...
return returnVector;
}
std::vector<T> myVector = fillVector();
这很好。 vector
按值返回,但大多数编译器(至少在启用优化时)在(命名的)返回值优化下删除了对复制构造函数的调用。
此外,使用C ++ 11,移动语义确保调用移动构造函数而不是复制构造函数,这将简单地窃取返回向量的内容,而不会生成昂贵的副本。
2
std::vector<T>& fillVector() {
std::vector<T>* returnVector = new std::vector<T>;
...
return (*returnVector);
}
std::vector<T> myVector = fillVector();
不要这样做。动态分配的不必要的开销,加上必须记住您必须释放返回的对象的负担。避免手动内存管理,更喜欢1。
3
std::vector<T>* fillVector() {
std::vector<T>* returnVector = new std::vector<T>;
...
return returnVector;
}
std::vector<T>* myVector = fillVector();
与上述相同。避免手动内存管理。
4
std::vector<T> myVector1;
... (myVector1 being filled)
std::vector<T> myVector = myVector1;
这是一个概念上不同的操作。在这里你想要创建一个副本,看起来你做得对。在C ++ 11中,如果您只需要传输 std::vector<T> myVector = std::move(myVector1)
的内容而不是复制它,则可能需要使用myVector1
。
5
std::vector<T>* myVector1 = new std::vector<T>;
... (myVector1 being filled)
std::vector<T> myVector = (*myVector1);
与上面相同,您想要来创建副本,但是您不必要地动态分配向量。这将再次迫使您手动处理它的生命周期,这很糟糕且容易出错。不要这样做。
6
void myFunction(std::vector<T> myParam) {
...
}
std::vector<T> myVector;
... (myVector being filled)
myFunction(myVector);
在这里,您按价值传递myVector
。这是否可以优化取决于myFunction
所支持的论点:它会改变它吗?如果是这样,您是否希望在从函数返回后可以看到这些更改?如果是,则传递值是正确的,并且没有办法优化它除非你想要获取myVector
对象:在这种情况下,在C ++ 11中你可以<当它传递给函数时,em>移动它。这将避免昂贵的,不必要的副本。
7
void myFunction(std::vector<T>& myParam) {
...
}
std::vector<T> myVector;
... (myVector being filled)
myFunction(myVector);
这将通过引用传递,只要可以在从函数返回后查看myFunction
在myVector
上的副作用,就可以了。一般不能告诉它是否正确,这取决于你的应用程序的特定逻辑。
答案 1 :(得分:1)
最快和最惯用的是选项1.两个副本(从returnVector
到返回值,从返回值到myVector
)几乎肯定会被编译器删除。复制省略是编译器可能进行的一项优化,涉及删除任何不必要的副本。在这里,两份副本都是不必要的,std::vector
将直接构建myVector
。
事实上,即使您使用编译器禁用复制省略优化,在C ++ 11中,两个副本实际上都会移动。移动std::vector
需要一些分配并且非常快。第一个被认为是特殊规则的移动,第二个是移动,因为表达式fillVector()
是一个右值表达式。