7

我正在使用一个使用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/changes in 中的参数更改为myVectorinmyFunction不会损害程序的其余部分:

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

4

2 回答 2

7

1.

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

std::vector<T> myVector = fillVector();

这可以。由valuevector返回,但大多数编译器(至少在打开优化时)在(命名的)返回值优化下省略了对复制构造函数的调用。

此外,在 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;

这是概念上不同的操作。在这里,您创建一个副本,看来您做得对。std::vector<T> myVector = std::move(myVector1)在 C++11 中,如果您只需要传输内容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 中,您可以在将其传递给函数时移动它。这将避免昂贵的、不必要的副本。

7.

void myFunction(std::vector<T>& myParam) {
   ...
}

std::vector<T> myVector;
... (myVector being filled)
myFunction(myVector);

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

于 2013-02-27T19:15:59.407 回答
1

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

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

于 2013-02-27T19:10:14.803 回答