在 C++ 中是否有通过引用返回新对象实例的正确方法?
-
13-09-2019 - |
题
所以我正在编写一些代码,我有这样的代码:
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++ 编码,我不知道这一点,我感到几乎尴尬,所以希望有人能为我解决这个问题。:)
解决方案
您无法返回对堆栈上临时对象的引用。您有三个选择:
- 按值返回
- 通过指针通过引用返回到您使用 new 运算符在堆上创建的内容。
- 通过引用返回您通过引用收到的作为参数的内容。[编辑:感谢@harshath.jr 指出了这一点]
请注意,当您按值返回(如下面的代码所示)时,编译器应优化赋值以避免复制 - 即它只会通过将 create+assign+copy 优化为 create 来创建单个矩形(rect)。仅当您从函数返回时创建新对象时,这才有效。
Rectangle GetRect( void ) const
{
return Rectangle( x, y, w, h );
}
Rectangle rect = theBox.GetRect();
其他提示
不,你不能这样做。本质上,您在此示例中尝试执行的操作是返回对堆栈上临时变量的引用。当返回引用时,它指向的变量将被销毁,因此引用无效。
按值返回对象(参见下面的示例)实际上可能比您想象的要便宜。编译器通常会优化掉额外的副本。这被称为 返回值优化.
Rectangle GetRect( void ) const
{
return Rectangle( x, y, w, h );
}
是否有正确的方法可以通过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();
“作业”现在应该可以工作了。(从技术上讲,这不是赋值运算符,而是调用的复制构造函数。)
希望能有所帮助
您可能对临时生命周期的概念感到困惑。考虑:
void f1( const A & a ) {
}
A f2() {
return A;
}
f1( f2() );
这是正常的代码,标准规定 f2 创建的无名临时文件必须挂起足够长的时间才能在 f1 中使用。
但是,您的情况有些不同。你的函数返回的是一个引用,因此无名临时也是一个引用。该引用必须保留足够长的时间才能有用,但它所引用的内容则不需要。
这不可能。引用是指针的另一种形式,实际上,您返回一个对象的地址,该对象将被销毁(调用析构函数),甚至可能在调用者收到控制权时被覆盖。
你可以
- 调用 new 并返回一个指向堆分配对象的指针(也许您应该想到智能指针)或
- 按值返回或
- 通过引用将对象传递到函数中,以便它填充它。
- 要么返回对你的内部结构的引用
Box
类(有一个Rectangle
成员。返回一个const
建议参考)。 - 或者只是返回一个
Rectangle
. 。请注意,使用习语return SomeClass(a,b,c);
可能会触发 返回值优化(RVO) 在体面的编译器上。
检查你的 std::complex
实施详情。
如果矩形按位看起来像盒子,即由四个浮点数组成(但有不同的成员函数),您可以使用 重新解释_cast, ,尽管我根本不会推荐它:
const Rectangle & GetRect( void ) const
{
assert(sizeof(Rectangle) == sizeof(Box));
return reinterpret_cast <Rectangle> (*this);
}
如果我们想使用 new 并且安全地避免内存泄漏,我们可以使用 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 ));
}
};