有没有正确的方法在 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++ 中通过引用返回一个新的对象实例?的主要内容,如果未能解决你的问题,请参考以下文章