在 C++ 中通过引用返回 - 引用赋值与值赋值

Return by reference in C++ - Reference assignment vs value assignment

本文关键字:赋值 引用 C++ 返回      更新时间:2023-10-16

假设我有:

    class SomeObject {
    };
    SomeObject& f() {
        SomeObject *s = new SomeObject();
        return *s;
    }
    // Variant 1
    int main() {
        SomeObject& s = f();
        // Do something with s
    }
   // Variant 2
    int main() {
        SomeObject s = f();
        // Do something with s
    }

第一个变体和第二个变体之间有什么区别吗? 任何我会使用一个而不是另一个的情况吗?

编辑:还有一个问题,s在这两种情况下都包含什么?

首先,您永远不希望返回对以下对象的引用:在函数中动态分配。 这是一个记忆泄漏等待发生。

除此之外,它还取决于对象的语义,以及什么你在做。 使用引用(变体 1)允许修改它所指的对象,以便其他一些函数将看到修改后的值。 声明值(变体 2)表示您拥有自己的本地副本,并且任何修改等将是针对它,而不是对象在函数返回中引用。

通常,如果函数返回对非常量的引用,这是因为它期望修改值;一个典型的示例类似于std::vector<>::operator[],其中表达式如下:

v[i] = 42;

预期会修改向量中的元素。 如果这是不是这种情况,那么函数应该返回一个值,而不是一个引用(你几乎不应该使用这样的函数来初始化本地引用)。 当然,这只会使是否返回对可访问内容的引用别处;全局变量或(更有可能)数据由函数所属的类拥有。

在第一个变体中,您将引用直接附加到动态分配的对象。这是一种相当非正统的拥有动态内存的方式(指针更适合此目的),但它仍然让您有机会正确释放该对象。即在你的第一个main结束时,你可以做

delete &s;

在第二个变体中,您将丢失引用,即丢失指向该动态分配对象的唯一链接。对象变为内存泄漏。

同样,通过引用拥有动态分配的对象对我来说不是一种好的做法。通常最好使用指针或智能指针来实现此目的。出于这个原因,您的两个变体都有缺陷,即使第一个变体在形式上是可兑换的。

变体 1 将复制对象的地址并且速度很快

变体 2

将复制整个对象并且速度很慢(正如变体 2 中已经指出的那样,您无法删除通过调用 new 创建的对象)

对于编辑:两个 f 都包含相同的对象

您询问的两个选项都不是很好。在这种特殊情况下,您应该使用 shared_ptrunique_ptr ,或者如果您使用较旧的C++编译器,则应使用 auto_ptr,并更改函数以使其返回指针,而不是引用。另一个不错的选择是按值返回对象,特别是如果对象很小且构造成本低。

修改以按值返回对象:

SomeObject f() { return SomeObject(); }
SomeObject s(f());

简单,干净,安全 - 这里没有内存泄漏。

使用unique_ptr

SomeObject* f() { return new SomeObject(); }
unique_ptr<SomeObject> s(f());

在这里使用 unique_ptrshared_ptr 的一个优点是,您可以在某个时候更改函数f以返回从SomeObject派生的类的对象,并且不需要更改任何客户端代码 - 只需确保基类 (SomeObject) 具有虚拟构造函数即可。

为什么您考虑的选项不是很好:

变式 1:

SomeObject& s = f();

你打算如何摧毁这个物体?无论如何,您都需要对象的地址来调用其析构函数,因此在某些时候您需要取消引用s引用的对象(&s

变式2.这里有一个泄漏,没有机会调用从函数返回的对象的析构函数。