18

所以我在写一些代码,我有这样的东西:

class Box
{
    private:
    float x, y, w, h;

    public:
    //...
    Rectangle & GetRect( void ) const
    {
        return Rectangle( x, y, w, h );
    }
};

然后在一些代码中:

Rectangle rect = theBox.GetRect();

这在我的调试版本中有效,但在发布时有“问题”通过引用返回该矩形——我基本上得到了一个未初始化的矩形。Rectangle 类有一个 = 运算符和一个复制构造函数。在不了解为什么会中断的情况下,我实际上对通过引用返回(新)对象的正确方法更感兴趣,以便复制分配给变量。我只是傻吗?不应该这样做吗?我知道我可以返回一个指针,然后在赋值时取消引用,但我宁愿不这样做。我的某些部分感觉按值返回会导致对象的冗余复制——编译器是否解决了这一问题并对其进行了优化?

这似乎是一个微不足道的问题。经过多年的 C++ 编码,我感到几乎很尴尬,所以希望有人可以为我解决这个问题。:)

4

9 回答 9

26

您不能返回对堆栈上临时对象的引用。你有三个选择:

  1. 按值返回
  2. 通过指向您使用 new 运算符在堆上创建的内容的指针通过引用返回。
  3. 通过引用返回您通过引用收到的内容作为参数。[编辑:感谢@harshath.jr 指出这一点]

请注意,当您在下面的代码中按值返回时,编译器应优化分配以避免复制 - 即它只会通过将 create+assign+copy 优化为 create 来创建单个 Rectangle (rect)。这仅在您从函数返回时创建新对象时才有效。

Rectangle GetRect( void ) const
{
    return Rectangle( x, y, w, h );
}

Rectangle rect = theBox.GetRect();
于 2009-06-15T13:53:59.183 回答
16

不,你不能这样做。本质上,您在此示例中尝试做的是返回对堆栈上临时变量的引用。在返回引用时,它指向的变量将被销毁,因此引用无效。

于 2009-06-15T13:47:14.007 回答
10

按值返回对象(参见下面的示例)实际上可能比您想象的要便宜。编译器通常会优化掉多余的副本。这称为返回值优化

    Rectangle GetRect( void ) const
    {
            return Rectangle( x, y, w, h );
    }
于 2009-06-15T14:07:27.560 回答
5

有没有正确的方法在 C++ 中通过引用返回一个新的对象实例?

不,不是参考。创建新对象有两种方法:

在堆栈上:

Rectangle makeRect()
{
  return Rectangle(x, y, w, h);
}
Rectangle r = makeRect(); // return by value

在堆上:

Rectangle * makeRect()
{
  return new Rectangle(x, y, w, y);
}
Rectangle * r = makeRect(); // returned a pointer, don't forget to delete it later

为什么不这样呢?

class Box
{
  private:
    Rectangle mRectangle;

  public:
    Box(float x, float y, float w, float h) :
      mRectangle(x, y, w, h) // Forgive me for making assumptions
                             // about the inner workings of your
                             // code here.
    {
    }

    const Rectangle & GetRect() const
    {
      return mRectangle;
    }
};

Rectangle rect = theBox.GetRect();

“作业”现在应该可以工作了。(从技术上讲,这不是赋值运算符,而是调用的复制构造函数。)

希望有所帮助

于 2009-06-15T23:10:31.133 回答
2
  • 要么返回对您Box班级内部的引用(有一个Rectangle成员。const建议返回引用)。
  • 或者只是返回一个Rectangle. 请注意,使用该成语可能会在体面的编译器上return SomeClass(a,b,c);触发返回值优化 (RVO) 。

检查您的std::complex实施以获取详细信息。

于 2009-06-15T14:02:45.357 回答
2

您可能对临时生命周期的概念感到困惑。考虑:

void f1( const A & a ) {
}

A f2() {
   return A;
}

f1( f2() );

这是可以的代码,标准规定 f2 创建的无名临时文件必须挂起足够长的时间才能在 f1 中使用。

但是,您的情况有些不同。你的函数返回的东西是一个引用,因此无名临时也是一个引用。该引用必须保留足够长的时间才能有用,但它所指的东西不需要。

于 2009-06-15T14:04:22.763 回答
2

这是不可能的。引用是指针的另一种形式,实际上您返回了一个对象的地址,该地址将被销毁(称为析构函数),甚至可能在调用者获得控制权时被覆盖。

你可以

  • 调用 new 并返回一个指向堆分配对象的指针(也许你应该考虑一个智能指针)或
  • 按值返回或
  • 通过引用将对象传递给函数,以便填充它。
于 2009-06-15T14:04:42.997 回答
0

如果矩形按位看起来像 Box 即由四个浮点数组成(但有不同的成员函数),您可以使用reinterpret_cast,尽管我根本不推荐它:

    const Rectangle & GetRect( void ) const
    {
            assert(sizeof(Rectangle) == sizeof(Box));
            return reinterpret_cast <Rectangle> (*this);
    }
于 2009-06-15T14:12:16.893 回答
0

如果我们想使用新的和安全的内存泄漏,我们可以使用 auto_ptr

class Box  { 

  private:    float x, y, w, h;   

  public:    

  //...    

  std::auto_ptr<Rectangle> GetRect( void ) const   
  {        
      return std::auto_ptr<Rectangle> ( new Rectangle( x, y, w, h ));   
  }

};
于 2009-06-15T15:35:18.597 回答