何时复制矢量,何时传递参考?

MrW*_*yne 7 c++ stl stdvector

我正在使用一个充分利用它的程序std::vector.还有很多分配/解除分配,数十亿分配,我试图避免尽可能多的分配/解除分配.由于我对C++比较陌生,所以我在使用向量时会有一些关于分配的问题(例如,在向其添加元素时).我在Win7 64位机器上,程序是32位,我正在使用当前版本的MinGW编译器.

我想知道,在下列情况下会发生什么,即如果复制了矢量,作为参考传递,...

1.

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

std::vector<T> myVector = fillVector();
Run Code Online (Sandbox Code Playgroud)

2.

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

std::vector<T> myVector = fillVector();
Run Code Online (Sandbox Code Playgroud)

3.

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

std::vector<T>* myVector = fillVector();
Run Code Online (Sandbox Code Playgroud)

以下是不同的操作:

4.

std::vector<T> myVector1;
... (myVector1 being filled)
std::vector<T> myVector = myVector1;
Run Code Online (Sandbox Code Playgroud)

5.

std::vector<T>* myVector1 = new std::vector<T>;
... (myVector1 being filled)
std::vector<T> myVector = (*myVector1);
Run Code Online (Sandbox Code Playgroud)

假设我不想改变myFunction/中的参数改为myVectorin myFunction会不会伤害程序的其余部分:

6.

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

std::vector<T> myVector;
... (myVector being filled)
myFunction(myVector);
Run Code Online (Sandbox Code Playgroud)

7.

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

std::vector<T> myVector;
... (myVector being filled)
myFunction(myVector);
Run Code Online (Sandbox Code Playgroud)

如果我的理解是正确的,最快的选择(意味着传递参考而不是创建副本并传递它们)将是2/3,5和7.如果我错了,请纠正我!

And*_*owl 7

1.

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

std::vector<T> myVector = fillVector();
Run Code Online (Sandbox Code Playgroud)

这可以.它vector按值返回,但大多数编译器(在命名的)返回值优化下都会忽略对复制构造函数的调用(至少在启用优化时).

此外,使用C++ 11,移动语义确保调用移动构造函数而不是复制构造函数,这将简单地窃取返回向量的内容,而不会生成昂贵的副本.

2.

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

std::vector<T> myVector = fillVector();
Run Code Online (Sandbox Code Playgroud)

不要这样做.动态分配的不必要的开销,加上必须记住您必须释放返回的对象的负担.避免手动内存管理,更喜欢1.

3.

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

std::vector<T>* myVector = fillVector();
Run Code Online (Sandbox Code Playgroud)

与上述相同.避免手动内存管理.

4.

std::vector<T> myVector1;
... (myVector1 being filled)
std::vector<T> myVector = myVector1;
Run Code Online (Sandbox Code Playgroud)

这是一个概念上不同的操作.在这里你创建一个副本,看起来你做得对.在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);
Run Code Online (Sandbox Code Playgroud)

与上面相同,您想要创建一个副本,但是您不必要地动态分配向量.这将再次迫使您手动处理它的生命周期,这很糟糕且容易出错.不要这样做.

6.

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

std::vector<T> myVector;
... (myVector being filled)
myFunction(myVector);
Run Code Online (Sandbox Code Playgroud)

在这里,你正在经历myVector价值.这是否可以优化取决于myFunction它的论点是什么:它会改变它吗?如果是这样,您是否希望在从函数返回后可以看到这些更改?如果是,则按值传递是正确的,除非您想要获取myVector对象,否则无法对其进行优化:在这种情况下,在C++ 11中,您可以在其传递给函数时将其移动.这将避免昂贵的,不必要的副本.

7.

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

std::vector<T> myVector;
... (myVector being filled)
myFunction(myVector);
Run Code Online (Sandbox Code Playgroud)

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


Jos*_*eld 1

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

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