RVO是否与“新”一起使用?

时间:2015-08-07 07:03:13

标签: c++ c++03 rvo nrvo

在这种情况下复制elision是否会启动?换句话说,使用copy elision的现代编译器是否可以避免在这里调用任何复制构造函数?

class BigObj{};

BigObj fun()
{
  BigObj b;
  return b;
}
int main()
{
  BigObj *pb = new BigObj(fun());
}

我的目标是将对象存储在指针中。该对象由函数返回。我想存储它而不复制它。

我不能使用c ++ 11

2 个答案:

答案 0 :(得分:1)

国际海事组织并不完全清楚你的目标是什么。如果动态分配是您想要使用的,那么您的函数应该只是:

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;

令我惊讶的是还输出了相同的地址,同时省略了返回值复制和复制构造函数。 c中的foo直接在内存块中构建,由new分配。

总之,C ++编译器非常聪明地分析和完成所有可能的优化。

分别关闭第一种情况和第二种情况下的优化:

C created
0x28fd97
C copied
C copied
C copied
0x28fdeb

...

C created
0x28fd97
C copied
C copied
0x34fd00

答案 1 :(得分:0)

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相匹配。