问题描述
想象一下这样的情况,我有这样的功能:
Imagine such situation that I have a function like this:
Object f()
{
Object obj;
return obj;
}
其中,的sizeof(对象)
是一个很大的值。
然后,我让这个函数的调用:
And then I make a call of this function:
Object object = f();
我是否正确理解,第一个对象会在栈上创建(在功能),然后将复制到对象变量?
Do i understand correctly that first Object will be created on a stack (in the function) and then will be copied to object variable?
如果是这样,它是合理的在功能上堆创建一个对象,并返回一个指向它,而不是一个副本?
If so, is it reasonably to create an object in the function on a heap and to return a pointer to it instead of a copy ?
不过,我的意思是对象必须在 F()
函数创建 - 不是通过指针或这个函数的引用传递和初始化
But i mean that the object must be created in the f()
function - not passed by a pointer or a reference to this function and initialized.
修改
我的意思不是说f是一个很简单的功能。它可以根据一些背景有对象初始化一个非常复杂的程序。请问编译器仍然优化它呢?
I don't mean that f is a very simple function. It can have a really complex routine of object initialization depending on some context. Will the compiler still optimize it as well?
推荐答案
有关的特定情况下,可以采取的事实,编译器现在有足够的智慧来优化它的优势。优化叫做 价值优化(NRVO),所以它的好返回大物像。编译器可以看到这样的机会(尤其是作为你的code段简单的东西),并生成二进制,这样不需要拷贝的。
For that specific case, you can take advantage of the fact that compilers nowadays are smart enough to optimize for it. The optimization is called named return value optimization (NRVO), so it's okay to return "big" objects like that. The compiler can see such opportunities (especially in something as simple as your code snippet) and generate the binary so that no copies are made.
您还可以返回无名临时对象:
You can also return unnamed temporaries:
Object f()
{
return Object();
}
这将调用(未命名)返回值优化(RVO)在几乎所有现代的C ++编译器。事实上,VISUAL C ++实现了即使所有的优化都关闭这个特别的优化。
This invokes (unnamed) return value optimization (RVO) on just about all modern C++ compilers. In fact, Visual C++ implements this particular optimization even if all optimizations are turned off.
这些种类的优化是专门由C ++标准允许的:
These kinds of optimizations are specifically allowed by the C++ standard:
ISO 14882:2003的C ++标准,§12.8段。 15:
复制类对象
当满足一定的条件,一个
实施允许省略
一类对象的拷贝构造,
即使拷贝构造函数和/或
析构函数的对象有副作用
影响。在这种情况下,
实施将源和
省略复制操作的目标
作为简单的两个不同的方式
指同一对象,并且
该对象的破坏发生
以后的时代当两
对象将被销毁
没有优化。这elison
复制操作是允许的
以下的情况下(其可以是
结合消除多
份):
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):
- 在
收益
语句,
当恩pression是一个名字
与非挥发性自动对象
同样的CV-不合格类型作为
函数返回类型,复印
操作可以通过省略
构建自动对象
直接插入函数的返回
值 - 当尚未结合至参考临时类对象
将被复制到一个类对象,具有
同样的CV-unqualitied型,拷贝
操作可以通过省略
构建临时对象
直接进入的目标
省略副本。
在一类terturn类型的函数
一般情况下,编译器总是尝试实施NRVO和/或视网膜静脉阻塞,虽然它可能会失败,在某些情况下这样做,就像多个返回路径。然而,这是一个非常有用的优化,你不应该害怕使用它。
Generally, the compiler will always try to implement NRVO and/or RVO, although it may fail to do so in certain circumstances, like multiple return paths. Nevertheless, it's a very useful optimization, and you shouldn't be afraid to use it.
如果有疑问,可以随时通过插入调试语句测试你的编译器,看看自己:
If in doubt, you can always test your compiler by inserting "debugging statements" and see for yourself:
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失败(这可能是不太可能发生),那么你可以尝试返回一个代理对象:
If the returned object isn't meant to be copyable, or (N)RVO fails (which is probably not likely to happen), then you can try returning a proxy object:
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();
}
当然,这不完全是显而易见的,将需要让适当的文件(至少一个关于它的评论)。
Of course, this isn't exactly obvious so proper documentation would be needed (at least a comment about it).
您还可以返回某种类型的智能指针(如的std :: auto_ptr的
或的boost :: shared_ptr的
或类似的东西),在对象上的空闲存储分配。如果您需要返回派生类型的实例,这是必要的:
You can also return a smart pointer of some kind (like std::auto_ptr
or boost::shared_ptr
or something similar) to an object allocated on the free-store. This is needed if you need to return instances of derived types:
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);
}
这篇关于创建和从函数返回一个大对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!