When是复制的向量,When是传递的引用

When is a vector copied, when is a reference passed?

本文关键字:When 引用 向量 复制      更新时间:2023-10-16

我正在使用一个大量使用std::vector的程序。还有很多分配/释放正在发生,数十亿,我试图避免尽可能多的。由于我对c++比较陌生,所以在使用vector(例如向其添加元素)时,我有一些关于分配的问题。我在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中的参数/将myFunction中的参数更改为myVector不会影响程序的其余部分:

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。如果我说错了,请纠正我!

1.

std::vector<T> fillVector() {
    std::vector<T> returnVector;
    ...
    return returnVector;
}
std::vector<T> myVector = fillVector();

这很好。vector通过值返回,但是大多数编译器(至少在打开优化时)在(命名的)返回值优化下省略了对复制构造函数的调用。

此外,在c++ 11中,move语义确保调用move构造函数而不是复制构造函数,后者只会窃取返回向量的核心,而不会生成代价高昂的副本。

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中,如果你只需要传输 myVector1的内容,而不是复制它,你可能想使用std::vector<T> myVector = std::move(myVector1)

5 .

std::vector<T>* myVector1 = new std::vector<T>;
... (myVector1 being filled)
std::vector<T> myVector = (*myVector1);
和上面一样,你想要创建一个拷贝,但是你没有必要动态地分配vector。这将再次迫使您手动处理它的生命周期,这很糟糕,而且容易出错。不要这样做。

6。

void myFunction(std::vector<T> myParam) {
   ...
}
std::vector<T> myVector;
... (myVector being filled)
myFunction(myVector);

在这里你通过值传递myVector。这是否可以优化取决于myFunction应该如何处理它的参数:它应该改变它吗?如果是这样,您希望从函数返回后这些更改是可见的吗?如果是,按值传递是正确的,并且没有办法优化它,除非您想要获得myVector对象:在这种情况下,在c++ 11中,您可以在将传递给函数时移动。这将避免昂贵的、不必要的复制。

7。

void myFunction(std::vector<T>& myParam) {
   ...
}
std::vector<T> myVector;
... (myVector being filled)
myFunction(myVector);

这将通过引用传递,只要在从函数返回后可以看到myFunctionmyVector的副作用就可以。一般来说,不能告诉它是否正确,这取决于应用程序的特定逻辑。

最快且最惯用的是选项1。两个副本(从returnVector到返回值和从返回值到myVector)几乎肯定会被编译器省略。拷贝省略是编译器可能进行的一种优化,涉及删除任何不必要的拷贝。在这里,两个副本都是不必要的,std::vector将直接构造代替myVector

实际上,即使在编译器中禁用了复制省略优化,在c++ 11中,两个副本实际上都是移动的。移动std::vector需要几个任务,并且非常快。第一个被特殊规则认为是移动,第二个是移动,因为表达式fillVector()是一个右值表达式。