虚拟主机 删除网站缓存,做家教在哪个网站,郴州网络推广公司排名,咸宁公司网站建设本文中部分内容来自下面的文章#xff0c;还有一部分来自智谱清言
C 返回值优化_c 局部变量返回优化-CSDN博客
elision:省略
copy elision#xff1a;拷贝省略
RVO (Return Value Optimization)#xff1a;返回值优化
------
我最近也遇到了上面博文中说到的问题还有一部分来自智谱清言
C 返回值优化_c 局部变量返回优化-CSDN博客
elision:省略
copy elision拷贝省略
RVO (Return Value Optimization)返回值优化
------
我最近也遇到了上面博文中说到的问题
观察这段代码
std::vectorint f() {std::vectorint v;// do some thing with vreturn v;
}std::vectorint test f();
------
在C中当返回一个局部对象时会发生以下事情
拷贝返回默认情况下返回局部对象时会通过拷贝构造函数创建返回值的副本。
这意味着在函数返回后局部对象被销毁但是它的副本会被用来初始化调用处的变量。
------
在这个例子中返回局部对象v时会通过拷贝构造函数创建返回值的副本[假设是temp]。
这意味着在函数返回后局部对象 v 被销毁但是它的副本temp会被用来初始化调用处的变量test。
------
移动语义C11及以后版本
如果编译器支持返回值优化RVOReturn Value Optimization或者具名返回值优化NRVONamed Return Value Optimization那么在返回 v 时编译器可能会直接在调用者作用域构造 test从而避免了额外的拷贝。
如果 std::vectorint 支持移动构造函数编译器还可能会使用移动语义这意味着 v 的资源如动态分配的内存会被移动到 test 中而不是复制从而提高效率。
------
拷贝返回的话共调用了1次构造函数2次拷贝构造函数 一次构造函数调用在函数 f() 内部std::vectorint v; 这行代码会调用 std::vectorint 的默认构造函数来创建局部对象 v。 两次拷贝构造函数调用 第一次是在 return v; 语句执行时这时会创建一个 v 的副本用于返回给函数的调用者。第二次是在函数外部接收返回值时即 std::vectorint test f(); 这行代码这里 test 通过拷贝构造函数使用 f() 返回的 v 的副本[假设是temp]来初始化。
------
值得注意的是现代C编译器通常会应用返回值优化RVO或具名返回值优化NRVO在这种情况下编译器可能会优化掉不必要的拷贝。
------
如果编译器能够应用这种优化那么实际上可能只会有一次构造函数调用即使是在返回局部对象时。在这种情况下编译器会在调用者作用域直接构造 test而不是在函数内部创建一个副本[假设是temp]然后拷贝它。
------
例如如果编译器应用了RVO/NRVO那么上面的代码在优化后的汇编代码中可能会直接在 test 的位置构造 std::vectorint从而避免了额外的拷贝。
如果 std::vectorint 支持移动语义在C11及以后版本中编译器还可能会使用移动构造函数来进一步提高效率。
---------
对于上面的例子通过返回值优化RVO或具名返回值优化NRVO编译器可以省略掉两次不必要的拷贝构造函数调用只会调用一次构造函数。
具体来说 RVOReturn Value Optimization当编译器检测到函数返回的是一个局部对象并且该局部对象是直接返回的没有经过任何中间步骤编译器可能会直接在调用者的栈上构造这个对象而不是在函数内部构造然后再拷贝。 NRVONamed Return Value Optimization这是RVO的一种特殊情况当返回的局部对象有名字时编译器可能会使用NRVO。
------
对于上面的示例我猜会使用NRVO:
编译器检测到函数返回的是局部对象v并且该局部对象是直接返回的没有经过任何中间步骤编译器会直接在调用者的栈上构造对象test而不是在函数内部构造v然后再拷贝到temp再拷贝到test。
------
如果没有应用RVO/NRVO但编译器支持移动语义C11及以后版本
并且 std::vectorint 支持移动构造函数那么编译器可能会使用移动构造函数来代替拷贝构造函数这样仍然可以避免拷贝但会调用一次移动构造函数。在这种情况下总共会有一次构造函数调用和一次移动构造函数调用。
------
移动构造函数在何时被调用
当对象即将被销毁并且其资源可以被移动到另一个对象时
在函数 f() 返回局部对象 v 时局部对象 v 将要离开其作用域并被销毁。
如果此时 v 的资源如动态分配的内存可以被移动而不是复制那么编译器会调用移动构造函数来将 v 的资源移动到将要接收返回值的对象test中。
------
总结
如果编译器应用了RVO/NRVO总共一次构造函数调用。如果编译器没有应用RVO/NRVO但使用了移动语义总共一次构造函数调用和一次移动构造函数调用。如果编译器既没有应用RVO/NRVO也没有使用移动语义总共一次构造函数调用和两次拷贝构造函数调用。不过在现代编译器中这种情况不太可能发生因为它们通常会利用RVO/NRVO或移动语义来优化代码。
------