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

Posted

技术标签:

【中文标题】有没有正确的方法在 C++ 中通过引用返回一个新的对象实例?【英文标题】:Is there a right way to return a new object instance by reference in C++? 【发布时间】:2010-11-03 00:43:48 【问题描述】:

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

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++ 编码,我感到几乎很尴尬,我不知道这一点,所以希望有人可以为我解决这个问题。 :)

【问题讨论】:

请注意,您的代码中没有赋值。 【参考方案1】:

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

【讨论】:

所以你是说如果它是 const Rectangle & GetRect( ... ) 是正确的? 问题不在于常量,而是你引用的东西是在堆栈上创建的。 @pbhogan,没有。我不应该在我的答案中添加 const 词。那是我的一个错误。你应该在这里按值返回。【参考方案2】:

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

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

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

Rectangle GetRect( void ) const

    return Rectangle( x, y, w, h );


Rectangle rect = theBox.GetRect();

【讨论】:

选项 3:通过引用返回您收到的作为引用争论的内容。这在覆盖运算符时特别有用。 指针的例子在这个答案中会很好,比如 Rectangle * GetRect(void) const return new Rectangle(x, y, w, h) Rectangle* rect = theBox.GetRect() ;稍后删除矩形;【参考方案3】: 要么返回对 Box 类内部结构的引用(有 Rectangle 成员。建议返回 const 引用)。 或者只返回一个Rectangle。请注意,使用成语return SomeClass(a,b,c); 可能会在体面的编译器上触发return value optimization (RVO)。

请检查您的 std::complex 实现以了解详细信息。

【讨论】:

【参考方案4】:

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

void f1( const A & a ) 


A f2() 
   return A;


f1( f2() );

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

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

【讨论】:

你当然是对的。我在动态语言上花费了太多时间——我开始期待临时的生命周期遵循参考。【参考方案5】:

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

你可以

调用 new 并返回一个指向堆分配对象的指针(也许您应该考虑一个智能指针)或 按值返回或 通过引用将对象传递给函数,以便填充它。

【讨论】:

【参考方案6】:

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

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

【讨论】:

【参考方案7】:

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

    const Rectangle & GetRect( void ) const
    
            assert(sizeof(Rectangle) == sizeof(Box));
            return reinterpret_cast <Rectangle> (*this);
    

【讨论】:

【参考方案8】:

如果我们想使用新的和安全的内存泄漏,我们可以使用 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 ));   
  

;

【讨论】:

【参考方案9】:

有没有正确的方法来返回一个新的 在 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();

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

希望能帮到你

【讨论】:

我的选择的一个很好的总结,谢谢。我已经尽可能地实现了你的最后一个选项,但是我在这里的例子有点简单。 Box 实际上是更复杂的东西,GetRect 返回一个计算得到的 Rectangle。无论如何,你的第一个选择是我应该做的......我只是想比我猜的编译器更聪明:p

以上是关于有没有正确的方法在 C++ 中通过引用返回一个新的对象实例?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C++ 中通过引用返回向量

如何在 C++ 中通过引用返回类对象?

在 C++ 中通过引用/值传递

在 C++ 中通过引用和值传递字符串

在 C++ 中通过迭代器将向量写入二进制文件的正确方法

在 C++ 中通过引用传递对象