从函数创建并返回一个大对象

And*_*rew 10 c c++ memory

想象一下这种情况,我有这样的功能:

Object f()
{
    Object obj;
    return obj;
}
Run Code Online (Sandbox Code Playgroud)

sizeof(Object)价值在哪里.

然后我调用了这个函数:

Object object = f();  
Run Code Online (Sandbox Code Playgroud)

我是否正确理解第一个Object将在堆栈上创建(在函数中)然后将被复制到对象变量?

如果是这样,是否合理地在堆上的函数中创建一个对象并返回指向它而不是副本的指针?

但我的意思是必须在f()函数中创建对象- 不是通过指针或对此函数的引用传递并初始化.

编辑

我并不是说f是一个非常简单的功能.根据某些上下文,它可能有一个非常复杂的对象初始化例程.编译器是否仍会优化它?

In *_*ico 18

对于这种特定情况,您可以利用现在的编译器足够智能来优化它的事实.优化称为命名返回值优化(NRVO),因此可以返回类似的"大"对象.编译器可以看到这样的机会(特别是在代码片段这么简单的事情中)并生成二进制文件以便不会创建副本.

您还可以返回未命名的临时工:

Object f()
{
    return Object();
}
Run Code Online (Sandbox Code Playgroud)

这几乎在所有现代C++编译器上调用(未命名)返回值优化(RVO).实际上,即使关闭所有优化,Visual C++也会实现此特定优化.

C++标准特别允许这些类型的优化:

ISO 14882:2003 C++标准,§12.8段.15:复制类对象

当满足某些条件时,允许实现省略类对象的复制结构,即使该对象的复制构造函数和/或析构函数具有副作用.在这种情况下,实现将省略的复制操作的源和目标视为两种不同的引用同一对象的方式,并且该对象的销毁发生在两个对象在没有优化的情况下被销毁的时间之后. .在下列情况下允许这种复制操作的优点(可以组合使用以消除多个副本):

  • return具有类terturn类型的函数的语句中,当表达式是具有与函数返回类型相同的cv-unqualified类型的非易失性自动对象的名称时,可以通过直接构造自动对象来省略复制操作进入函数的返回值
  • 当一个尚未绑定到引用的临时类对象被复制到具有相同cv-unqualitied类型的类对象时,可以通过将临时对象直接构造到省略副本的目标中来省略复制操作.

通常,编译器将始终尝试实现NRVO和/或RVO,尽管在某些情况下可能无法执行此操作,例如多个返回路径.尽管如此,这是一个非常有用的优化,你不应该害怕使用它.

如果有疑问,您可以随时通过插入"调试语句"来测试编译器,并亲自查看:

class Foo
{
public:
    Foo()                      { ::printf("default constructor\n"); }
    // "Rule of 3" for copyable objects
    ~Foo()                     { ::printf("destructor\n");          }
    Foo(const Foo&)            { ::printf("copy constructor\n");    }
    Foo& operator=(const Foo&) { ::printf("copy assignment\n");     } 
};

Foo getFoo()
{
    return Foo();
}

int main()
{
    Foo f = getFoo();
}
Run Code Online (Sandbox Code Playgroud)

如果返回的对象不是可复制的,或者(N)RVO失败(可能不太可能发生),那么您可以尝试返回代理对象:

struct ObjectProxy
{
private:
    ObjectProxy() {}
    friend class Object;    // Allow Object class to grab the resource.
    friend ObjectProxy f(); // Only f() can create instances of this class.
};

class Object
{
public:
    Object() { ::printf("default constructor\n"); }
    ~Object() { ::printf("destructor\n"); }
    // copy functions undefined to prevent copies
    Object(const Object&);
    Object& operator=(const Object&);
    // but we can accept a proxy
    Object(const ObjectProxy&)
    {
        ::printf("proxy constructor\n");
        // Grab resource from the ObjectProxy.
    }
};

ObjectProxy f()
{
    // Acquire large/complex resource like files
    // and store a reference to it in ObjectProxy.
    return ObjectProxy();
}

int main()
{
     Object o = f();
}
Run Code Online (Sandbox Code Playgroud)

当然,这并不是很明显,因此需要适当的文档(至少是对它的评论).

您还可以将某种智能指针(如std::auto_ptrboost::shared_ptr类似的东西)返回到在免费商店中分配的对象.如果需要返回派生类型的实例,则需要这样做:

class Base {};
class Derived : public Base {};

// or boost::shared_ptr or any other smart pointer
std::auto_ptr<Base> f()
{
    return std::auto_ptr<Base>(new Derived);
}
Run Code Online (Sandbox Code Playgroud)