RVO 是否适用于 "new"?
Does RVO work with "new"?
复制省略会在这种情况下起作用吗?换句话说,具有复制省略的现代编译器是否避免在此处调用任何复制构造函数?
class BigObj{};
BigObj fun()
{
BigObj b;
return b;
}
int main()
{
BigObj *pb = new BigObj(fun());
}
我的目标是将一个对象存储在一个指针中。该对象由函数返回。我想保存而不复制。
我不会用c++11
RVO 是标准允许的内容之一,但没有特别要求。也就是说,大多数现代编译器(至少启用了适当的优化设置)都会实现它。但是,如果您想要保证,则需要阅读编译器文档。
既然目标是动态分配一个对象,我会简单地改变这个例子,让被调用的函数进行动态分配。而不是(OP 的代码);
BigObj fun()
{
BigObj b;
// presumably the point of fun() is that some initialisation
// of b occurs here
return b;
}
int main()
{
BigObj *pb = new BigObj(fun());
}
我会简单地使用
BigObj *fun()
{
BigObj *b = new BigObj;
// presumably the point of fun() is that some initialisation
// of *b occurs here
return b;
}
int main()
{
BigObj *pb = fun();
}
并一起消除对 BigObj
的潜在复制。唯一被复制的是指针的值。因此,编译器不依赖于 C++11 移动构造函数的存在来优化上述内容,因为它避免了不必要的对象创建和复制,因此这满足了 OP 不使用 C++11 的需要。
显然,无论哪种情况,最好将 operator new 的用法与相应的 operator delete 相匹配。
IMO 并不完全清楚您的目标是什么。如果您想使用动态分配,那么您的函数应该简单:
BigObj * fun()
{
return new BigObj;
}
int main()
{
BigObj *pb = fun();
}
...省去麻烦。
与之前的答案修改相反,事实证明编译器可以省略大量工作,只要它处于可以彻底分析的静态上下文中:
class C {
public:
C() {qDebug() << "C created";}
C(const C & o) { qDebug() << "C copied"; }
};
C foo() {
C c;
qDebug() << &c;
return c;
}
...
C c = C(foo()); // note that `C c = ` is also copy construction
qDebug() << &c;
输出验证两个实例具有相同的地址,因此即使在本地上下文中,实例实际上也没有存储在foo
的堆栈帧中。
更改为:
C * cp = new C(foo());
qDebug() << cp;
令我惊讶的是还输出了相同的地址,return 按值复制和复制构造函数都被省略了。 foo
中的c
直接在内存块中构造,由new
分配。
总而言之,C++ 编译器在分析和进行所有可能的优化方面非常聪明。
分别在第一种和第二种情况下关闭优化:
C created
0x28fd97
C copied
C copied
C copied
0x28fdeb
...
C created
0x28fd97
C copied
C copied
0x34fd00
复制省略会在这种情况下起作用吗?换句话说,具有复制省略的现代编译器是否避免在此处调用任何复制构造函数?
class BigObj{};
BigObj fun()
{
BigObj b;
return b;
}
int main()
{
BigObj *pb = new BigObj(fun());
}
我的目标是将一个对象存储在一个指针中。该对象由函数返回。我想保存而不复制。
我不会用c++11
RVO 是标准允许的内容之一,但没有特别要求。也就是说,大多数现代编译器(至少启用了适当的优化设置)都会实现它。但是,如果您想要保证,则需要阅读编译器文档。
既然目标是动态分配一个对象,我会简单地改变这个例子,让被调用的函数进行动态分配。而不是(OP 的代码);
BigObj fun()
{
BigObj b;
// presumably the point of fun() is that some initialisation
// of b occurs here
return b;
}
int main()
{
BigObj *pb = new BigObj(fun());
}
我会简单地使用
BigObj *fun()
{
BigObj *b = new BigObj;
// presumably the point of fun() is that some initialisation
// of *b occurs here
return b;
}
int main()
{
BigObj *pb = fun();
}
并一起消除对 BigObj
的潜在复制。唯一被复制的是指针的值。因此,编译器不依赖于 C++11 移动构造函数的存在来优化上述内容,因为它避免了不必要的对象创建和复制,因此这满足了 OP 不使用 C++11 的需要。
显然,无论哪种情况,最好将 operator new 的用法与相应的 operator delete 相匹配。
IMO 并不完全清楚您的目标是什么。如果您想使用动态分配,那么您的函数应该简单:
BigObj * fun()
{
return new BigObj;
}
int main()
{
BigObj *pb = fun();
}
...省去麻烦。
与之前的答案修改相反,事实证明编译器可以省略大量工作,只要它处于可以彻底分析的静态上下文中:
class C {
public:
C() {qDebug() << "C created";}
C(const C & o) { qDebug() << "C copied"; }
};
C foo() {
C c;
qDebug() << &c;
return c;
}
...
C c = C(foo()); // note that `C c = ` is also copy construction
qDebug() << &c;
输出验证两个实例具有相同的地址,因此即使在本地上下文中,实例实际上也没有存储在foo
的堆栈帧中。
更改为:
C * cp = new C(foo());
qDebug() << cp;
令我惊讶的是还输出了相同的地址,return 按值复制和复制构造函数都被省略了。 foo
中的c
直接在内存块中构造,由new
分配。
总而言之,C++ 编译器在分析和进行所有可能的优化方面非常聪明。
分别在第一种和第二种情况下关闭优化:
C created
0x28fd97
C copied
C copied
C copied
0x28fdeb
...
C created
0x28fd97
C copied
C copied
0x34fd00