考虑下面的代码。
// Consider that MyObject receives three integers in it's constructor.
MyObject createObject()
{
MyObject result(1, 2, 3);
return result;
}
只要我知道,当你在C ++中返回一个对象时(比如在这个函数中),编译器将在客户端的堆栈中创建一个新对象(作为局部变量)。在这个例子中,它将是一个新的MyObject
对象,它将使用带有result
作为参数的复制构造函数创建。这意味着对机器进行冗余处理:将在堆栈中创建对象result
,然后创建并返回第二个对象。
避免这种情况的另一种方法是动态创建对象(不在堆栈中),然后返回它的指针。但它会导致内存泄漏或强制客户端在使用后删除该对象。所以,我找到的解决方案是使用智能指针,如下所示:
// Consider that MyObject receives three integers in it's constructor.
boost::smart_ptr<MyObject> createObject()
{
boost::smart_ptr<MyObject> result(new MyObject(1, 2, 3));
return result;
}
这样可行,但智能指针仍然是一个将被重新创建的对象(即使成本很低,因为它基本上只包含一个指针)。我猜是否有办法更容易地做到这一点;或者如果编译器没有一种方法可以优化这项工作或类似的东西。是否有一种语法可以告诉编译器做我想做的事情?
答案 0 :(得分:6)
首先描述你的代码,我99%肯定没有复制的开销。
NRVO存在,并且很可能在这种情况下启动。
如果不是复制省略,则返回集合(如std::vector
等)将导致大量问题。
当你在C ++中返回一个对象时(比如在这个函数中),编译器将在客户端的堆栈中创建一个新对象(作为局部变量)。
不是真的。它可能会直接在调用上下文中创建,正好可以防止额外的副本。
答案 1 :(得分:2)
如果您愿意使用C ++ 11,则可以使用“移动语义”。让我先解释一下:
class expensive {
public:
expensive() : n(0), V(NULL) {}
expensive(int _n) {
n = _n;
V = new int[_n];
for (int i = 0; i < n; ++i) V[i] = i*i;
}
expensive(const expensive& rhs) { // copy constructor
n = rhs.n;
V = new int[n];
for (int i = 0; i < n; ++i) V[i] = rhs.V[i];
}
expensive(expensive&& rhs) { //move constructor
n = rhs.n;
V = rhs.V;
rhs.n = -1;
rhs.V = NULL;
printf("Moving\n");
}
~expensive() {
if (n == -1) printf("Destroying 'moved' instance\n");
if (V) delete [] V;
}
private:
int *V;
int n;
};
expensive f(int x) {
expensive temp(50);
expensive temp2(temp); // Copy temp to temp2, both are valid now
expensive temp3(std::move(temp)); // Move temp to temp3, temp is not valid anymore
return std::move(temp2); // move temp2 to the return
}
int main() {
expensive E = f(30);
return 0;
}
该程序的输出是:
Moving
Moving
Destroying 'moved' instance
Destroying 'moved' instance
所有普通的STL容器都支持移动语义。此外,std :: swap使用它(因此,std :: sort也是如此)。
编辑: 如上所述,如果使用NRVO,则最后一次std :: move是不必要的。让我举一个更复杂的例子。这是一个玩具示例,但它应该表明我的观点。
class expensive {
... // Same definition as before
}
expensive g(int x) {
vector<expensive> V;
V.reserve(2*x);
for (int i = 1; i <= x; ++i)
V.push_back(expensive(i)); // Here the objects are moved to the vector
for (int i = 1; i <= x; ++i)
V.emplace_back(i); // Here the objects are constructed directly in the vector
return std::move(V[x/2]); // V[x/2] is moved to the answer
}
int main() {
expensive x(g(2)); // 3 objects should be moved, 2 in push_back and one on the return
return 0;
}
答案 2 :(得分:1)
来源:http://blogs.msdn.com/b/slippman/archive/2004/02/03/66739.aspx
编译器优化器足够聪明,可以弄清楚发生了什么,所以在优化它时会在内部重写调用:
void CallingFunction()
{
// Call to createObject() gets eliminated through transformation
// since in this case it's only code was the initialization
MyObject obj(1, 2, 3);
// ... do stuff
}