在函数中返回大对象

swo*_*ngu 40 c++

比较以下两段代码,第一段使用对大对象的引用,第二段使用大对象作为返回值.对"大对象"的强调指的是不必要地重复对象的副本是浪费的循环.

使用对大对象的引用:

void getObjData( LargeObj& a )
{
  a.reset() ;
  a.fillWithData() ;
}

int main()
{
  LargeObj a ;
  getObjData( a ) ;
}
Run Code Online (Sandbox Code Playgroud)

使用大对象作为返回值:

LargeObj getObjData()
{
  LargeObj a ;
  a.fillWithData() ;
  return a ;
}

int main()
{
  LargeObj a = getObjData() ;
}
Run Code Online (Sandbox Code Playgroud)

第一段代码不需要复制大对象.

在第二个片段中,对象是在函数内部创建的,因此通常在返回对象时需要复制.但是,在这种情况下,main()正在声明对象.编译器是否会首先创建一个默认构造的对象,然后复制返回的对象getObjData(),或者它是否与第一个片段一样有效?

我认为第二个片段更容易阅读,但我担心效率会降低.

编辑:通常,我认为案例LargeObj是通用容器类,为了参数,它们包含数千个对象.例如,

typedef std::vector<HugeObj> LargeObj ;
Run Code Online (Sandbox Code Playgroud)

所以直接修改/添加方法LargeObj不是一个可直接访问的解决方案.

Dav*_*eas 45

第二种方法更具惯用性和表现力.很明显,在读取代码时,函数对参数没有先决条件(它没有参数),并且它实际上会在里面创建一个对象.对于随意的读者来说,第一种方法并不是那么清楚.调用意味着对象将被更改(通过引用传递)但是如果传递的对象上有任何先决条件则不太清楚.

关于副本.您发布的代码不使用赋值运算符,而是复制构造.C++定义了在所有主要编译器中实现的返回值优化.如果您不确定是否可以在编译器中运行以下代码段:

#include <iostream>
class X
{
public:
    X() { std::cout << "X::X()" << std::endl; }
    X( X const & ) { std::cout << "X::X( X const & )" << std::endl; }
    X& operator=( X const & ) { std::cout << "X::operator=(X const &)" << std::endl; }
};
X f() {
    X tmp;
    return tmp;
}
int main() {
    X x = f();
}
Run Code Online (Sandbox Code Playgroud)

使用g ++,你将得到一行X :: X().编译器保留在堆栈的空间X对象,然后调用构造函数TMP超过X(事实上TMP X.内部的操作f()的直接施加X,等效于您的第一代码段(通过引用传递).

如果您没有使用复制构造函数(如果您编写:X x; x = f();)那么它将创建xtmp并应用赋值运算符,产生三行输出:X :: X()/X :: X()/X :: operator =.因此在案件中效率可能会低一些.

  • 我用VS2008检查了它,发现/ Od(禁用)调用X :: X()和X :: X(X const&),而/ O1/O2/Ox只调用X :: X().因此,在禁用优化的情况下,在f()之外返回时完成复制. (5认同)
  • 感谢您的精彩解释以及“测试”返回值优化的代码。我可以确认 Microsoft Visual Studio 2008 编译器返回与 g++ 相同的结果 - 即优化的操作集。 (2认同)

ava*_*kar 12

使用第二种方法.似乎效率较低,但C++标准允许避免副本.此优化称为命名返回值优化,并在大多数当前编译器中实现.