c++ - 从函数创建和返回一个大对象

标签 c++ c memory

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

Object f()
{
    Object obj;
    return obj;
}

其中 sizeof(Object) 是一个很大的值。

然后我调用这个函数:

Object object = f();  

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

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

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

编辑

我并不是说 f 是一个非常简单的函数。根据某些上下文,它可以有一个非常复杂的对象初始化例程。编译器还会优化它吗?

最佳答案

对于这种特定情况,您可以利用现在的编译器足够聪明,可以针对它进行优化这一事实。优化被称为命名return value optimization (NRVO),因此可以返回这样的“大”对象。编译器可以看到这样的机会(尤其是像您的代码片段这样简单的东西)并生成二进制文件,这样就不会复制。

您也可以返回未命名的临时对象:

Object f()
{
    return Object();
}

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

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

ISO 14882:2003 C++ Standard, §12.8 para. 15: Copying Class Objects

When certain criteria are met, an implementation is allowed to omit the copy construction of a class object, even if the copy constructor and/or destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy operation as simply two different ways of referring to the same object, and the destruction of that object occurs later of the times when the two objects would have been destroyed without the optimization. This elison of copy operations is permitted in the following circumstances (which may be combined to eliminate multiple copies):

  • in a return statement in a function with a class terturn type, when the expression is the name of a non-volatile automatic object with the same cv-unqualified type as the function return type, the copy operation can be omitted by constructing the automatic object directly into the function's return value
  • when a temporary class object that has not been bound to a reference would be copied to a class object with the same cv-unqualitied type, the copy operation can be omitted by constructing the temporary object directly into the target of the omitted copy.

通常,编译器将始终尝试实现 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();
}

如果返回的对象不是可复制的,或者 (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();
}

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

您还可以将某种智能指针(如 std::auto_ptrboost::shared_ptr 或类似的东西)返回到在 free-店铺。如果您需要返回派生类型的实例,则需要这样做:

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);
}

关于c++ - 从函数创建和返回一个大对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4809120/

相关文章:

c++ - 未处理的异常 - 如果/SUBSYSTEM 参数已更改,则访问冲突

c++ - Qt QScriptEngine 和从 Qt 脚本到 C++ 的默认转换

c++ - 组合问题 : operator '==' and operator '-'

c - 为什么 restrict 限定符仍然允许 memcpy 访问重叠内存?

将自删除推回 vector 时 C++ 崩溃

c - 在 C : Is it faster to access a char* = malloc() used like a 2D array than an array[][]?

c++ - 在 while 循环中嵌套 while 循环和在 while 循环中嵌套 if-else 循环有什么显着区别吗? (C++)

c - 读取文本文件的完整内容后 fread() 失败

c - 单个 printf() 函数是否可以在循环中从 getchar() 获取输入时执行多次?

c - 如何从进程内存镜像创建 ELF 可执行文件